SIMD 프로그래밍 코드베이스의 유지 보수 비용


14

질문:

소프트웨어 산업의 합의는 깨끗하고 간단한 코드가 코드베이스와 코드를 소유 한 조직의 장기적인 생존력에 필수적이라는 것입니다. 이러한 특성으로 인해 유지 관리 비용이 절감되고 코드 기반이 계속 될 가능성이 높아집니다.

그러나 SIMD 코드는 일반적인 응용 프로그램 코드와 다르며 SIMD 코드에 특별히 적용되는 깨끗하고 간단한 코드와 비슷한 합의가 있는지 알고 싶습니다.


내 질문에 대한 배경.

다양한 이미지 처리 및 분석 작업을 위해 많은 SIMD (단일 명령, 다중 데이터) 코드를 작성합니다. 최근에는 한 아키텍처 (SSE2)에서 다른 아키텍처 (ARM NEON)로 소수의 기능을 이식해야했습니다.

이 코드는 축소 포장 소프트웨어 용으로 작성되었으므로 MATLAB과 같은 무제한 재배포 권한이없는 독점 언어에 의존 할 수 없습니다.

일반적인 코드 구조의 예 :

  • 모든 메모리, 버퍼 및 수명 관리에 OpenCV 의 매트릭스 유형 ( Mat) 사용
  • 입력 인수의 크기 (차원)를 확인한 후 각 픽셀 행의 시작 주소에 대한 포인터가 사용됩니다.
  • 각 입력 행렬에서 픽셀 수 및 각 픽셀 행의 시작 주소는 일부 저수준 C ++ 함수로 전달됩니다.
  • 이러한 저수준 C ++ 함수는 SIMD 내장 함수 ( Intel ArchitectureARM NEON 용 )를 사용하여 원시 포인터 주소에서로드 및 저장합니다.
  • 이러한 저수준 C ++ 함수의 특징 :
    • 독점적으로 1 차원 (메모리에 연속)
    • 메모리 할당을 처리하지 않습니다.
      (임시를 포함한 모든 할당은 OpenCV 기능을 사용하는 외부 코드에 의해 처리됩니다.)
    • 기호 (내장, 변수 이름 등)의 이름 길이 범위는 대략 10-20 자이며 이는 상당히 과도합니다.
      (테크노-바블처럼 읽습니다.)
    • 컴파일러가 "단일 할당"코딩 스타일로 작성 되지 않은 코드를 올바르게 구문 분석하는 데있어 버그가 많기 때문에 SIMD 변수를 재사용하지 않는 것이 좋습니다 .
      (여러 컴파일러 버그 보고서를 제출했습니다.)

SIMD 프로그래밍의 어떤 측면으로 인해 토론이 일반적인 경우와 다릅니 까? 또는 SIMD가 다른 이유는 무엇입니까?

초기 개발 비용 측면에서

  • 좋은 성능을 가진 C ++ SIMD 코드의 초기 개발 비용은 부담없이 작성된 C ++ 코드에 비해 약 10x-100x (넓은 마진) 입니다.
  • 성능 대 읽기 가능 / 청소기 코드 선택 에 대한 답변에서 언급했듯이 ? 대부분의 코드 (일반적으로 작성된 코드 및 SIMD 코드 포함)는 처음 에는 깨끗하지도 않고 빠르지도 않습니다 .
  • 스칼라 및 SIMD 코드 모두에서 코드 성능의 혁신적인 개선은 권장되지 않으며 ( 소프트웨어 재 작업 의 일종으로 간주되기 때문에 ) 비용과 이점은 추적되지 않습니다.

