벡터화(Vectorization)

병렬 명령어를 찾으려면 트레이서가 메모리 로드/스토어 연산에 대해 충분한 정보를 제공해야 합니다. 그것들은 메모리에서 인접해야 합니다. 이를 위한 요건은 그것들이 동일한 인덱스 변수를 사용하고 오프셋이 선형 또는 아핀 조합으로 표현될 수 있어야 한다는 것입니다.

명령줄 플래그:

  • –jit vec=1: 표시된 jitdriver들에 대해 벡터화를 켭니다(예: NumPyPy 모듈에 있는 것들).
  • –jit vec_all=1: 모든 JIT 드라이버에 대해 벡터화(vectorization)를 활성화합니다. 트레이스(trace)의 필터링 휴리스틱에 대해서는 매개변수를 참고하십시오.

기능

현재 트레이스에 병렬 연산이 포함되어 있는 경우 다음 연산들을 벡터화할 수 있습니다:

  • float32/float64: 덧셈, 뺄셈, 곱셈, 나눗셈, 부호 반전, 절댓값
  • int8/int16/int32/int64 산술 연산: 덧셈, 뺄셈, 곱셈, 부호 반전, 절댓값
  • int8/int16/int32/int64 논리: and, or, xor

축소(Reduction)

리덕션(Reduction)이 구현되어 있습니다:

  • sum, prod, any, all

상수 및 변수 확장

패킹된 산술 연산(packed arithmetic operations)은 스칼라 변수나 상수를 벡터 레지스터로 확장합니다.

가드(guard) 강화

언롤된 가드(guard)들은 산술적 수준에서 강화됩니다(GuardStrengthenOpt 참조). 결과로 나온 벡터 트레이스는 인덱스를 검사하는 가드(guard)를 하나만 가지게 됩니다.

인덱스 변수에 대한 계산 중 (병합된 로드/저장 명령어로 인해) 중복되는 계산은 제거되지 않습니다. 백엔드는 트레이스를 어셈블링하는 동안 이러한 명령어를 제거합니다.

또한 단순한 휴리스틱(–jit vec_all=1로 활성화됨)은 애플리케이션 레벨 루프에 대해 배열 경계 검사를 제거하려고 시도합니다. 이 휴리스틱은 배열 경계 검사를 식별하려고 시도하고, 루프의 상단에 전이적 가드(guard)를 추가합니다.:

label(...)
...
guard(i < n) # index guard
...
guard(i < len(a))
a = load(..., i, ...)
...
jump(...)
# becomes
guard(n < len(a))
label(...)
guard(i < n) # index guard
...
a = load(..., i, ...)
...
jump(...)

향후 작업 및 한계

  • 현재 지원되는 유일한 SIMD 명령어 아키텍처는 SSE4.1입니다.
  • int8, int64에 대한 packed mul(PMUL 참조)입니다. PCLMULQDQ를 사용하는 것도 가능할 것입니다. 일부 CPU에서만 지원되며 cpuid에서 확인해야 합니다.
  • int(8|16|32|64)에서 int(8|16)으로 타입을 변환하는 루프는 현재 SSE4.1 어셈블러 구현에서 지원되지 않습니다. 필요한 오피코드는 여러 명령어에 걸쳐 있습니다. 성능 면에서는 이러한 변환에 SIMD 명령어를 사용하는 것의 이점이 거의 없거나 전혀 없을 수 있습니다.
  • 벡터 정수 레지스터의 참/거짓을 검사하는 가드(guard)에는, xmm 레지스터 2개(하나는 0비트로 채우고 다른 하나는 모든 비트를 1로 채운 것)를 갖는 것이 편리할 것입니다. 이는 가드(guard) 검사를 위한 명령어 2개를 줄여주지만, 그 대가로 레지스터 압박(register pressure)이 높아집니다.
  • prod, sum은 64비트 데이터 타입에서만 지원됩니다.
  • 동형(isomorphic) 함수는 다음 경우들이 하나의 쌍으로 결합되는 것을 방지합니다: 1) getarrayitem_gc, getarrayitem_gc_pure 2) int_add(v,1), int_sub(v,-1)