"집중적으로 최적화 된"컴퓨팅 집약적 인 코드를 다른 사람에게 문서화하고 가르치는 방법은 무엇입니까?


11

때때로 계산량이 많은 코드의 1 %가 가장 많은 종류의 저수준 최적화를 필요로합니다. 예를 들어 비디오 처리, 이미지 처리 및 모든 종류의 신호 처리가 있습니다.

목표는 코드를 유지 보수 할 수없고 새로운 개발자가 제거하기 쉽도록 최적화 기술을 문서화하고 가르치는 것 입니다. (*)

(*) 미래의 일부 CPU에서는 특정 최적화가 완전히 쓸모 없을 가능성이 있음에도 불구하고 코드가 삭제됩니다.

소프트웨어 오퍼링 (상업용 또는 공개 소스)은 가장 빠른 코드를 보유하고 최신 CPU 아키텍처를 사용하여 경쟁 우위를 유지한다는 점을 고려할 때 소프트웨어 작성자는 종종 특정 코드를 동일한 출력을 얻는 동안 코드를 더 빠르게 실행하기 위해 코드를 조정해야합니다. 작은 양의 반올림 오류를 허용하는 작업.

일반적으로 소프트웨어 작성자는 여러 버전의 함수를 각 최적화 / 알고리즘 다시 쓰기의 문서로 유지할 수 있습니다. 이 버전을 다른 사람들이 최적화 기술을 연구 할 수 있도록하려면 어떻게해야합니까?

관련 :


1
코드에서 다른 버전을 유지하고 주석을 달았으며 독자에게 무슨 일이 일어나고 있는지에 대한 많은 주석이 있습니다.
Mike Dunlavey

1
그리고 코드가 무엇을하고 있는지 알려주지 말고 왜 그렇게 빠른지 설명하십시오. 필요한 경우 인터넷에서 사용할 수있는 고유 한 Wiki와 같은 문서 또는 리소스에 대한 알고리즘에 대한 링크를 포함합니다 (이 경우 링크 로트를 알고 있어야합니다. .)
Marjan Venema

1
@MikeDunlavey는 : 아야는, 제발 그것을 언급하지 않습니다. 동일한 함수를 여러 번 구현하고 가장 빠른 함수를 호출하십시오. 이렇게하면 다른 버전의 코드로 쉽게 전환하여 모두 벤치 마크 할 수 있습니다.
sleske

2
@sleske 때로는 더 많은 이진 코드를 사용하면 속도가 느려질 수 있습니다.
quant_dev

@quant_dev : 그렇습니다. 나는 코드를 최신 상태로 유지하기 위해 정기적으로 (이상적으로) 코드를 작성하고 실행하는 것이 중요하다고 생각합니다. 디버그 모드로만 빌드하십시오.
sleske

답변:


10

짧은 답변

최적화를 로컬로 유지하고, 명확하게하고, 문서화하고, 최적화하고 소스 코드와 런타임 성능 측면에서 최적화되지 않은 버전과 최적화되지 않은 버전을 쉽게 비교할 수 있도록합니다.

전체 답변

이러한 최적화가 정말 경우 입니다 제품 중요, 당신은 최적화 전에 유용했다뿐만 아니라 이유를 알고 있지만, 또한 미래에 도움이 될 것입니다 여부를 알 도움말 개발자에게 충분한 정보를 제공해야합니다.

이상적으로는 성능 테스트를 빌드 프로세스에 포함시켜야하므로 새로운 기술이 오래된 최적화를 무효화하는시기를 알 수 있습니다.

생각해 내다:

프로그램 최적화의 첫 번째 규칙 :하지 마십시오.

프로그램 최적화의 두 번째 규칙 (전문가에게만 해당) : 아직하지 마십시오. "

— 마이클 에이 잭슨

지금 이 시간 인지 알기 위해서는 벤치마킹 및 테스트가 필요합니다.

언급했듯이, 최적화 된 코드의 가장 큰 문제는 가능한 한 유지하기가 어렵다는 것입니다. 가능한 한 최적화 된 부분을 최적화되지 않은 부분과 분리해야합니다. 컴파일 타임 링크, 런타임 가상 함수 호출 또는 그 사이의 무언가를 통해이 작업을 수행하든 상관 없습니다. 중요한 것은 테스트를 실행할 때 현재 관심있는 모든 버전 에 대해 테스트 할 수 있기를 원한다는 것입니다.

프로덕션 코드의 기본 최적화되지 않은 버전을 사용하여 항상 코드의 의도 를 이해하는 데 사용할 수있는 방식으로 시스템을 구축 한 다음 최적화 된 버전을 포함하여 다른 최적화 된 모듈을 빌드하여 어디서나 명시 적으로 문서화하는 경향이 있습니다. 최적화 된 버전은 기준과 다릅니다. 테스트 (단위 및 통합)를 실행할 때 최적화되지 않은 버전 모든 현재 최적화 된 모듈에서 테스트를 실행합니다 .