성향 측면에서
(예 : 파레토 원리, 일명 80-20 규칙 )

  • 이미지 처리가 소프트웨어 시스템의 20 % 만 구성하더라도 (코드 크기 및 기능 모두), 이미지 처리는 시간이 80 % 이상 걸리는 (소비 된 CPU 시간의 백분율로 볼 때) 비교적 느립니다.
    • 이는 데이터 크기 효과 때문입니다. 일반적인 이미지 크기는 메가 바이트로 측정되는 반면 이미지가 아닌 데이터의 일반적인 크기는 킬로바이트로 측정됩니다.
  • 이미지 처리 코드 내에서 SIMD 프로그래머는 C ++ 코드에서 루프 구조를 식별하여 핫스팟을 포함하는 20 % 코드를 자동으로 인식하도록 훈련됩니다. 따라서 SIMD 프로그래머의 관점에서 볼 때 "중요한 코드"의 100 %가 성능 병목 현상입니다.
  • 이미지 처리 시스템에서 종종 여러 핫스팟이 존재하며 비슷한 시간 비율을 차지합니다. 예를 들어, 총 시간의 각각을 차지하는 5 개의 핫스팟이있을 수 있습니다 (20 %, 18 %, 16 %, 14 %, 12 %). 고성능의 이득을 얻으려면 모든 핫스팟을 SIMD로 다시 작성해야합니다.
    • 이것은 풍선 터지는 규칙 으로 요약됩니다. 풍선 은 두 번 터질 수 없습니다.
    • 풍선이 5 개라고 가정합니다. 그들을 데 시리시키는 유일한 방법은 하나씩 하나씩 터지는 것입니다.
    • 첫 번째 풍선이 터지면 나머지 4 개의 풍선이 총 실행 시간의 비율이 더 높아집니다.
    • 더 많은 이익을 얻으려면 다른 풍선을 터뜨려 야합니다.
      (이것은 80-20 최적화 규칙 에 위배됩니다 . 가장 낮은 과일의 20 %를 뽑은 후에 좋은 경제적 인 결과를 얻을 수 있습니다.)

가독성 및 유지 관리 측면에서

  • SIMD 코드는 읽기 어렵습니다.

    • 이름 지정, 캡슐화, const-correctness (및 부작용을 명백하게 함), 함수 분해 등 모든 소프트웨어 엔지니어링 모범 사례를 따르는 경우에도 마찬가지입니다.
    • 숙련 된 SIMD 프로그래머에게도 마찬가지입니다.
  • 최적의 SIMD 코드는 동등한 C ++ 프로토 타입 코드와 비교하여 매우 왜곡됩니다 ( 비고 참조) .

    • SIMD 코드를 왜곡하는 방법은 여러 가지가 있지만 10 회 중 1 회만 허용되는 빠른 결과를 얻을 수 있습니다.
    • (즉, 높은 개발 비용을 정당화하기 위해 4x-10x의 성능 향상으로 조정됩니다. 실제로는 더 높은 이득이 관찰되었습니다.)

(참고)
이것은 논문제목을 그대로 인용하여 MIT Halide 프로젝트 의 주요 논문입니다.

"이미지 처리 파이프 라인을 쉽게 최적화하기 위해 일정에서 알고리즘을 분리"

앞으로 적용 가능성 측면에서

  • SIMD 코드는 단일 아키텍처와 엄격하게 연결되어 있습니다. 각각의 새로운 아키텍처 (또는 광범위한 SIMD 레지스터)를 다시 작성해야합니다.
  • 대부분의 소프트웨어 개발과 달리 각 SIMD 코드는 일반적으로 변경되지 않는 단일 목적으로 작성됩니다.
    (다른 아키텍처로 이식하는 것을 제외하고)
  • 일부 아키텍처는 완벽한 역 호환성을 유지합니다 (Intel). 사소한 양 (ARM AArch64, 일부 대체하여 짧은 하강 vtbl하여 vtblq) 충분하지만 몇 가지 코드 컴파일 실패한다.

기술과 훈련 측면에서

  • SIMD 코드를 작성하고 유지 관리하기 위해 새로운 프로그래머를 올바르게 훈련시키기 위해 어떤 지식 전제 조건이 필요한지 명확하지 않습니다.
  • 학교에서 SIMD 프로그래밍을 배운 대학 졸업생들은 그것을 비현실적인 경력 트랙으로 멸시하고 무시하는 것처럼 보입니다.
  • 분해 판독 및 저수준 성능 프로파일 링은 고성능 SIMD 코드 작성을위한 두 가지 기본 기술로 인용됩니다. 그러나이 두 가지 기술로 프로그래머를 체계적으로 훈련시키는 방법은 불분명합니다.
  • 교과서에서 가르치는 것과 크게 다른 최신 CPU 아키텍처는 교육을 더욱 어렵게 만듭니다.

