내 코드의 직렬 성능을 향상시키기위한 좋은 전략은 무엇입니까?


66

나는 계산 과학 분야에서 일하고 있으며 결과적으로 많은 코드의 과학적 처리량을 높이고 이러한 코드의 효율성을 이해하려고 노력하는 데 사소한 시간을 소비합니다.

작업중인 소프트웨어의 성능과 가독성 / 재사용 성 / 유지 보수성 간의 균형을 평가하고 성능을 발휘할시기라고 결정했다고 가정 해 보겠습니다. 또한 플롭 / s 및 메모리 대역폭 측면에서 문제에 대한 더 나은 알고리즘이 없다는 것을 알고 있다고 가정 해 봅시다. 내 코드 기반이 C, C ++ 또는 Fortran과 같은 저수준 언어로되어 있다고 가정 할 수도 있습니다. 마지막으로 코드에서 병렬 처리가 없거나 단일 코어의 성능에만 관심이 있다고 가정 해 봅시다.

가장 먼저 시도해야 할 것이 무엇입니까? 얼마나 많은 성능을 얻을 수 있는지 어떻게 알 수 있습니까?

답변:


66

우선, 기술자Dan 이 지적했듯이 프로파일 링은 필수적입니다. 개인적 으로 Linux에서 인텔의 VTune 증폭기 를 사용 합니다. 시간이 어디에서 무엇을했는지에 대한 매우 세밀한 개요를 제공합니다.

알고리즘을 변경하지 않으려는 경우 (즉, 모든 최적화를 더 이상 사용하지 않는 주요 변경 사항이없는 경우) 큰 차이를 만들 수있는 일반적인 구현 세부 정보를 찾는 것이 좋습니다.

  • 메모리 위치 : 함께 읽거나 사용하는 데이터가 함께 저장되어 있습니까?

  • 메모리 정렬 : 복식은 실제로 4 바이트로 정렬됩니까? 당신은 어떻게 포장 structs했습니까? pedantic하려면 posix_memalign대신을 사용하십시오 malloc.

  • 캐시 효율성 : 로컬 리티는 대부분의 캐시 효율성 문제를 처리하지만 자주 읽고 쓰는 작은 데이터 구조가있는 경우 정수 배수 또는 캐시 라인의 일부 (보통 64 바이트) 인 경우 도움이됩니다. 또한 데이터가 캐시 라인의 크기에 맞춰지는 경우에도 도움이됩니다. 이를 통해 데이터를로드하는 데 필요한 읽기 수를 대폭 줄일 수 있습니다.

  • 벡터화 : 아니요, 직접 코딩 한 어셈블러에 신경 쓰지 마십시오. gccSSE / AltiVec / 모든 명령을 자동으로 변환하는 벡터 유형을 제공합니다.

  • 명령 수준 병렬 처리 : 벡터화의 자식. 자주 반복되는 계산이 제대로 벡터화되지 않으면 입력 값을 누적하고 여러 값을 한 번에 계산해 볼 수 있습니다. 루프 언 롤링과 비슷합니다. 여기서 악용하는 것은 CPU에 일반적으로 코어 당 둘 이상의 부동 소수점 단위가 있다는 것입니다.

  • 산술 정밀도 : 당신이하는 모든 일에서 배정도 산술이 정말로 필요합니까? 예를 들어 뉴턴 반복에서 수정을 계산하는 경우 일반적으로 계산하는 모든 숫자가 필요하지 않습니다. 보다 자세한 내용은 백서를 참조하십시오 .

이 트릭 중 일부는 daxpy_cvec 스레드 에서 사용됩니다 . 당신이 포트란 (내 책에서 저수준 언어가 아님)을 사용한다면, 대부분의 "트릭"을 거의 통제 할 수 없을 것입니다.

모든 프로덕션 작업에 사용하는 클러스터와 같은 일부 전용 하드웨어에서 실행중인 경우 사용 된 CPU의 특정 사항을 읽을 수도 있습니다. 해당 아키텍처를 위해 직접 어셈블러로 자료를 작성하는 것이 아니라 놓칠 수있는 다른 최적화 방법을 찾도록 영감을 줄 수 있습니다. 기능을 아는 것은이를 악용 할 수있는 코드를 작성하는 데 필요한 첫 번째 단계입니다.

최신 정보

이 글을 쓴 지 얼마되지 않아서 그렇게 인기있는 답변이 된 것을 보지 못했습니다. 이러한 이유로 한 가지 중요한 점을 추가하고 싶습니다.

  • 지역 컴퓨터 과학자에게 문의하십시오 : 알고리즘 및 / 또는 계산을보다 효율적 / 우아한 / 병렬로 만드는 데 전념하는 학문이 있다면 멋지지 않습니까? 우리 모두 조언을 구할 수 있습니까? 좋은 소식은, 그 학문이 존재한다는 것입니다 : 컴퓨터 과학. 아마도 기관에 전담 부서가 있습니다. 이 사람들과 대화하십시오.

