C에서 "unsigned int"보다 "signed int"가 왜 더 빠릅니까?
performance optimization (3)
C에서 왜 unsigned int
보다 unsigned int
니까? 사실,이 웹 사이트에서 여러 번 묻고 답했습니다 (아래 링크 참조). 그러나 대부분의 사람들은 차이가 없다고 말했다. 필자는 코드를 작성하여 실수로 중요한 성능 차이를 발견했습니다.
왜 "서명되지 않은"버전의 코드가 "서명 된"버전보다 속도가 느려지 는가? (저는 x86-64 Intel 프로세서를 가지고 있습니다).
비슷한 링크
컴파일 명령 : gcc -Wall -Wextra -pedantic -O3 -Wl,-O3 -g0 -ggdb0 -s -fwhole-program -funroll-loops -pthread -pipe -ffunction-sections -fdata-sections -std=c11 -o ./test ./test.c && strip --strip-all --strip-unneeded --remove-section=.note --remove-section=.comment ./test
signed int
버전
참고 : 명시 적으로 모든 숫자에 signed int
를 선언하면 차이가 없습니다.
int isprime(int num) {
// Test if a signed int is prime
int i;
if (num % 2 == 0 || num % 3 == 0)
return 0;
else if (num % 5 == 0 || num % 7 == 0)
return 0;
else {
for (i = 11; i < num; i += 2) {
if (num % i == 0) {
if (i != num)
return 0;
else
return 1;
}
}
}
return 1;
}
unsigned int
버전
int isunsignedprime(unsigned int num) {
// Test if an unsigned int is prime
unsigned int i;
if (num % (unsigned int)2 == (unsigned int)0 || num % (unsigned int)3 == (unsigned int)0)
return 0;
else if (num % (unsigned int)5 == (unsigned int)0 || num % (unsigned int)7 == (unsigned int)0)
return 0;
else {
for (i = (unsigned int)11; i < num; i += (unsigned int)2) {
if (num % i == (unsigned int)0) {
if (i != num)
return 0;
else
return 1;
}
}
}
return 1;
}
아래 코드로 파일에서이를 테스트하십시오.
int main(void) {
printf("%d\n", isprime(294967291));
printf("%d\n", isprime(294367293));
printf("%d\n", isprime(294967293));
printf("%d\n", isprime(294967241)); // slow
printf("%d\n", isprime(294967251));
printf("%d\n", isprime(294965291));
printf("%d\n", isprime(294966291));
printf("%d\n", isprime(294963293));
printf("%d\n", isprime(294927293));
printf("%d\n", isprime(294961293));
printf("%d\n", isprime(294917293));
printf("%d\n", isprime(294167293));
printf("%d\n", isprime(294267293));
printf("%d\n", isprime(294367293)); // slow
printf("%d\n", isprime(294467293));
return 0;
}
결과 ( time ./test
) :
Signed - real 0m0.949s
Unsigned - real 0m1.174s
부호있는 정수 오버플로는 정의되지 않았기 때문에 컴파일러는 부호가있는 정수와 관련된 코드에 대해 많은 가정과 최적화를 수행 할 수 있습니다. 부호없는 정수 오버플로가 랩 어라운드되도록 정의되므로 컴파일러는 그만큼 최적화 할 수 없습니다. http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html#signed_overflow 및 http://www.airs.com/blog/archives/120 .
상당한 시간 차이를 보일 수도 있고 그렇지 않을 수도있는 대체 위키 후보 테스트.
#include <stdio.h>
#include <time.h>
#define J 10
#define I 5
int main(void) {
clock_t c1,c2,c3;
for (int j=0; j<J; j++) {
c1 = clock();
for (int i=0; i<I; i++) {
isprime(294967241);
isprime(294367293);
}
c2 = clock();
for (int i=0; i<I; i++) {
isunsignedprime(294967241);
isunsignedprime(294367293);
}
c3 = clock();
printf("%d %d %d\n", (int)(c2-c1), (int)(c3-c2), (int)((c3-c2) - (c2-c1)));
fflush(stdout);
}
return 0;
}
샘플 출력
2761 2746 -15
2777 2777 0
2761 2745 -16
2793 2808 15
2792 2730 -62
2746 2730 -16
2746 2730 -16
2776 2793 17
2823 2808 -15
2793 2823 30
AMD / Intel의 명령어 사양에서 (K7의 경우) :
Instruction Ops Latency Throughput
DIV r32/m32 32 24 23
IDIV r32 81 41 41
IDIV m32 89 41 41
i7의 경우 대기 시간 및 처리량은 IDIVL
및 DIVL
과 동일하므로 μops에 약간의 차이가 있습니다.
-O3 어셈블리 코드는 내 컴퓨터의 서명 (DIVL 대 IDIVL)에 따라 다르므로 차이점을 설명 할 수 있습니다.