정확성 및 결함 관련 비용 측면에서

  • 단일 SIMD 처리 기능은 실제로 다음과 같은 방법으로 정확성을 확립 할 수있을 정도로 응집력이 높습니다.
    • 공식적인 방법을 적용 (펜과 종이로)를 하고,
    • 출력 정수 범위 확인 (시제품 코드를 사용하고 런타임 외부에서 수행) .
  • 그러나 검증 프로세스는 비용이 많이 들며 (코드 검토에 100 % 시간, 프로토 타입 모델 검사에 100 % 시간을 소비) SIMD 코드의 기존 개발 비용이 3 배가됩니다.
  • 버그가 어떻게 든이 검증 프로세스를 거치게되면, 결함이 의심되는 기능을 교체 (다시 쓰기)하는 것 외에는 "수리"(수정)하는 것이 거의 불가능합니다.
  • SIMD 코드는 C ++ 컴파일러 (코드 생성기 최적화)의 결함으로 인해 어려움을 겪고 있습니다.
    • C ++ 표현식 템플릿을 사용하여 생성 된 SIMD 코드 는 컴파일러의 결함으로 인해 크게 어려움을 겪습니다.

혁신적인 혁신의 관점에서

  • 학계에서 많은 솔루션이 제안되었지만 상업적으로 널리 사용되는 솔루션은 거의 없습니다.

    • MIT 할로겐
    • 스탠포드 암실
    • NT2 (Numerical Template Toolbox) 및 관련 Boost.SIMD
  • 널리 사용되는 라이브러리는 SIMD를 많이 사용하지 않는 것 같습니다.

    • 오픈 소스 라이브러리는 SIMD에 미지근한 것 같습니다.
      • 최근 버전 2.4.9부터 많은 OpenCV API 함수를 프로파일 링 한 후 이것을 직접 관찰했습니다.
      • 필자가 프로파일 링 한 다른 많은 이미지 처리 라이브러리도 SIMD를 많이 사용하지 않거나 실제 핫스팟을 그리워합니다.
    • 상업용 라이브러리는 SIMD를 완전히 피하는 것 같습니다.
      • 경우에 따라 이미지 처리 라이브러리가 이전 버전의 SIMD 최적화 코드를 이후 버전의 비 SIMD 코드로 되돌려 심각한 성능 저하를 초래하는 이미지 처리 라이브러리를 보았습니다.
        공급 업체의 답변은 컴파일러 버그를 피해야한다는 것입니다.

이 프로그래머의 질문 : 지연 시간이 짧은 코드가 때때로 "못생긴"상태 여야합니까? 관련이 있으며 이전에 몇 년 전에 나의 견해를 설명하기 위해 그 질문에 대한 답변을 썼습니다.

그러나 그 대답은 "조기 최적화"관점, 즉 다음과 같은 관점에 대한 "유희"입니다.

  • 모든 최적화는 정의에 따라 조기에 (또는 본질적으로 단기적으로 )
  • 장기적인 이점이있는 유일한 최적화는 단순성입니다.

그러나 이러한 관점은이 ACM 기사 에서 다뤄진다 .


: 그 리드 나 모두 물어
SIMD 코드는 일반적인 애플리케이션 코드와 다른, 나는 SIMD 코드 깨끗하고 간단한 코드의 가치에 대한 유사한 업계의 합의가 있는지 알고 싶습니다.


2
성능 요구 사항이 있습니까? SIMD를 사용하지 않고도 성능 요구 사항을 충족 할 수 있습니까? 그렇지 않다면 질문은 헛소리입니다.
Charles E. Grant

4
이것은 질문에 비해 너무 길다. 그 중 상당 부분이 효과적으로 질문에 대한 답변을 시도하고 있고 심지어 답변을 원하기 때문에 (부분적으로는 대부분의 합리적인 답변보다 훨씬 더 많은 측면을 다루고 있기 때문에) 가능성이 높다.