나는 많은 비 컴퓨터 과학자들에게 이것이 아무것도 아닌 다른 사람들의 일화에 대한 추억을 가져 왔던 학문과 실망스러운 토론에 대한 기억을 되 찾을 것이라고 확신합니다. 낙심하지 마십시오. 학제 간 협업은 까다로운 작업이며 약간의 작업이 필요하지만 보상은 엄청납니다.

내 경험상 컴퓨터 과학자 (CS)로서의 비결은 기대와 의사 소통을 모두 얻는 것입니다.

기대- 현명하게도 CS는 문제가 흥미 롭다고 생각 될 때만 도움을 줄 것입니다. 이것은 이해하지 못하는 문제에 대해 작성했지만 실제로 주석 처리하지 않은 코드 조각을 최적화 / 벡터화 / 병렬화하려는 시도는 거의 제외합니다. CS는 일반적으로 근본적인 문제, 예를 들어이를 해결하는 데 사용되는 알고리즘에 더 관심이 있습니다. 그들에게 해결책 을주지 말고 문제를 해결하십시오 .

또한 CS가 " 이 문제는 이미 해결되었습니다 "라고 말할 준비 를하고 논문에 대한 참고 자료를 제공하십시오. 조언 :이 백서를 읽고 문제에 실제로 적용되는 경우 제안하는 알고리즘을 구현하십시오. 이것은 잘난 척하는 CS가 아니며, 단지 당신을 도운 CS입니다. 문제를 일으키지 마십시오. 문제가 계산 상 흥미롭지 않은 경우, 즉 이미 해결되었고 솔루션이 최적으로 표시되면 문제가 해결되지 않으며 코드를 훨씬 적게 작성합니다.

의사 소통 측면 에서는 대부분의 CS가 해당 분야의 전문가가 아니라는 점을 기억 하고, 방법이유가 아니라 현재하고있는 일 에 대해 문제를 설명하십시오 . 우리는 일반적으로 정말 걱정하지 않는 이유 , 그리고이 방법 입니다, 잘, 우리는 최선의 일을.

예를 들어, 저는 현재 SPHMultipoles 기반으로 더 나은 시뮬레이션 코드 버전을 작성하기 위해 많은 전산 우주 론자들과 협력하고 있습니다. 암흑 물질 과 은하 후광 (huh?)의 관점에서 말을 멈추고 계산의 핵심으로 드릴 다운하는 데 약 3 회의가 걸렸습니다 . 즉, 각 입자의 주어진 반경 내에서 모든 이웃을 찾아야합니다. 그들 위에 양을 넣은 다음, 모든 이웃을 다시 실행하고 다른 계산에 그 양을 적용하십시오. 그런 다음 입자 또는 적어도 일부를 이동하고 다시 수행하십시오. 당신은 전자가 엄청나게 흥미로울 수 있지만 (후!), 후자는 알고리즘에 대한 생각을 시작하기 위해 이해해야하는 것입니다.

그러나 나는 요점에서 분기하고 있습니다 : 계산을 빠르게하는 데 관심이 있고 컴퓨터 과학자가 아닌 경우, 대화하십시오.


4
프로파일 링 도구가 진행됨에 따라 나는 valgrind를 잊지 않을 것 입니다.
GertVdE

1
페드로에게 동의합니다. 최적화되는 프로그램이 F1 경주 용 자동차와 같을 때 이미 최적에 가깝습니다. 내가 과학적으로 보았지만 실제로 보지 않는 프로그램은 종종 Cadillac Coupe DeVilles와 비슷합니다. 실제 성능을 얻으려면 많은 양의 지방을 잘라낼 수 있습니다. 그 후 사이클 쉐이빙이 시작됩니다.
Mike Dunlavey

1
@ MikeDunlavey : 완전히 동의합니다. 알고리즘 관련 문제를 해결하기 위해 답변에 업데이트를 추가했습니다.
Pedro

1
@MikeDunlavey, 내가 생각 CS 민속 :)
페드로

2
나는 U 매스 로웰에서 한 연설에서 이것을 증명했다. 730x 속도의 모든 단계를 보여주는 라이브 데모였습니다. 한 교수는 반 다스 중 하나를 지적했다고 생각합니다.
Mike Dunlavey

38

과학 소프트웨어는 튜닝이 필요한 것을 아는 한 다른 소프트웨어와 크게 다르지 않습니다.