예를 들어 고속 푸리에 변환 기능 이 있다고 가정 해 봅시다 . 에 기본 알고리즘 구현 fft.c및 테스트가있을 수 fft_tests.c있습니다.

그런 다음 Pentium이 나오고 MMX 명령어fft_mmx.c사용하여 고정 소수점 버전을 구현하기로 결정합니다 . 나중에 펜티엄 3이 나오고 에서 스트리밍 SIMD 확장 을 사용하는 버전을 추가하기로 결정 했습니다 .fft_sse.c

이제 CUDA 를 추가하려고 하므로을 추가 fft_cuda.c하지만 몇 년 동안 사용해온 테스트 데이터 세트를 사용하면 CUDA 버전이 SSE 버전보다 느립니다! 몇 가지 분석을 수행하고 100 배 더 큰 데이터 세트를 추가하면 예상 속도가 향상되지만 CUDA 버전을 사용하기위한 설정 시간이 중요하고 작은 데이터 세트를 사용하면 데이터 세트를 사용해야합니다. 설정 비용이없는 알고리즘.

이러한 각 경우에 동일한 알고리즘을 구현하는 경우 모두 동일한 방식으로 작동해야하지만 서로 다른 아키텍처에서 전혀 다른 효율성과 속도로 실행됩니다 (모두 실행될 경우). 그러나 코드 관점에서 소스 파일 쌍을 비교하여 동일한 인터페이스가 다른 방식으로 구현 된 이유를 찾을 수 있으며 일반적으로 가장 쉬운 방법은 최적화되지 않은 원래 버전을 다시 참조하는 것입니다.

최적화되지 않은 알고리즘을 구현하는 기본 클래스와 파생 클래스가 다른 최적화를 구현하는 OOP 구현도 마찬가지입니다.

중요한 것은 같은 일 유지하는 것입니다 동일을 너무 것을, 차이가 분명하다 .


7

특히 비디오 및 이미지 처리의 예를 들었으므로 코드를 동일한 버전의 일부로 유지하지만 상황에 따라 활성 또는 비활성 상태로 유지할 수 있습니다.

당신이 언급하지 않은 동안, 나는 C여기서 가정하고 있습니다.

C코드 에서 가장 간단한 방법 은 최적화를 수행하는 것입니다 (또한 이식성을 만들려고 할 때도 적용됨).

 
#ifdef OPTIMIZATION_XYZ_ENABLE 
   // your optimzied code here... 
#else  
   // your basic code here...

#define OPTIMIZATION_XYZ_ENABLEMakefile에서 컴파일하는 동안 활성화하면 모든 것이 적절하게 작동합니다.

일반적으로 함수 중간에 몇 줄의 코드를 자르면 너무 많은 함수가 최적화 될 때 지저분해질 수 있습니다. 따라서이 경우 특정 기능을 수행하기 위해 다른 기능 포인터 를 정의 합니다.

메인 코드는 항상 다음과 같은 함수 포인터를 통해 실행됩니다.


   codec->computed_idct(blocks); 

그러나 함수 포인터는 예제 유형에 따라 정의됩니다 (예 : 여기서 idct 함수는 다른 CPU 아키텍처에 최적화되어 있습니다.



if(OPTIMIZE_X86) {
  codec->computed_idct = compute_idct_x86; 
}
else if(OPTIMZE_ARM) {
  codec->computed_idct = compute_idct_ARM;
}
else {
  codec->computed_idct = compute_idct_C; 
}

당신이 볼 수 libjpeg의 코드와 libmpeg2의 코드를하고있을 수 는 FFmpeg 이러한 기술에 대한.


6

연구원으로서 나는 "병목 현상"코드를 작성했습니다. 그러나 일단 생산에 들어가면 제품에 제품을 통합하고 후속 지원을 제공 할 책임은 개발자에게 있습니다. 상상할 수 있듯이 프로그램의 운영 방식과 방법을 명확하게 전달하는 것이 무엇보다 중요합니다.

이 단계를 성공적으로 완료하는 데 3 가지 필수 요소가 있음을 발견했습니다.

  1. 사용 된 알고리즘은 반드시 명확해야합니다.
  2. 모든 구현 라인의 목적은 분명해야합니다.
  3. 예상 결과와의 편차는 가능한 빨리 식별해야합니다.

첫 번째 단계에서는 항상 알고리즘을 문서화 하는 간단한 백서 를 작성합니다. 여기서 목표는 실제로 다른 사람이 백서를 사용하여 처음부터이를 구현할 수 있도록 작성하는 것입니다. 잘 알려진 공개 알고리즘 인 경우 참조를 제공하고 주요 방정식을 반복하기에 충분합니다. 독창적 인 작품이라면 좀 더 명확해야합니다. 이것은 당신을 말할 것이다 어떤 코드가있다 할 예정 .

개발에 전달 된 실제 구현은 모든 미묘한 부분이 명시 적으로 표현되도록 문서화해야합니다. 교착 상태를 피하기 위해 특정 순서로 잠금을 획득 한 경우 주석을 추가하십시오. 캐시 일관성 문제로 인해 행렬의 행이 아닌 열을 반복하는 경우 주석을 추가하십시오. 약간 영리한 일을한다면 의견을 말하십시오. 백서를 보증 할 수 있고 코드가 VCS 또는 유사한 시스템을 통해 분리되지 않는 경우 백서를 다시 참조 할 수 있습니다. 결과는 쉽게 50 % 이상의 주석 일 수 있습니다. 그 괜찮아요. 이것은 코드가 그 역할을하는지 알려줄 것 입니다.

마지막으로, 변경에 대한 정확성을 보장 할 수 있어야합니다. 다행스럽게도 자동화 된 테스트지속적인 통합 플랫폼 에서 편리한 도구입니다 . 이것들은 코드가 실제로 무엇을 하고 있는지 알려줄 것 입니다.

나의 가장 진지한 추천은 어떤 단계에서도 빠지지 않는 것입니다. 나중에 필요합니다.)