3
최적화 된 대안 외에도 깨끗하고 단순한 코드 (느린 개념)와 초기 개념 증명 및 이후의 문서화 목적을 갖고 싶습니다. 이렇게하면 사람들이 클린 / 심플 / 슬로우 코드를 읽을 수 있으므로 이해하기 쉽고 검증하기도합니다 (최적화 된 버전과 클린 / 심플 / 슬로우 버전을 수동 및 단위 테스트로 비교)
Brendan

2
@Brendan 나는 비슷한 프로젝트에 있었고 단순 / 느린 코드로 테스트 방식을 사용했습니다. 고려해야 할 옵션이지만 제한 사항도 있습니다. 첫째, 성능 차이는 엄청나게 나올 수 있습니다. 최적화되지 않은 코드를 사용한 테스트는 몇 시간 ... 일 동안 실행될 수 있습니다. 둘째, 이미지 처리의 경우 최적화 된 코드가 약간 다른 결과를 생성 할 때 비트 별 비교가 작동하지 않을 수 있습니다. 따라서 ef root mean square diff
gnat

2
이 질문은 도움말 센터에 설명 된 개념적 프로그래밍 문제가 아니기 때문에 주제를 벗어난 주제로 마무리하려고합니다 .
durron597

답변:


6

나는 자신을 위해 많은 SIMD 코드를 작성하지 않았지만 수십 년 전에 많은 어셈블러 코드를 작성했습니다. SIMD 내장 함수를 사용하는 AFAIK는 기본적으로 어셈블러 프로그래밍이며 "SIMD"를 "어셈블리"라는 단어로 바꾸면 전체 질문을 다시 표현할 수 있습니다. 예를 들어 이미 언급 한 사항은

  • 이 코드는 "고수준 코드"보다 10 배에서 100 배가 걸립니다

  • 그것은 특정 아키텍처에 묶여있다

  • 코드는 결코 "깨끗"하거나 리팩토링하기 쉽지 않습니다

  • 작성 및 유지 관리 전문가가 필요합니다

  • 디버깅 및 유지 관리가 어렵고 정말 발전하고 있습니다.

SIMD에 "특별한"방법은 없습니다. 이러한 요점은 모든 종류의 어셈블리 언어에 적용되며 모두 "산업 컨센서스"입니다. 또한 소프트웨어 산업의 결론은 어셈블러와 거의 동일합니다.

  • 필요하지 않은 경우에는 작성하지 마십시오. 가능하면 고급 언어를 사용하고 컴파일러가 열심히 일하도록하십시오.

  • 컴파일러가 충분하지 않은 경우 최소한 일부 라이브러리에서 "낮은 수준"부분을 캡슐화하지만 프로그램 전체에 코드를 확산시키지 마십시오.

  • "자체 문서화"어셈블러 또는 SIMD 코드를 작성하는 것은 거의 불가능하므로 많은 문서로이 균형을 맞추십시오.

물론 "클래식"어셈블리 또는 머신 코드의 상황과 실제로는 차이가 있습니다. 오늘날 현대 컴파일러는 일반적으로 수동 언어로 작성된 어셈블러 코드보다 최적화 된 고급 언어의 고품질 머신 코드를 생성합니다. 오늘날 널리 사용되는 SIMD 아키텍처의 경우 사용 가능한 컴파일러의 품질은 AFAIK보다 훨씬 낮습니다. 자동 벡터화는 여전히 과학 연구의 주제이므로 결코 도달하지 못할 것입니다. 예를 들어, 컴파일러와 사람 간의 최적화의 차이점을 설명하고 좋은 SIMD 컴파일러를 만드는 것이 매우 어렵다는 개념을 설명하는 이 기사 를 참조하십시오 .