내가 사용하는 방법은 무작위 일시 중지 입니다. 나를 위해 찾은 속도 향상의 일부는 다음과 같습니다.

logand와 같은 함수에 많은 시간을 소비 exp하면 해당 함수에 대한 인수가 호출되는 포인트의 함수로 무엇인지 알 수 있습니다. 종종 같은 주장으로 반복해서 부르고 있습니다. 그렇다면 메모를하면 엄청난 속도 향상 요소가 발생합니다.

BLAS 또는 LAPACK 함수를 사용하는 경우 배열 복사, 행렬 곱하기, 콜레 스키 변환 등의 루틴에 많은 시간이 소요될 수 있습니다.

  • 어레이를 복사하는 루틴은 속도가 아니라 편의를위한 것입니다. 덜 편리하지만 더 빠른 방법이 있습니다.

  • 행렬을 곱하거나 거꾸로 돌리거나 콜레 스키 변환을 수행하는 루틴에는 위쪽 또는 아래쪽 삼각형의 경우 'U'또는 'L'과 같은 옵션을 지정하는 문자 인수가있는 경향이 있습니다. 다시, 그것들은 편의상 존재합니다. 내가 찾은 것은 매트릭스가 크지 않았기 때문에 루틴은 옵션을 해독하기 위해 문자비교 하기 위해 서브 루틴을 호출하는 데 절반 이상을 소비하고있었습니다 . 가장 비용이 많이 드는 수학 루틴의 특수 목적 버전을 작성하면 속도가 크게 향상되었습니다.

후자를 확장 할 수 있다면 행렬 곱셈 루틴 DGEMM은 LSAME를 호출하여 문자 인수를 해독합니다. "양호한"것으로 간주되는 포괄적 인 백분율 시간 (유일한 통계 가치)을 보면 80 %와 같은 총 시간의 일부를 사용하여 DGEMM을 표시하고 50 %와 같은 총 시간의 일부를 사용하여 LSAME를 표시 할 수 있습니다. 전자를 살펴보면 "잘 최적화해야하므로 그렇게 할 수있는 일은 많지 않다"고 말하고 싶은 유혹을받을 것이다. 후자를 보면, "어? 그게 다 뭐야? 그것은 아주 작은 일 과일뿐입니다.이 프로파일 러는 틀렸어 야합니다!"라고 말하고 싶을 것입니다.

그것은 틀린 것이 아니며, 당신이 알아야 할 것을 말하지 않습니다. 무작위로 일시 중지하는 것은 DGEMM이 스택 샘플의 80 %에 있고 LSAME가 50 %에 있다는 것입니다. (그것을 탐지하기 위해 많은 샘플이 필요하지 않습니다. 10은 일반적으로 충분합니다.) 또한, 많은 샘플에서 DGEMM은 몇 가지 다른 코드 라인에서 LSAME호출 하는 중입니다.

이제 루틴이 왜 그렇게 많은 시간을 소비 하는지 알고 있습니다. 또한 코드 내 에서이 모든 시간을 보내기 위해 어디에서 호출 되는지 알고 있습니다. 그래서 나는 무작위로 일시 중지를 사용하고 프로파일 러가 얼마나 잘 만들어 졌는지에 대한 황달 한 견해를 취합니다. 진행 상황을 알려주는 것보다 측정을 얻는 데 더 관심이 있습니다.

수학 라이브러리 루틴이 n 도로 최적화되었다고 가정하기는 쉽지만 실제로는 광범위한 목적으로 사용할 수 있도록 최적화되었습니다. 추정하기 쉬운 것이 아니라 실제로 진행되고 있는 것을 볼 필요 가 있습니다.

추가 : 마지막 두 가지 질문에 대답하십시오.

가장 먼저 시도해야 할 것이 무엇입니까?

10-20 개의 스택 샘플을 가져 와서 요약하지 말고 각 샘플이 무엇을 말하는지 이해하십시오. 먼저, 마지막, 중간에 수행하십시오. (젊은 Skywalker는 "시도"가 없습니다.)

얼마나 많은 성능을 얻을 수 있는지 어떻게 알 수 있습니까?

xβ(s+1,(ns)+1)sn1/(1x)n=10s=5x
여기에 이미지 설명을 입력하십시오
xx

전에 말씀 드렸듯이 더 이상 할 수 없을 때까지 전체 절차를 반복 할 수 있으며 복합 속도 향상 비율이 상당히 클 수 있습니다.

(s+1)/(n+2)=3/22=13.6%.) 다음 그래프에서 아래쪽 곡선은 분포입니다.

여기에 이미지 설명을 입력하십시오

