memset()은 C의 루프보다 효율이 높습니까?
나나?memset()
for
loopsyslog.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는 어셈블리에 최적화된 버전의memset
amd64와 같은 대부분의 주요 플랫폼:
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
'IT이야기' 카테고리의 다른 글
vuejs로 응답을 보내기 전에 악리 응답을 기다리는 방법 (0) | 2022.07.24 |
---|---|
Vuex mapAction을 사용한 '알 수 없는 작업 유형' (0) | 2022.07.24 |
Java에서 목록으로 배열 변환 (0) | 2022.07.24 |
어떻게 cypress에 nuxt 앱vuex점을 노출하기 위해? (0) | 2022.07.24 |
Vuex 맵 상태가 정의되지 않음 상태 (0) | 2022.07.24 |