귀하의 질문에 이미 설명했듯이 현재 최신 라이브러리에는 품질 문제가 있습니다. 따라서 IMHO가 기대할 수있는 최선의 방법은 향후 몇 년 동안 컴파일러와 라이브러리의 품질이 향상 될 것입니다. 아마도 SIMD 하드웨어가보다 "컴파일러 친화적"으로 바뀌어야 할 것입니다. 당신은 두 번 언급) 더 인기가 될 것입니다 (이미 포트란의 힘이 아니 었습니까?). Wikipedia 에 따르면 SIMD는 15 ~ 20 년 전에 "대량 제품"이되었습니다 (그리고 문서를 올바르게 해석하면 Halide는 3 세 미만입니다). 이것을 성숙화하는 데 필요한 "클래식"어셈블리 언어의 컴파일러와 비교해보십시오. 이 Wikipedia 기사 에 따르면컴파일러가 (비 병렬 기계 코드 생성에서) 인간 전문가의 성능을 초과 할 때까지 거의 30 년이 걸렸습니다 (1970 년 ~ 1990 년대 말). 따라서 SIMD 가능 컴파일러에서 10-15 년 정도 기다려야 할 수도 있습니다.


Wikipedia 기사를 읽었을 때 , 낮은 수준에서 최적화 된 코드가 "기억해야 할 수많은 기술적 세부 사항 때문에 사용하기 어려운 것으로 간주된다"는 업계 의 일반적인 의견 이 있는 것 같습니다.
gnat

@ gnat : 그렇습니다. 그러나이 답변에 이것을 추가하면 OP에 의해 이미 언급 된 12 가지 다른 말을 그의 너무 긴 질문으로 생각해야합니다.
Doc Brown

귀하의 답변에 대한 분석은 그 자체로 "과적재"위험을 초래할 수 있다는 점에 덧붙이십시오.
gnat

4

우리 조직은이 정확한 문제를 해결했습니다. 우리 제품은 비디오 공간에 있지만, 우리가 작성하는 코드의 대부분은 스틸 이미지에도 적용되는 이미지 처리입니다.

우리는 우리 자신의 컴파일러를 작성함으로써 문제를 "해결했다"(또는 "dealt with"). 처음에 들리는 것만 큼 그리 미친 것은 아닙니다. 제한된 입력 세트가 있습니다. 우리는 모든 코드가 이미지, 주로 RGBA 이미지에서 작동한다는 것을 알고 있습니다. 입력 및 출력 버퍼가 겹칠 수없는 등의 제약 조건을 설정 했으므로 포인터 앨리어싱이 없습니다. 그런 것들.

그런 다음 OpenGL Shading Language (glsl)로 코드를 작성합니다. 스칼라 코드, SSE, SSE2, SSE3, AVX, Neon 및 물론 실제 glsl로 컴파일됩니다. 새 플랫폼을 지원해야 할 경우 해당 플랫폼의 코드를 출력하도록 컴파일러를 업데이트합니다.

또한 캐시 일관성을 향상시키기 위해 이미지 타일링을 수행합니다. 그러나 이미지 처리를 작은 커널에 유지하고 포인터를 지원하지 않는 glsl을 사용하면 코드 컴파일의 복잡성을 크게 줄일 수 있습니다.

이 방법은 모든 사람을위한 것이 아니며 자체 문제가 있습니다 (예 : 컴파일러의 정확성을 보장해야 함). 그러나 그것은 우리를 위해 상당히 잘 작동했습니다.


🔥🔥 소리! 이 제품을 판매하거나 독립형으로 사용할 수 있습니까? (또한 'AVC'= AVX입니까?)
Ahmed Fasih

미안합니다. 예, AVX를 의미했습니다 (수정하겠습니다). 현재 컴파일러를 독립형 제품으로 판매하지는 않지만 향후에 발생할 수 있습니다.
user1118321

농담이 아닙니다. 정말 깔끔합니다. 내가 본 것 중 가장 가까운 것은 CUDA 컴파일러가 디버깅을 위해 CPU에서 실행되는 "직렬"프로그램을 만들 수 있었던 방법이다. 아아. 내가 생각할 수있는 다음으로 가장 가까운 것은 OpenCL입니다. OpenCL을 평가하고 GLSL-to-all 컴파일러보다 열등하다고 생각하십니까?
Ahmed Fasih

1
우리가 시작할 때 OpenCL이 존재하지 않았다고 생각합니다. (그렇다면 상당히 새롭습니다.) 실제로 방정식에 포함되지 않았습니다.
user1118321

0

고급 언어 사용을 고려하면 유지 관리 오버 헤드가 너무 많이 발생하지 않는 것 같습니다.