우리가 40 개나되는 샘플을 한 번에 가져 왔고 그중 2 개에서만 문제가 발생했는지 생각해보십시오. 더 큰 곡선에서 볼 수 있듯이 해당 문제의 예상 비용 (모드)은 5 %입니다.

"거짓 긍정적"이란 무엇입니까? 문제를 해결하면 예상보다 적은 이익을 실현하고 문제를 해결 한 것을 후회할 수 있습니다. 곡선은 (문제가 "작은"경우) 이득 그것을 보여주는 샘플의 비율보다 작을 있지만 평균적으로 더 클 것임을 보여줍니다.

훨씬 더 심각한 위험이 있습니다- "거짓 부정적인". 그때 문제가 있지만 찾을 수 없습니다. (기여의 부재가 부재의 증거로 취급되는 경향이있는 "확인 편향"이다.)

당신이 프로파일 러 (좋은)에 도착하면 문제가 실제로 무엇인지에 대해 훨씬 덜 정확한 정보의 비용으로 훨씬 더 정확한 측정 (오 탐지하여 적은 기회)를 얻을 수있다 이다 (그것을 발견하고 점점 따라서 적은 기회 모든 이득). 이는 달성 할 수있는 전체 속도 향상을 제한합니다.

프로파일 러 사용자가 실제로 속도 향상 요소를보고하도록 권장합니다.


다시 만들어야 할 또 다른 요점이 있습니다. 오 탐지에 대한 페드로의 질문.

그는 고도로 최적화 된 코드에서 작은 문제를 해결할 때 어려움이있을 수 있다고 언급했습니다. (나에게 작은 문제는 총 시간의 5 % 이하를 차지하는 문제입니다.)

5 %를 제외하고는 완전히 최적 인 프로그램을 구성하는 것이 전적으로 가능하므로이 답변 에서처럼 경험적으로 만 해결할 수 있습니다 . 경험적 경험을 일반화하기 위해 다음과 같이 진행됩니다.

작성된 프로그램에는 일반적으로 최적화 기회가 여러 개 있습니다. (이를 "문제"라고 부를 수 있지만 종종 완벽하게 좋은 코드이며, 상당히 개선 될 수 있습니다.)이 다이어그램은 시간이 오래 걸리는 인공 프로그램 (100 초)을 보여 주며 문제 A, B, C, ... 발견 및 고정시 원래 100 대 30 %, 21 % 등을 절약 할 수 있습니다.

여기에 이미지 설명을 입력하십시오

문제 F는 원래 시간의 5 %를 소비하므로 "소형"이며 40 개 이상의 샘플이 없으면 찾기가 어렵습니다.

그러나 처음 10 개의 샘플은 문제 A를 쉽게 찾을 수 있습니다. ** 문제가 해결되면 100/70 = 1.43x의 속도로 70 초만 걸립니다. 그것은 프로그램을 더 빠르게 만들뿐만 아니라 나머지 문제에 의해 취해진 비율을 그 비율로 확대합니다. 예를 들어 문제 B는 원래 총 21 % 인 21 초를 사용했지만 A를 제거한 후 B는 70 초에서 21 초 또는 30 %를 차지하므로 전체 프로세스가 반복되는시기를 찾기가 더 쉽습니다.

프로세스가 5 번 반복되면 실행 시간은 16.8 초이며, 그 중 문제 F는 5 %가 아닌 30 %이므로 10 개의 샘플이 쉽게 찾을 수 있습니다.

그게 요점입니다. 경험적으로 프로그램에는 크기 분포가있는 일련의 문제가 있으며 발견 및 수정 된 문제로 인해 나머지 문제를 쉽게 찾을 수 있습니다. 이를 달성하기 위해, 시간이 걸리고, 총 속도를 제한하며, 나머지 문제를 확대하지 못하기 때문에 문제가 발생하면 건너 뛸 수 없습니다. 그래서 숨어 있는 문제를 찾는 것이 매우 중요합니다 .

문제 A에서 F까지 발견되고 수정되면 속도는 100 / 11.8 = 8.5x입니다. 둘 중 하나를 놓치면 (예 : D) 속도는 100 / (11.8 + 10.3) = 4.5x에 불과합니다. 그것이 거짓 부정에 대한 대가입니다.

따라서 프로파일 러가 "여기에 중요한 문제는없는 것 같습니다"(예 : 좋은 코더, 이것은 실제로 최적의 코드 임)라고 말하면 맞을 수도 있고 아닐 수도 있습니다. ( 거짓 부정 .) 다른 프로파일 링 방법을 시도하고 있음을 발견하지 않으면 더 빠른 속도로 해결할 문제가 더 있는지 확실하지 않습니다. 필자의 경험에 따르면, 프로파일 링 방법에는 요약 된 많은 수의 샘플이 아니라 적은 수의 샘플이 필요합니다. 여기서 각 샘플은 최적화 기회를 인식 할 수있을만큼 충분히 이해됩니다.

