IT이야기

memset()은 C의 루프보다 효율이 높습니까?

cyworld 2022. 7. 24. 22:15
반응형

memset()은 C의 루프보다 효율이 높습니까?

나나?memset() forloopsyslog.syslog..syslog.

다음 코드를 고려:

char x[500];
memset(x,0,sizeof(x));

그리고 이건...

char x[500];
for(int i = 0 ; i < 500 ; i ++) x[i] = 0;

어떤 것이 더 효율적이며 그 이유는 무엇입니까?블록 레벨의 초기화를 실시하기 위한 하드웨어에 특별한 지시가 있습니까?

memset그 루프보다 훨씬 더 빠를 겁니다.한 번에 한 문자를 처리하는 방법은 매우 최적화되어 있기 때문에 MMX 및 SSE 명령을 사용할 수 있는 경우라도 이러한 함수는 한 번에 여러 바이트를 설정할 수 있습니다.

통상은 못하고 C 라고 생각합니다.strlen기능.적어도 O(n) 퍼포먼스는 있다고 생각할 수 있지만 실제로는 아키텍처에 따라 O(n/4) 또는 O(n/8)가 있습니다(예, 빅 O()는 동일하지만 실제로는 8분의 1이 됩니다).어떻게요? 까다롭지만, 좋은 방법이죠.

VS 2010에서 생성된 어셈블리 코드를 살펴보겠습니다.

char x[500];
char y[500];
int i;      

memset(x, 0, sizeof(x) );   
  003A1014  push        1F4h  
  003A1019  lea         eax,[ebp-1F8h]  
  003A101F  push        0  
  003A1021  push        eax  
  003A1022  call        memset (3A1844h)  

그리고 당신의 루프는...

char x[500];
char y[500];
int i;    

for( i = 0; i < 500; ++i )
{
    x[i] = 0;

      00E81014  push        1F4h  
      00E81019  lea         eax,[ebp-1F8h]  
      00E8101F  push        0  
      00E81021  push        eax  
      00E81022  call        memset (0E81844h)  

      /* note that this is *replacing* the loop, 
         not being called once for each iteration. */
}

따라서 이 컴파일러에서는 생성된 코드가 완전히 동일합니다. memset가 스마트하게 하고 있기 에 콜을 발신하는 과 같은 을 알 수 있습니다.memset어쨌든 한 번이면 돼

컴파일러가 실제로 루프를 그대로 두면 한 번에 여러 바이트 크기의 블록을 설정할 수 있기 때문에 속도가 느려질 수 있습니다(즉, 최소한 루프를 조금 풀 수 있습니다).라고 할 수 .memset적어도 루프와 같은 단순한 구현만큼 빠릅니다.디버깅 빌드로 시도하면 루프가 교체되지 않습니다.

단, 컴파일러의 기능에 따라 달라집니다.분해 과정을 살펴보는 것은 무슨 일이 일어나고 있는지 정확하게 알 수 있는 좋은 방법입니다.

'먹다'는 '먹다'는 말이죠. memset보다 효율적일 수도 있고 내부에서 for 루프를 사용할 수도 있습니다.의 경우는 생각할 수 없다.memset효율이 떨어집니다.이 경우 루프 효율이 향상될 수 있습니다.루프가 500회 반복되고 어레이의 바이트 값이 매번0 으로 설정됩니다.64비트 머신에서는 한 번에 8바이트(긴 길이)를 설정하면 거의 8배 빨라지고 마지막에 나머지 4바이트(500%8)를 처리할 수 있습니다.

편집:

사실, 이것은memset는 glibc에서 다음을 수행합니다.

http://repo.or.cz/w/glibc.git/blob/HEAD:/string/memset.c

Michael이 지적한 바와 같이 (컴파일 시에 어레이 길이를 알 수 있는) 경우에 따라서는 C 컴파일러는 인라인으로 할 수 있습니다.memset함수 호출의 오버헤드를 제거합니다.Glibc는 어셈블리에 최적화된 버전의memsetamd64와 같은 대부분의 주요 플랫폼:

http://repo.or.cz/w/glibc.git/blob/HEAD:/sysdeps/x86_64/memset.S

루프 수를 줄이는 루프 언롤링 등의 다른 기술도 사용할 수 있습니다.memset() 코드는 유명한 더프의 디바이스를 모방할 수 있습니다.

void *duff_memset(char *to, int c, size_t count)
{
    size_t n;
    char *p = to;
    n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *p++ = c;
    case 7:      *p++ = c;
    case 6:      *p++ = c;
    case 5:      *p++ = c;
    case 4:      *p++ = c;
    case 3:      *p++ = c;
    case 2:      *p++ = c;
    case 1:      *p++ = c;
            } while (--n > 0);
    }
    return to;
}

과거에는 실행 속도를 높이는 데 사용되었습니다.그러나 현대 아키텍처에서는 이로 인해 코드 크기가 커지고 캐시 누락이 증가하는 경향이 있습니다.

따라서 컴파일러 최적화의 품질, 특별한 하드웨어 명령의 활용 능력, 동작 중인 데이터의 양 및 기본 운영 체제의 기능(페이지 장애 관리, TLB 누락, Copy-On-Write)에 따라 어떤 구현이 더 빠를지 알 수 없습니다.

예를 들어 glibc에서는 bzero()strcpy()와 같은 다양한 "copy/set" 함수와 함께 memset()의 구현은 SSE나 AVX같은 다양한 최적화된 하드웨어 명령을 활용하기 위해 아키텍처에 의존합니다.

컴파일러와 라이브러리에 의존합니다.오래된 컴파일러 또는 단순한 컴파일러의 경우 memset은 라이브러리에 구현되어 커스텀루프보다 성능이 우수하지 않습니다.

사용할 가치가 있는 거의 모든 컴파일러에 대해 memset은 본질적인 함수이며 컴파일러는 이를 위해 최적화된 인라인 코드를 생성합니다.

다른 사람들은 프로파일링과 비교를 제안했지만, 저는 신경 쓰지 않습니다.그냥 memset을 사용하세요.코드는 간단하고 이해하기 쉽다.벤치마크에서 이 코드가 퍼포먼스 핫스팟임을 알 수 있을 때까지 걱정하지 마십시오.

정상적인 컴파일러에서는 for 루프가 인식되어 최적의 인라인시퀀스 또는 memset 호출 중 하나로 대체됩니다.또한 버퍼 크기가 작을 경우 memset을 최적의 인라인시퀀스로 바꿉니다.

실제로 최적화 컴파일러를 사용하면 생성된 코드(따라서 성능)는 동일합니다.

상기의 의견에 동의하다.사정에 따라 다르겠지.단, 확실히 memset이 for-loop과 같거나 빠릅니다.사용 환경이 불분명하거나 테스트하기 귀찮은 경우 안전한 경로를 사용하여 memset을 사용합니다.

언급URL : https://stackoverflow.com/questions/7367677/is-memset-more-efficient-than-for-loop-in-c

반응형