Vector<float> values = GetValues();
Vector<float> increment = GetIncrement();

// Perform addition as a vector operation:
List<float> result = (values + increment).ToList();

vs

List<float> values = GetValues();
List<float> increment = GetIncrement();

// Perform addition as a monadic sequence operation:
List<float> result = values.Zip(increment, (v, i) => v + i).ToList();

물론 라이브러리의 한계에 직면해야하지만 직접 관리하지는 않습니다. 유지 관리 비용과 성능 측면에서 균형이 맞을 수 있습니다.

http://blogs.msdn.com/b/dotnet/archive/2014/04/07/the-jit-finally-proposed-jit-and-simd-are-getting-married.aspx

http://blogs.msdn.com/b/dotnet/archive/2014/05/13/update-to-simd-support.aspx


내 독서에 따르면 외부 라이브러리를 사용하는 옵션은 이미 조사되어 해결되었습니다. "대부분의 상용 사용 라이브러리는 SIMD를 많이 사용하지 않는 것 같습니다 ..."
gnat

@gnat 필자는 실제로 최상위 글 머리 기호뿐만 아니라 전체 단락을 읽었으며 포스터는 일반적인 SIMD 라이브러리, 컴퓨터 비전 및 이미지 처리 라이브러리를 언급하지 않았습니다. C ++ 태그가없고 질문 제목에 C ++ 고유성이 반영되지 않았음에도 불구하고 고급 언어 응용 프로그램 분석이 완전히 누락되었다는 것은 말할 것도 없습니다. 이것은 내 질문이 일차적으로 간주되지는 않지만 사람들이 다른 옵션을 인식하게하여 가치를 추가 할 가능성이 있다고 믿게합니다.
Den

1
이해하기 위해 OP는 널리 상업적으로 사용되는 솔루션이 있는지 묻고 있습니다. 귀하의 힌트에 감사하지만 (여기서는 프로젝트에 lib를 사용할 수 있습니다) RyuJIT은 "광범위한 업계 표준"이 아닙니다.
Doc Brown

@DocBrown일지도 모르지만 그의 실제 질문은 "... SIMD 코드를위한 깨끗하고 간단한 코드의 가치에 관한 산업적 합의 ..."와 같이보다 일반적인 것으로 공식화되었다. 나는 (공식적인) 합의가 전혀 의심의 여지가 없지만, C ++처럼 어셈블리를 잊어 버리고 유지 보수 비용을 절감하는 것처럼 고급 언어는 "일반적인"코드와 SIMD 코드의 차이를 줄일 수 있다고 주장한다.
Den

-1

최근 SIMD 프로그래밍이 아닌 과거에 어셈블리 프로그래밍을 수행했습니다.

인텔과 같은 SIMD 인식 컴파일러를 사용해 보셨습니까? 가 벡터화에 가이드는 인텔 ® C ++ 컴파일러와 재미?

"풍선 팝핑 (balloon-popping)"과 같은 여러 의견은 컴파일러 사용을 제안합니다 (단일 핫스팟이없는 경우 전체적으로 혜택을 누리기 위해).


내 독서에 따르면,이 접근법은 asker에 의해 시도되었습니다, 문제의 컴파일러 버그 / 결함에 대한 언급을 참조하십시오
gnat

영업 이익은 시도 것인지 말하지 않았다 인텔 컴파일러 도의 주제이며, 이 Programmers.SE 항목을 . 대부분의 사람들은 시도하지 않았습니다. 모두를위한 것은 아닙니다. 그러나 OP의 비즈니스 / 질문에 적합 할 수 있습니다 (코딩 / 디자인 / 유지 보수 비용이 낮을수록 성능이 향상됨).
ChrisW

이 질문에서 읽은 내용은 asker가 인텔 및 기타 아키텍처의 컴파일러에 대해 알고 있음을 시사합니다. "일부 아키텍처는 완벽한 하위 호환성 (인텔)을 유지하지만 일부는 부족합니다 ..."
gnat

그 문장에서 "인텔"은 인텔-컴파일러-라이터가 아니라 인텔-칩-디자이너를 의미합니다.
ChrisW
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.