2/0.3=6.671 - pbinom(1, numberOfSamples, sizeOfProblem)1 - pbinom(1, 20, 0.3) = 0.9923627

xβ(s+1,(ns)+1)nsy1/(1x)xyy1BetaPrime 배포. 이 동작에 도달하여 2 백만 개의 샘플로 시뮬레이션했습니다.

         distribution of speedup
               ratio y

 s, n    5%-ile  95%-ile  mean
 2, 2    1.58    59.30   32.36
 2, 3    1.33    10.25    4.00
 2, 4    1.23     5.28    2.50
 2, 5    1.18     3.69    2.00
 2,10    1.09     1.89    1.37
 2,20    1.04     1.37    1.17
 2,40    1.02     1.17    1.08

 3, 3    1.90    78.34   42.94
 3, 4    1.52    13.10    5.00
 3, 5    1.37     6.53    3.00
 3,10    1.16     2.29    1.57
 3,20    1.07     1.49    1.24
 3,40    1.04     1.22    1.11

 4, 4    2.22    98.02   52.36
 4, 5    1.72    15.95    6.00
 4,10    1.25     2.86    1.83
 4,20    1.11     1.62    1.31
 4,40    1.05     1.26    1.14

 5, 5    2.54   117.27   64.29
 5,10    1.37     3.69    2.20
 5,20    1.15     1.78    1.40
 5,40    1.07     1.31    1.17

(n+1)/(ns)s=ny

이것은 5, 4, 3, 2 개의 샘플 중 2 개의 적중에 대한 속도 향상 요인의 분포와 그 평균의 도표입니다. 예를 들어, 3 개의 샘플이 수집되고 그 중 2 개가 문제에 부딪 히고 해당 문제를 제거 할 수있는 경우 평균 속도 향상 요소는 4 배가됩니다. 2 개의 적중이 2 개의 샘플에서만 보이는 경우, 무한 루프를 가진 프로그램이 0이 아닌 확률로 존재하기 때문에 평균 속도가 정의되지 않습니다!

여기에 이미지 설명을 입력하십시오


1
음 ... VTune에서 제공 한 프로파일 러 호출 그래프 또는 "하단"유형 요약을보고있는이 정보를 정확히 얻지 못합니까?
Pedro

2
@Pedro : 경우에만. 스택 샘플 (및 관련 변수)에는 시간 증가가 소비되는 전체 이유가 인코딩됩니다. 왜 소비되는지 알지 못하면 제거 할 수 없습니다. 일부 정보는 정보가 제한되어 있지만 모든 정보가있는 것은 아닙니다 . 당신이 그들 중 일부만 얻지 만 모든 것을 얻지 못하면, 당신이 얻지 못하는 문제는 더 이상의 속도 향상으로부터 당신을 막습니다. 여기여기를 확인 하십시오 .
Mike Dunlavey

아마도 방법을 잘못된 프로파일 링 과 비교하는 것입니다 ... 총 실행 시간에 대한 기여와 무관하게 각 루틴의 프로파일을 살펴보고 동일한 효과로 개선점을 찾을 수도 있습니다. 내가 당신의 접근 방식에서 걱정하는 것은 코드의 "핫스팟"이 점점 작아짐에 따라 추적 오류가 증가한다는 것입니다.
Pedro

@Pedro : 둘 이상의 샘플에서 고칠 수있는 것이 보일 때까지 샘플을 계속 가져 가십시오. 베타 배포판은 관심이 있다면 절약 할 수있는 양을 알려주지 만 속도보다 속도가 떨어지는 것을 두려워하는 경우 더 많을 수있는 기회를 버리고 있다는 것을 알고하십시오. ). 프로파일 러를 요약하면 더 큰 위험은 거짓 부정 입니다. 이 문제가 될 수 있지만, 당신은 단지하고 희망 프로파일 러가 될 수있는 위치에 대한 매우되지 않은 특정 될 때 당신의 직관을 빼낼 것입니다.
Mike Dunlavey

@ 페드로 : 내가 아는 유일한 약점은 스냅 샷을 볼 때 요청자가 숨어있는 비동기 이벤트 또는 비동기 프로토콜을 처리하는 것과 같이 그 시간이 소비되는 이유를 알 수 없다는 것입니다. 더 "일반적인"코드의 경우, "좋은"프로파일 러를 보여 주면 문제가 있거나 찾을 수없는 문제를 보여줄 것입니다. 일반적으로 이러한 문제를 구성하는 방법은 제공되는 목적을 로컬에서 해독 할 수 없도록하는 것입니다. 그리고 그러한 문제는 소프트웨어에 풍부합니다.
Mike Dunlavey

