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


상당한 시간 차이를 보일 수도 있고 그렇지 않을 수도있는 대체 위키 후보 테스트.

#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의 경우 대기 시간 및 처리량은 IDIVLDIVL 과 동일하므로 μops에 약간의 차이가 있습니다.

-O3 어셈블리 코드는 내 컴퓨터의 서명 (DIVL 대 IDIVL)에 따라 다르므로 차이점을 설명 할 수 있습니다.





signed