다음과 같이하면 속도 나 효율성이 저하 될지 궁금합니다.
int i = 0;
while(i < 100)
{
int var = 4;
i++;
}
int var
백 번 선언합니다 . 있을 것 같지만 확실하지 않습니다. 대신 이것을하는 것이 더 실용적 / 빠른 것입니까?
int i = 0;
int var;
while(i < 100)
{
var = 4;
i++;
}
아니면 속도와 효율성이 동일합니까?
다음과 같이하면 속도 나 효율성이 저하 될지 궁금합니다.
int i = 0;
while(i < 100)
{
int var = 4;
i++;
}
int var
백 번 선언합니다 . 있을 것 같지만 확실하지 않습니다. 대신 이것을하는 것이 더 실용적 / 빠른 것입니까?
int i = 0;
int var;
while(i < 100)
{
var = 4;
i++;
}
아니면 속도와 효율성이 동일합니까?
답변:
지역 변수의 스택 공간은 일반적으로 함수 범위에 할당됩니다. 따라서 루프 내에서는 스택 포인터 조정이 발생하지 않으며 4를 var
. 따라서이 두 조각의 오버 헤드는 동일합니다.
var
변수는 초기화되었지만 사용되지 않았으므로 합리적인 옵티마이 저는 변수를 완전히 제거 할 수 있습니다 ( 변수가 루프 이후 어딘가에서 사용 된 경우 두 번째 스 니펫 제외 ).
기본 유형 및 POD 유형의 경우 차이가 없습니다. 컴파일러는 함수 시작 부분에 변수에 대한 스택 공간을 할당하고 두 경우 모두 함수가 반환 될 때 할당을 취소합니다.
중요하지 않은 생성자가있는 비 -POD 클래스 유형의 경우 차이를 만들 것입니다. 루프는 루프가 반복 될 때마다 생성자와 소멸자를 호출합니다. 클래스의 생성자, 소멸자 및 할당 연산자가 수행하는 작업에 따라 이것은 바람직 할 수도 있고 그렇지 않을 수도 있습니다.
둘 다 동일하며 컴파일러가 수행하는 작업을 살펴보면 알 수있는 방법이 있습니다 (최적화를 높음으로 설정하지 않아도).
컴파일러 (gcc 4.0)가 간단한 예제에서 수행하는 작업을 살펴보십시오.
1.c :
main(){ int var; while(int i < 100) { var = 4; } }
gcc -S 1.c
1. 초 :
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
2.c
main() { while(int i < 100) { int var = 4; } }
gcc -S 2.c
2. 초 :
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -16(%ebp)
jmp L2
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
leave
ret
이로부터 두 가지를 볼 수 있습니다. 첫째, 코드는 둘 다 동일합니다.
둘째, var에 대한 저장소는 루프 외부에 할당됩니다.
subl $24, %esp
마지막으로 루프의 유일한 것은 할당 및 조건 확인입니다.
L3:
movl $4, -12(%ebp)
L2:
cmpl $99, -16(%ebp)
jle L3
루프를 완전히 제거하지 않고도 가능한 한 효율적입니다.
요즘에는 컴파일러가 코드를 더 잘 최적화 할 수 있기 때문에 (변수 범위를 줄임) 상수가 아니면 루프 내에서 선언하는 것이 좋습니다.
편집 :이 답변은 현재 거의 사용되지 않습니다. 포스트 클래식 컴파일러의 등장으로 컴파일러가 그것을 알아낼 수없는 경우가 드물어지고 있습니다. 여전히 구성 할 수 있지만 대부분의 사람들은 구성을 잘못된 코드로 분류합니다.
대부분의 최신 컴파일러는이를 최적화합니다. 그것은 내가 더 읽기 쉽다는 것을 알게되면 첫 번째 예를 사용할 것이라고 말했습니다.
내장 유형의 경우 두 스타일간에 차이가 없을 가능성이 높습니다 (아마도 생성 된 코드까지).
그러나 변수가 중요하지 않은 생성자 / 소멸자가있는 클래스 인 경우 런타임 비용에 큰 차이가있을 수 있습니다. 일반적으로 범위를 가능한 한 작게 유지하기 위해 루프 내부로 변수 범위를 지정하지만 성능에 영향을 미치는 것으로 판명되면 클래스 변수를 루프 범위 외부로 이동하는 방법을 찾습니다. 그러나 ode 경로의 의미가 변경 될 수 있으므로 추가 분석이 필요하므로 의미가 허용하는 경우에만 수행 할 수 있습니다.
RAII 클래스에는이 동작이 필요할 수 있습니다. 예를 들어, 파일 액세스 수명을 관리하는 클래스는 파일 액세스를 적절하게 관리하기 위해 각 루프 반복에서 생성 및 소멸되어야 할 수 있습니다.
LockMgr
생성 될 때 중요 섹션을 획득하고 파괴 될 때 해제 하는 클래스가 있다고 가정합니다 .
while (i< 100) {
LockMgr lock( myCriticalSection); // acquires a critical section at start of
// each loop iteration
// do stuff...
} // critical section is released at end of each loop iteration
다음과는 상당히 다릅니다.
LockMgr lock( myCriticalSection);
while (i< 100) {
// do stuff...
}
두 루프의 효율성은 동일합니다. 둘 다 무한한 시간이 걸립니다. :) 루프 내에서 i를 증가시키는 것이 좋습니다.
한 번 성능 테스트를 실행했는데 놀랍게도 사례 1이 실제로 더 빠르다는 것을 알았습니다! 루프 내부에서 변수를 선언하면 범위가 줄어들어 더 일찍 해제되기 때문일 수 있습니다. 그러나 그것은 아주 오래된 컴파일러에서 오래 전이었습니다. 현대 컴파일러가 차이를 최적화하는 더 나은 작업을 수행한다고 확신하지만 변수 범위를 가능한 한 짧게 유지하는 것은 여전히 문제가되지 않습니다.
&i
).
확실하게하는 유일한 방법은 시간을 맞추는 것입니다. 그러나 차이가 있다면 미시적이므로 강력한 큰 타이밍 루프가 필요합니다.
요컨대, 첫 번째는 변수 var를 초기화하고 다른 하나는 초기화되지 않은 상태로두기 때문에 더 나은 스타일입니다. 이것은 변수를 가능한 한 사용 지점에 가깝게 정의해야한다는 지침은 일반적으로 첫 번째 형식이 선호되어야 함을 의미합니다.
나는 대부분의 답변에서 고려해야 할 주요 요점이 누락되었다고 생각합니다. "이것이 분명합니까?"그리고 모든 토론에서 분명히 사실입니다. 전혀 그렇지 않다. 대부분의 루프 코드에서 효율성은 거의 문제가되지 않는다고 제안합니다 (화성 착륙선을 계산하지 않는 한). 그래서 실제로 유일한 질문은 무엇이 더 현명하고 읽기 쉽고 유지 관리 할 수 있는지입니다. 루프 앞과 외부의 변수-이것은 단순히 더 명확하게 만듭니다. 그렇다면 당신과 같은 사람들은 그것이 유효한지 아닌지 온라인으로 확인하는 데 시간을 낭비하지 않을 것입니다.
그것은 사실이 아니지만 오버 헤드가 있지만 무시할 수있는 오버 헤드가 있습니다.
아마도 그들은 스택의 같은 위치에있을 것이지만 여전히 그것을 할당합니다. 해당 int에 대해 스택에 메모리 위치를 할당 한 다음} 끝에 해제합니다. 힙이없는 의미에서는 sp (스택 포인터)를 1만큼 이동합니다. 그리고 로컬 변수가 하나만 있다는 점을 고려하면 단순히 fp (프레임 포인터)와 sp를 동일시합니다.
짧은 대답은 다음과 같습니다. 두 가지 방법 모두 거의 동일하게 작동합니다.
그러나 스택이 어떻게 구성되는지 더 읽어보십시오. 내 학부 학교는 그것에 대해 꽤 좋은 강의를 가졌습니다. 더 많은 것을 읽고 싶다면 여기 http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html