23

뿐만 아니라 당신은 당신의 친밀한 지식이 있어야 할 컴파일러 , 당신은 또한 당신의 친밀한 지식이 목표 아키텍처운영 체제를 .

성능에 영향을 줄 수있는 것은 무엇입니까?

모든 마지막 성능을 짜내려면 대상 아키텍처를 변경할 때마다 코드를 조정하고 다시 최적화해야합니다. 하나의 CPU로 최적화 된 것이 동일한 CPU의 다음 개정에서 차선책이 될 수 있습니다.

이것의 좋은 예는 CPU 캐시입니다. 빠른 소형 캐시가있는 CPU에서 약간 느리고 약간 큰 캐시 가있는 CPU로 프로그램을 이동하면 프로파일 링이 크게 변경 될 수 있습니다.

대상 아키텍처가 변경되지 않더라도 운영 체제에 대한 낮은 수준의 변경도 성능에 영향을 줄 수 있습니다. 스펙터 및 멜트 다운 완화 패치는 일부 워크로드에 큰 영향을 미치므로 최적화를 다시 평가해야 할 수 있습니다.

코드를 최적화 된 상태로 유지하려면 어떻게해야합니까?

고도로 최적화 된 코드를 개발할 때는 모듈 식으로 유지하고 사용 가능한 리소스 및 크기 / 복잡성에 따라 런타임에 사용 된 특정 버전을 선택하는 경우에도 동일한 알고리즘의 다른 버전을 쉽게 교체 할 수 있어야합니다. 처리 될 데이터.

모듈화는 또한 그들은 모두 동일하게 동작하는지 확인 할 수 있도록, 당신의 최적화 unoptimised 모든 버전에서 동일한 테스트 스위트를 사용 할 수있는 의미 A의 신속 각각의 프로파일 과 같은 -에 대한 같은 비교. 나는 계산적으로 집중적 인 코드를“인식을 넘어 최적화 된”다른 사람들을 문서화하고 가르치는 방법에 대한 대답에서 좀 더 자세히 설명 합니다. .

추가 자료

또한 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 것만 큼 David Goldberg의 환상에 경의를 표하는 Ulrich Drepper의 우수한 논문 인 모든 프로그래머가 기억해야 할 내용을 살펴 보는 것이 좋습니다 .

모든 최적화 에는 미래의 안티 최적화 가 될 가능성이 있으므로 가능한 코드 냄새로 간주하여 최소한으로 유지해야합니다. 내 대답은 코딩 할 때 마이크로 최적화가 중요한가요? 개인적인 경험을 통해 구체적인 예를 제시합니다.


8

나는 당신이 질문을 너무 좁게 표현한다고 생각합니다. 필자의 견해로는 데이터 구조와 알고리즘 만 변경하면 100 줄 이상의 코드에서 성능이 크게 향상 될 수 있다는 가정하에 유용한 태도를 취하는 것이 바람직하다. 이 주장.


3
원칙적으로 동의했지만 알고리즘 / 데이터 구조의 성능과 기본 하드웨어의 세부 사항 간의 상호 작용을 과소 평가해서는 안됩니다. 예를 들어 균형 이진 트리는 데이터를 검색 / 저장하는 데 유용 하지만 글로벌 메모리의 대기 시간에 따라 해시 테이블 더 좋습니다.
Pedro

1
동의했다. 알고리즘 및 데이터 구조는 O (10)에서 O (100)로 향상 될 수 있습니다. 그러나 분자 역학 계산, 천체 물리학, 실시간 이미지 및 비디오 처리, 재무 등의 계산 한계 문제에 대해 고도로 조정 된 임계 루프는 전체 애플리케이션 실행 시간이 3 배에서 10 배 빨라질 수 있습니다.
fcruz

상당한 크기의 "프로덕션"코드에서 잘못 정렬 된 중첩 루프를 보았습니다. 그 외에는 당신이 옳다고 생각합니다.
dmckee

8

가장 먼저해야 할 일은 코드를 프로파일 링하는 것입니다. 당신은 발견 할 어떤 그렇지 않으면 당신은 어쨌든 실행 시간의 대부분을 먹는되지 않은 코드의 일부를 최적화 끝낼 수도, 당신은 최적화를 시작하기 전에 프로그램의 일부가 당신을 늦추고있다.

리눅스

gprof 는 꽤 좋지만 각 줄이 아닌 각 함수가 얼마나 많은 시간을 소비하는지 알려줍니다.

애플 OS X

