나는 std::copy
약간의 거의 눈에 띄지 않는 성능 손실 을 가져올 일반적인 지혜에 반대 할 것입니다. 방금 테스트를 수행하여 사실이 아님을 발견했습니다. 성능 차이를 발견했습니다. 그러나 우승자는 std::copy
입니다.
C ++ SHA-2 구현을 작성했습니다. 내 테스트에서 4 개의 SHA-2 버전 (224, 256, 384, 512)을 모두 사용하여 5 개의 문자열을 해시하고 300 번 반복합니다. Boost.timer를 사용하여 시간을 측정합니다. 그 300 루프 카운터는 내 결과를 완전히 안정화시키기에 충분합니다. memcpy
버전과 버전을 번갈아 가며 테스트를 각각 5 회 실행했습니다 std::copy
. 내 코드는 가능한 한 많은 청크에서 데이터를 가져 오는 이점을 활용합니다 (다른 구현은 char
/로 char *
작동하지만 T
/는 T *
( T
오버 플로우 동작이 올바른 사용자 구현에서 가장 큰 유형 임) 작동하므로 빠른 메모리 액세스 내가 할 수있는 가장 큰 유형은 알고리즘 성능의 핵심입니다.
SHA-2 테스트 실행을 완료하는 데 걸리는 시간 (초)
std::copy memcpy % increase
6.11 6.29 2.86%
6.09 6.28 3.03%
6.10 6.29 3.02%
6.08 6.27 3.03%
6.08 6.27 3.03%
memcpy보다 std :: copy의 총 평균 속도 증가 : 2.99 %
내 컴파일러는 Fedora 16 x86_64에서 gcc 4.6.3입니다. 내 최적화 플래그는 -Ofast -march=native -funsafe-loop-optimizations
입니다.
내 SHA-2 구현을위한 코드입니다.
MD5 구현에서도 테스트를 실행하기로 결정했습니다. 결과는 훨씬 덜 안정적이어서 10 번의 런을하기로 결정했습니다. 그러나 처음 몇 번의 시도 후에는 실행마다 크게 다른 결과가 나타 났으므로 일종의 OS 활동이 진행되고 있다고 생각합니다. 나는 다시 시작하기로 결정했다.
동일한 컴파일러 설정 및 플래그. MD5에는 한 가지 버전 만 있으며 SHA-2보다 빠르므로 비슷한 5 개의 테스트 문자열 세트에서 3000 회 반복했습니다.
이것들은 나의 최종 10 결과입니다 :
MD5 테스트 실행을 완료하는 데 걸리는 시간 (초)
std::copy memcpy % difference
5.52 5.56 +0.72%
5.56 5.55 -0.18%
5.57 5.53 -0.72%
5.57 5.52 -0.91%
5.56 5.57 +0.18%
5.56 5.57 +0.18%
5.56 5.53 -0.54%
5.53 5.57 +0.72%
5.59 5.57 -0.36%
5.57 5.56 -0.18%
memcpy에 비해 std :: copy의 총 평균 속도 감소 : 0.11 %
내 MD5 구현을위한 코드
이 결과는 std::copy
MD5 테스트에 사용할 수없는 SHA-2 테스트에 std :: copy가 최적화되어 있음을 나타 냅니다. SHA-2 테스트에서 두 배열은 모두 std::copy
/ 와 같은 함수로 작성되었습니다 memcpy
. 내 MD5 테스트에서 배열 중 하나가 함수에 함수 매개 변수로 전달되었습니다.
나는 std::copy
더 빨리 만들기 위해 무엇을 할 수 있는지 확인하기 위해 조금 더 많은 테스트를 수행 했습니다. 대답은 간단합니다. 링크 시간 최적화를 켜십시오. 다음은 LTO가 켜져있는 결과입니다 (gcc의 옵션 -flto).
-flto를 사용하여 MD5 테스트 실행을 완료하는 데 걸리는 시간 (초)
std::copy memcpy % difference
5.54 5.57 +0.54%
5.50 5.53 +0.54%
5.54 5.58 +0.72%
5.50 5.57 +1.26%
5.54 5.58 +0.72%
5.54 5.57 +0.54%
5.54 5.56 +0.36%
5.54 5.58 +0.72%
5.51 5.58 +1.25%
5.54 5.57 +0.54%
memcpy보다 std :: copy의 총 평균 속도 증가 : 0.72 %
요약하면을 (를) 사용하면 성능이 저하되지 않습니다 std::copy
. 실제로 성능이 향상되는 것으로 보입니다.
결과 설명
그렇다면 왜 std::copy
성능이 향상 될 수 있습니까?
첫째, 인라인 최적화가 켜져있는 한 구현에 대해 속도가 느려질 것으로 기대하지 않습니다. 모든 컴파일러는 적극적으로 인라인합니다. 그것은 다른 많은 최적화를 가능하게하기 때문에 아마도 가장 중요한 최적화 일 것입니다. std::copy
(그리고 모든 실제 구현이 그렇게 생각합니다) 인수가 사소하게 복사 가능하고 메모리가 순차적으로 배치되어 있음을 감지 할 수 있습니다. 이는 최악의 경우 memcpy
합법적 인 경우 std::copy
더 나쁘게 수행되지 않아야 함을 의미합니다 . 의 사소한 구현 std::copy
이 연기를하는 memcpy
"항상 인라인이 속도 나 크기로 최적화 할 때"의 컴파일러의 기준을 충족해야한다.
그러나 std::copy
더 많은 정보를 유지합니다. 를 호출 std::copy
하면 함수는 유형을 그대로 유지합니다. memcpy
에서 작동하며 void *
거의 모든 유용한 정보가 삭제됩니다. 예를 들어,의 배열을 전달 std::uint64_t
하면 컴파일러 또는 라이브러리 구현자가와 64 비트 정렬을 활용할 수 std::copy
있지만로 수행하는 것이 더 어려울 수 있습니다 memcpy
. 이와 같은 알고리즘의 많은 구현은 먼저 범위의 시작 부분에서 정렬되지 않은 부분에 대해 작업 한 다음 정렬 된 부분, 끝에서 정렬되지 않은 부분을 처리하여 작동합니다. 그것이 모두 정렬되도록 보장되면 코드가 더 간단하고 빨라지며 프로세서의 분기 예측기가 정확 해집니다.
조기 최적화?
std::copy
흥미로운 위치에 있습니다. memcpy
현대의 최적화 컴파일러 보다 느리거나 때로는 빠를 것으로 기대합니다 . 또한, 당신이 할 수있는 모든 것이 memcpy
가능 std::copy
합니다. memcpy
버퍼에서 오버랩을 허용하지 않는 반면 std::copy
한 방향으로 오버랩을 지원합니다 ( std::copy_backward
다른 방향으로 오버랩). memcpy
단지이 포인터에서 작동 std::copy
어떤 반복자에서 작동 ( std::map
, std::vector
, std::deque
, 또는 내 자신의 사용자 정의 유형). 다시 말해, std::copy
데이터 덩어리를 복사해야 할 때만 사용해야합니다.
char
구현에 따라, 서명 또는 부호 될 수있다. 바이트 수가> = 128 일 수있는 경우unsigned char
바이트 배열에 사용 하십시오. ((int *)
캐스트도 더 안전 할 것(unsigned int *)
입니다.)