귀하의 포괄적 인 답변에 감사드립니다. 나는 당신의 모든 요점에 동의합니다. 자동화 된 테스트의 관점에서, 나는 고정 소수점 산술 및 SIMD 코드의 숫자 범위를 적절하게 다루는 것이 어렵다는 것을 알았습니다. 주석에만 언급 된 전제 조건 (강화 코드없이)이 항상 충족되는 것은 아닙니다.
rwong

아직 귀하의 답변을 받아들이지 않은 이유는 "짧은 백서"의 의미와이를 작성하기 위해 어떤 노력을 기울여야하는지에 대한 자세한 지침이 필요하기 때문입니다. 일부 산업에서는 이것이 주요 비즈니스 라인의 일부이지만 다른 산업에서는 비용을 고려해야하고 합법적으로 이용 가능한 지름길을 취해야합니다.
rwong

우선, 자동화 된 테스트, 부동 소수점 산술 및 병렬 코드에 대한 어려움을 느낍니다. 모든 경우에 유효한 솔루션이없는 것이 걱정됩니다. 일반적으로 나는 상당히 자유로운 관용으로 일하지만, 당신의 산업에서는 불가능할 수도 있습니다.
drxzcl

2
실제로이 백서는 종종 "fluff"부분이없는 과학 논문의 초안처럼 보입니다 (의미없는 소개, 요약, 최소한의 결론 / 토론 및이를 이해하는 데 필요한 참조 만). 알고리즘 개발 및 / 또는 알고리즘 선택에 대한 보고서 및 보고서의 일부로 논문을 작성하는 것을 봅니다. 이 알고리즘 (스펙트럼 FFT)을 구현하기로 선택했습니다. 정확히 무엇입니까? 왜이 것을 다른 것보다 선택 했습니까? 병렬화 특성은 무엇입니까? 노력은 선택 / 개발 작업에 비례해야합니다.
drxzcl

5

나는 코드의 포괄적 인 주석을 통해 각 중요한 코드 블록이 미리 설명 주석을 달는 지점까지 가장 잘 해결 될 것이라고 생각합니다.

주석에는 사양 또는 하드웨어 참조 자료에 대한 인용이 포함되어야합니다.

적절한 경우 산업 차원의 용어 및 알고리즘 이름을 사용하십시오. 예를 들어 '아키텍처 X는 정렬되지 않은 읽기에 대한 CPU 트랩을 생성하므로이 Duff의 장치는 다음 정렬 경계를 채 웁니다'.

나는 무슨 일이 일어나고 있는지 오해하지 않도록 얼굴 내 변수 이름을 사용합니다. 헝가리어는 아니지만 두 개의 수직 픽셀 사이의 거리를 바이트 단위로 나타내는 '스트라이드'와 같은 것들.

또한이 다이어그램에는 사람이 읽을 수있는 짧고 사람이 읽을 수있는 높은 수준의 다이어그램과 블록 디자인이있는 문서가 포함되어 있습니다.


1
동일한 프로젝트에서 하나의 일에 대해 일관된 용어를 사용하면 (예 : 유사한 의미의 용어 (예 : "단계", "정렬")에 대해 "스트라이드"를 사용) 도움이됩니다. 여러 프로젝트의 코드베이스를 하나의 프로젝트에 통합 할 때는 다소 어렵습니다.
rwong
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.