이것은 컴파일러가 최적의 코드를 생성하지 못하는 코드 부분의 속도를 높이기 위해 컴파일러 내장 함수를 사용하는 것에 관한 세 번째 실습입니다.

이 실습의 과제는 3차원 공간에 있는 일련의 위치들의 평균 위치를 구하는 것입니다:

struct Position {
    u32 x;
    u32 y;
    u32 z;
}
...
    u64 x = 0;
    u64 y = 0;
    u64 z = 0;

    for (Position pos: input) {
        x += pos.x; // extend 32 bit to 64 bit and accumulate
        y += pos.y;
        z += pos.z;
    }

현재 컴파일러는 x86에서 이 코드를 벡터화하지 못합니다. 그러나 ARM에서는 XYZ 성분을 디인터리브하는 마법 같은 LDM 명령어를 사용하여 영리한 코드를 생성합니다. 1 그렇지만 여전히 이를 더 효율적으로 벡터화할 수 있습니다.

이 루프를 수동으로 벡터화하려면 메모리 레이아웃과 요소에 적용되는 연산에 대해 생각해 보십시오.

주의 사항: 현재 벤치마크는 16k개의 위치를 처리하며, 이는 192KB의 공간을 차지합니다(12 bytes * 16K positions). 입력 크기가 커질수록 벤치마크는 점점 더 Memory Bound 상태가 되어 성능 향상 폭이 줄어듭니다.

이 실습은 Jonathan Hallström(@JonathanHallstrom)이 기여했습니다.


  1. 다음은 ARM의 LDM 명령어의 예시입니다.

    // Assume input points to an array of structures {u32 x, u32 y, u32 z}
    LD3 {V0.4S, V1.4S, V2.4S}, [input] // Load 4 positions at once (each containing xyz elements) 
    // After that:
    //  V0.4S will have four X coordinates,
    //  V1.4S will have four Y coordinates,
    //  V2.4S will have four Z coordinates,
    // Reducing vectors with such a friendly layout is much easier.
    ↩︎