당신은 상어 를 시도 할 수 있습니다 . Apple 개발자 사이트의 다운로드> 개발자 도구> CHUD 4.6.2 (이전 버전) 에서 사용할 수 있습니다 . CHUD에는 BigTop 프론트 엔드, PMC 색인 검색 도구, 토성 기능 레벨 프로파일 러 및 기타 여러 명령과 같은 다른 프로파일 링 도구도 포함되어 있습니다. 상어는 명령 행 버전과 함께 제공됩니다.


+1 프로필? 예, 어떤 식 으로든 ... 추측하는 것보다 훨씬 낫지 만, 특히 gprof 및 다른 많은 프로파일 러에 적용 되는 문제 목록이 있습니다 .
Mike Dunlavey

OS X에서 Shark는 이전 명령입니까? 여기 더 . Mountain Lion에서 기기를 사용해야합니까?
hhh

@hhh : mac 용 GUI 프로파일 러 였지만 더 이상 유지 관리되지 않는 것 같습니다. 이 답변을 작성한 이후 애플 컴퓨터에서 프로그래밍하지 않았으므로 도움을 많이 줄 수 없습니다.
Dan

1
Apple 개발자 사이트의 다운로드> 개발자 도구> CHUD 4.6.2에 있습니다. 여기에 있는 이전 버전 과 여기 에는 모든 종류의 프로파일 링이 포함되어 있습니다. 불행히도이 설치는 성공하지 못합니다. "제조업체에 문의하십시오". Shark는 Lion 이후 Xcode에서 나온 후 MacUpdate에서 무료 도구가 된 후 Apple Dev 사이트로 돌아갔습니다.
hhh

@ hhh : 당신은 나보다 이것에 대답하는 것이 더 자격이있는 것 같습니다. 답변을 수정하여 업데이트하거나 직접 작성하십시오.
Dan

7

얼마나 많은 성능을 얻을 수 있는지에 대해서는 코드 프로파일 링에서 결과를 가져 와서 시간의 "p"분량의 조각을 식별한다고 가정 해 봅시다. "s"의 계수로만 해당 조각의 성능을 향상 시키려면 전체 속도가 1 / ((1-p) + p / s)가됩니다. 따라서 속도를 최대 1 / (1-p)만큼 높일 수 있습니다. 잘만되면 당신은 높은 p의 지역을 가지고있다! 이것은 직렬 최적화에 대한 암달의 법칙 과 동일 합니다.


5

코드 최적화는 신중하게 수행해야합니다. 이미 코드를 이미 디버깅했다고 가정 해 봅시다. 특정 우선 순위를 따르는 경우 많은 시간을 절약 할 수 있습니다.

  1. 가능한 경우 고도로 최적화 된 (또는 전문적으로 최적화 된) 라이브러리를 사용하십시오. 예를 들어 FFTW, OpenBlas, Intel MKL, NAG 라이브러리 등이있을 수 있습니다. GotoBLAS 개발자와 같이 재능이 뛰어나지 않으면 전문가를 이길 수 없습니다.

  2. 프로파일 러를 사용하여 (이 목록에서 이미 Intel Threade, valgrind, gprof, gcov 등의 일부 목록이 이미 명명 된) 프로파일 러를 사용하여 코드에서 가장 많은 시간이 걸리는 부분을 찾으십시오. 거의 호출되지 않는 코드 부분을 최적화하는 데 시간을 낭비하지 않아도됩니다.

  3. 프로파일 러 결과에서 가장 많은 시간이 소요 된 코드 부분을 살펴보십시오. 알고리즘의 본질이 무엇인지 결정하십시오. CPU 바인딩 또는 메모리 바인딩입니까? 각기 다른 최적화 기술이 필요합니다. 많은 캐시 누락이 발생하면 메모리에 병목 현상이 발생할 수 있습니다. CPU가 메모리 사용 가능을 기다리는 동안 클럭주기를 낭비하고 있습니다. 루프가 시스템의 L1 / L2 / L3 캐시에 맞는지 생각해보십시오. 루프에 "if"문이있는 경우 프로파일 러가 분기 잘못 예측에 대해 말하는 것이 있는지 확인하십시오. 시스템의 브랜치 오판 벌칙은 무엇입니까? 그건 그렇고, Intel Optimization Reference Manuals [1]에서 분기 잘못 예측 데이터를 얻을 수 있습니다. Intel 설명서에서 볼 수 있듯이 분기 잘못된 예측 페널티는 프로세서마다 다릅니다.

  4. 마지막으로 프로파일 러가 식별 한 문제를 해결하십시오. 많은 기술들이 여기에서 이미 논의되었습니다. 최적화에 관한 많은 훌륭하고 신뢰할 수있는 포괄적 인 리소스도 제공됩니다. 이름을 2 개로 지정하기 위해 Intel Optimization Reference Manual [1]과 Agner Fog [2]의 5 가지 최적화 매뉴얼이 있습니다. 컴파일러에서 루프 언 롤링, 메모리 정렬 등과 ​​같이 컴파일러에서 이미 수행 한 경우 수행 할 필요가없는 사항이 있습니다. 컴파일러 설명서를주의해서 읽으십시오.

