왜 memcpy보다 memmove가 더 빠른가?
나는 시간의 50%를 memmove(3)에서 보내는 애플리케이션에서 성능 핫스팟을 조사하고 있다.응용 프로그램은 정렬된 배열로 수백만 개의 4바이트 정수를 삽입하고, 삽입된 값을 위한 공간을 만들기 위해 데이터를 "오른쪽으로" 이동시키기 위해 memmove를 사용한다.
내 예상은 기억 복사 속도가 엄청나게 빠르다는 것이었고, 그렇게 많은 시간이 내 사랑에서 소비된다는 것에 놀랐다.그러나 그때 나는 memmove가 중복되는 지역을 이동하기 때문에 느리다는 생각을 갖게 되었는데, 그것은 큰 페이지의 메모리를 복사하는 대신, 긴밀하게 순환하여 실행되어야 한다.나는 memcpy와 memmove 사이에 성능 차이가 있는지 알아보기 위해 작은 마이크로벤치마크를 썼는데, memcpy가 손을 뗄 것으로 예상한다.
나는 두 대의 기계(코어 i5, 코어 i7)에 대한 벤치마크를 실행했는데, memmove가 실제로 memcpy보다 더 빠른 것을 보았고, 구형 코어 i7에서는 거의 두 배나 더 빠른 것을 발견했다!지금 나는 설명을 찾고 있다.
여기 나의 벤치마크가 있다.memcpy로 100mb를 복사한 뒤 membove로 100mb 정도를 이동하는데, 소스와 목적지가 겹친다.소스와 목적지에 대한 다양한 "간섭"이 시도된다.각 테스트는 10회 실행되며, 평균 시간은 인쇄된다.
https://gist.github.com/cruppstahl/78a57cdf937bca3d062c
코어 i5에 대한 결과(리눅스 3.5.0-54-generic #81~precise1-Ubuntu SMP x86_64 GNU/리눅스, gcc는 4.6.3(Ubuntu/Linaro 4.6.3-1ubuntu5)이다.괄호 안의 숫자는 소스와 목적지 사이의 거리(갭 크기)이다.
memcpy 0.0140074
memmove (002) 0.0106168
memmove (004) 0.01065
memmove (008) 0.0107917
memmove (016) 0.0107319
memmove (032) 0.0106724
memmove (064) 0.0106821
memmove (128) 0.0110633
Memmove는 SSE에 최적화된 조립자 코드로 구현되며, 뒤에서 앞으로 복사한다.하드웨어 프리페치를 사용하여 데이터를 캐시에 로드하고, 128바이트를 XMM 레지스터에 복사한 후 대상에 저장한다.
(memcpy-ssse3-back).S, 라인 1650 ff)
L(gobble_ll_loop):
prefetchnta -0x1c0(%rsi)
prefetchnta -0x280(%rsi)
prefetchnta -0x1c0(%rdi)
prefetchnta -0x280(%rdi)
sub $0x80, %rdx
movdqu -0x10(%rsi), %xmm1
movdqu -0x20(%rsi), %xmm2
movdqu -0x30(%rsi), %xmm3
movdqu -0x40(%rsi), %xmm4
movdqu -0x50(%rsi), %xmm5
movdqu -0x60(%rsi), %xmm6
movdqu -0x70(%rsi), %xmm7
movdqu -0x80(%rsi), %xmm8
movdqa %xmm1, -0x10(%rdi)
movdqa %xmm2, -0x20(%rdi)
movdqa %xmm3, -0x30(%rdi)
movdqa %xmm4, -0x40(%rdi)
movdqa %xmm5, -0x50(%rdi)
movdqa %xmm6, -0x60(%rdi)
movdqa %xmm7, -0x70(%rdi)
movdqa %xmm8, -0x80(%rdi)
lea -0x80(%rsi), %rsi
lea -0x80(%rdi), %rdi
jae L(gobble_ll_loop)
왜 memcpy보다 memmove가 더 빠를까?나는 memcpy가 메모리 페이지를 복사하기를 기대하는데, 이것은 루프하는 것보다 훨씬 빠를 것이다.최악의 경우 나는 memcpy가 memmove만큼 빠를 것이라고 예상할 것이다.
PS: 코드의 memcpy로 memmove를 대체할 수 없다는 것을 알고 있다.코드 샘플이 C와 C++를 섞은 것으로 알고 있다.이 문제는 정말로 학문적인 목적만을 위한 것이다.
업데이트 1
나는 여러 가지 답을 바탕으로 시험의 변형을 좀 해보았다.
- memcpy를 두 번 실행하면 첫 번째 실행보다 두 번째 실행이 빠르다.
- memcpy의 대버 " " " " " when " " " " " " " ( ( ( ( ( ()
memset(b2, 0, BUFFERSIZE...)
의 첫 도 더 그러면 memcpy의 첫 번째 실행도 더 빠르다. - memcpy는 여전히 나보다 조금 느리다.
결과는 다음과 같다.
memcpy 0.0118526
memcpy 0.0119105
memmove (002) 0.0108151
memmove (004) 0.0107122
memmove (008) 0.0107262
memmove (016) 0.0108555
memmove (032) 0.0107171
memmove (064) 0.0106437
memmove (128) 0.0106648
제 결론: @Oliver Charlesworth의 코멘트를 바탕으로 운영 체제는 memcpy 목적지 버퍼에 처음으로 액세스하는 즉시 물리적 메모리를 커밋해야 한다(누군가 이것을 "증거"하는 방법을 알고 있다면 답변을 추가하십시오!).게다가 @Mats Petersson이 말했듯이 memcpy보다 memmove가 캐시 친화적이다.
좋은 답변과 댓글 고마워!
당신의memmove
통화는 2 ~ 128바이트의 메모리를 섞는 반면, 당신의 전화는 당신의memcpy
출처와 목적지가 완전히 다르다.어찌된 일인지 성능 차이에 대한 설명인데, 같은 장소에 복사해 놓으면 알 수 있을 것이다.memcpy
예를 들어, ideone.com:
memmove (002) 0.0610362
memmove (004) 0.0554264
memmove (008) 0.0575859
memmove (016) 0.057326
memmove (032) 0.0583542
memmove (064) 0.0561934
memmove (128) 0.0549391
memcpy 0.0537919
하지만 그 안에 있는 것은 거의 없다. 이미 잘못되어 있는 메모지에 답장을 쓰는 것이 큰 영향을 미친다는 증거는 없다. 그리고 우리는 분명히 시간의 반을 보지 못하고 있다.하지만 그것은 만드는 데 아무런 문제가 없다는 것을 보여준다.memcpy
사과와 사과를 비교했을 때 불필요하게 느리다.
당신이 때를 때memcpy
, 쓰기는 캐시에 들어가야 한다.사용할 때memmove
앞으로 작은 단계를 복사할 때 복사하고 있는 메모리는 이미 캐시에 저장되어 있을 것이다(2, 4, 16 또는 128바이트 "백"으로 읽혔기 때문에).시도하다memmove
목적지가 수 메가바이트(> 4 * 캐시 크기)이고, 유사한 결과를 얻을 수 있을 것이라고 생각한다.
대규모 메모리 작업을 수행할 때 모든 것이 캐시 유지보수에 관한 것임을 보증한다.
역사적으로 memmove와 memcpy는 같은 기능이다.그들은 같은 방식으로 일했고 같은 시행을 했다.그리고 나서 memcpy는 어떤 특정한 방법으로 중복되는 영역을 다루기 위해 정의될 필요가 없다는 것을 깨달았다.
최종 결과는 memove가 성능에 영향을 주더라도 특정한 방식으로 중복 영역을 처리하도록 정의되었다는 것이다. memcpy는 오버랩되지 않는 영역에 사용할 수 있는 최고의 알고리즘을 사용해야 한다.그 구현들은 보통 거의 동일하다.
당신이 부딪친 문제는 x86 하드웨어의 변형이 너무 많아서 어떤 방식으로 메모리를 옮기는 것이 가장 빠를지 알 수 없다는 것이다.그리고 한 가지 상황에서 결과가 나온다고 생각하더라도 메모리 레이아웃에 다른 '스트라이드'를 갖는 것만큼 간단한 것이 캐시 성능을 엄청나게 다르게 만들 수 있다.
실제로 무엇을 하고 있는지 벤치마킹하거나 문제를 무시하고 C 라이브러리에 대해 수행한 벤치마크에 의존할 수 있다.
편집: 아, 그리고 마지막으로, 많은 메모리 콘텐츠를 옮기는 것은 매우 느리다.간단한 B-Tree 구현으로 당신의 정수를 처리할 수 있는 어플리케이션이 더 빨리 실행될 것 같다. (오, 그렇구나, 알았어)
편집2: 코멘트에서 확장을 요약하려면:마이크로벤치마크가 문제지, 그것은 당신이 생각하는 것을 측정하는 것이 아니다.memcpy와 memove에게 주어진 업무는 서로 현저하게 다르다.memcpy에 주어진 작업이 memcpy 또는 memcpy와 함께 여러 번 반복된다면, 최종 결과는 영역이 겹치지 않는 한 어떤 메모리 시프트 기능을 사용하는가에 따라 달라지지 않을 것이다.
"memcpy가 memmove보다 더 효율적이다."당신의 경우, 당신은 아마도 두 가지 기능을 실행하는 동안 정확히 같은 일을 하지 않을 것이다.
일반적으로 필요한 경우에만 memmove를 사용하십시오.소스 및 대상 영역이 오버랩될 가능성이 매우 높을 때 사용하십시오.
참조: https://www.youtube.com/watch?v=Yr1YnOVG-4g 제리 케인 박사, (Stanford Intro Systems 강의 - 7) 시간: 36:00
참조URL: https://stackoverflow.com/questions/28623895/why-is-memmove-faster-than-memcpy
'programing' 카테고리의 다른 글
프스레드 vs.오픈MP (0) | 2022.05.05 |
---|---|
Vuex 스토어에 액세스할 수 없음 (0) | 2022.05.05 |
Vuex 작업을 여러 파일로 분할 (0) | 2022.05.05 |
사용모멘트VueJS 3의 JS 전역 (0) | 2022.05.05 |
.bss 세그먼트가 필요한 이유는? (0) | 2022.05.05 |