메모리 접근 폭도 성능에 영향을 준다

coalescing을 이해한 뒤에는 한 단계 더 나아가 vectorized load/store를 보게 된다. 이는 thread 하나가 scalar 값 하나만 읽는 대신, 여러 값을 묶어 더 넓은 폭으로 읽고 쓰는 방식이다.

이 접근이 중요한 이유는 memory transaction을 더 효율적으로 맞추고 instruction 수도 줄일 수 있기 때문이다.

언제 효과가 있을까

입력 데이터가 적절히 정렬되어 있고, thread마다 연속적인 여러 값을 처리하는 구조라면 vectorized load/store는 꽤 큰 도움이 될 수 있다.

예를 들어 float4처럼 묶어서 읽으면:

  • load instruction 수를 줄일 수 있고
  • alignment가 맞을 경우 bandwidth 활용이 좋아질 수 있다

물론 모든 상황에서 좋은 것은 아니다.

alignment가 왜 중요할까

vectorized access는 데이터 정렬이 맞지 않으면 기대한 이득이 깨질 수 있다. 즉, 단순히 타입만 바꾼다고 해결되는 것이 아니라 실제 메모리 주소와 layout이 적절해야 한다.

이 때문에 vectorization은 커널 자체뿐 아니라 입력 tensor layout과 padding까지 함께 봐야 하는 문제다.

예를 들어 float4 기반 load를 하려면 보통 16-byte alignment가 맞아야 한다. 그런데 입력 tensor가 slice나 transpose 결과라면 겉으로는 같은 shape처럼 보여도 실제 메모리 시작 주소와 stride가 기대와 다를 수 있다. 이 경우 vectorized path를 쓰기 애매해지거나, 앞단에서 contiguous 변환이 추가로 필요해질 수 있다.

즉, vectorization은 단순한 kernel 내부 기법이 아니라 tensor layout 계약과도 연결된다.

instruction 수를 줄이는 효과도 있다

vectorized load/store의 장점은 bandwidth 활용뿐 아니라 instruction count 감소에도 있다. thread 하나가 scalar 네 개를 따로 읽는 대신 한 번에 읽으면 load instruction 수를 줄일 수 있고, 그 다음 계산에서도 묶음 단위 처리가 가능해진다.

물론 이것도 조건부다. tail 처리와 unpack 비용이 커지면 기대만큼 단순하지 않을 수 있다. 그래서 vectorization은 "항상 더 적은 instruction"이라기보다 "적절한 layout일 때 instruction 구조를 더 깔끔하게 만들 수 있는 방법"으로 보는 편이 정확하다.

tail 처리는 생각보다 중요한 디테일이다

vector width가 4인데 입력 길이가 항상 4의 배수인 것은 아니다. 이때 마지막 몇 개 원소를 어떻게 처리할지가 구현의 중요한 디테일이 된다.

보통은 다음 중 하나를 택한다.

  • main path는 vectorized로 가고 tail은 scalar로 처리한다
  • 입력 shape를 padding해서 tail 문제를 줄인다
  • 작은 tail 오버헤드를 감수하되 main path를 최대한 빠르게 유지한다

언제 조심해야 하나

  • input size가 vector width와 잘 안 맞을 때
  • tail 처리 분기가 늘어날 때
  • alignment가 보장되지 않을 때
  • register usage가 늘어날 때

이런 상황에서는 vectorized access가 오히려 구현 복잡도만 높이고 기대보다 큰 이득이 없을 수도 있다.

여기에 하나를 더 붙이면, global memory 쪽에서 vectorized access가 좋아 보여도 shared memory에 풀어놓는 과정이나 register 사용량까지 보면 오히려 다른 제약이 생길 수 있다. 즉, vectorization은 독립된 최적화가 아니라 전체 memory path 안에서 봐야 한다.

실무에서는 어디서 자주 보이나

vectorized access는 아래 같은 kernel에서 자주 등장한다.

  • normalization kernel
  • elementwise fused op
  • matmul 전후 packing/unpacking 성격 연산
  • Triton kernel의 block load

특히 arithmetic보다 memory movement가 중요한 kernel에서는 vectorized load/store가 생각보다 실질적인 차이를 만든다.

정리

vectorized load/store는 GPU 메모리 최적화의 다음 단계다. coalescing이 기본 정돈이라면, vectorization은 그 정돈 위에서 접근 폭까지 더 잘 맞추는 작업에 가깝다.

중요한 것은 단순히 float4를 쓰는 것이 아니라:

  • alignment가 맞는가
  • tail 처리가 과하지 않은가
  • layout이 이를 허용하는가
  • register와 shared memory 구조까지 함께 괜찮은가

를 같이 보는 것이다.

다음 글에서는 register pressure와 spill을 보면서, 계산 재사용과 occupancy 사이의 긴장을 더 자세히 본다.