참고 문헌 :

[1] 인텔 64 및 IA-32 아키텍처 최적화 참조 설명서 : http://www.intel.sg/content/dam/doc/manual/64-ia-32-architectures-optimization-manual.pdf

[2] Agner Fog, "소프트웨어 최적화 리소스": http://www.agner.org/optimize/

  • "C ++에서 소프트웨어 최적화 : Windows, Linux 및 Mac 플랫폼을위한 최적화 안내서"
  • "조립 언어에서 서브 루틴 최적화 : x86 플랫폼을위한 최적화 안내서"
  • "인텔, AMD 및 VIA CPU의 마이크로 아키텍처 : 어셈블리 프로그래머 및 컴파일러 제조업체를위한 최적화 안내서"
  • "명령 테이블 : 인텔, AMD 및 VIA CPU에 대한 명령 대기 시간, 처리량 및 마이크로 작업 내역 목록"
  • "다른 C ++ 컴파일러 및 운영 체제에 대한 호출 규칙"

3

나는 여기에 많은 다른 사람들처럼 계산 과학자가 아니기 때문에 (그렇지 않을 수 있습니다 :) 요즘 우리가 표준 라이브러리를 사용하는 한 직렬 성능에 너무 많은 시간을 소비하는 것은 거의 없습니다. 코드 확장 성을 높이기 위해 추가 시간 / 노력을 투자하는 것이 더 가치가있을 수 있습니다.

어쨌든 여기에는 성능이 어떻게 향상되었는지에 대한 두 가지 예가 있습니다 (구조화되지 않은 FE 문제).

연재 : 초록 및 관련 텍스트의 후반부를 참조하십시오.

병렬 : 특히 초기화 단계, 초 4.2.


3

이것은 아마도 대답보다 메타 답변 일 것입니다 ...

컴파일러에 친숙한 것을 개발해야합니다. 설명서를 읽고 옵션을 실험하여 가장 효율적으로 얻을 수 있습니다.

@Pedro 디스펜스가 프로그램이 아닌 컴파일을 조정하여 구현할 수 있다는 좋은 조언이 많이 있습니다.


마지막 요점에 동의하지 않습니다. 컴파일러가 무엇을 할 수 있는지 아는 것은 하나의 일이지만 컴파일러가 실제로 무언가를 할 수 있도록 코드를 작성하는 것은 완전히 다른 문제입니다. 데이터를 정렬하거나 필요한 경우 정밀도를 낮추거나 분기가 거의 없거나 전혀 없도록 가장 안쪽의 루프를 다시 작성하는 컴파일러 플래그가 없습니다. 컴파일러를 아는 것은 좋은 일이지만 더 나은 코드를 작성하는 데 도움이되고 코드 자체를 더 잘 만들지는 않습니다.
페드로

1

Linux에서 프로그램을 프로파일 링하는 쉬운 방법은 모드 에서 사용하는 것 perf입니다 stat. 가장 간단한 방법은 다음과 같이 실행하는 것입니다.

perf stat ./my_program args ...

유용한 성능 통계를 제공합니다.

Performance counter stats for './simd_test1':

     3884.559489 task-clock                #    1.000 CPUs utilized
              18 context-switches          #    0.005 K/sec
               0 cpu-migrations            #    0.000 K/sec
             383 page-faults               #    0.099 K/sec
  10,911,904,779 cycles                    #    2.809 GHz
 <not supported> stalled-cycles-frontend
 <not supported> stalled-cycles-backend
  14,346,983,161 instructions              #    1.31  insns per cycle
   2,143,017,630 branches                  #  551.676 M/sec
          28,892 branch-misses             #    0.00% of all branches

     3.885986246 seconds time elapsed

때로는 D- 캐시로드 및 누락도 나열됩니다. 많은 캐시 누락이 발견되면 프로그램이 메모리를 많이 사용하고 캐시를 잘 처리하지 못하는 것입니다. 요즘 CPU는 메모리 대역폭보다 빨라지고 일반적으로 문제는 항상 메모리 액세스입니다.

perf record ./my_program; perf report프로파일 링하기 쉬운 방법을 시도해 볼 수도 있습니다 . 자세한 내용은 매뉴얼 페이지를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.