성능 가이드

Cloud TPU는 낮은 비용으로 높은 성능을 제공합니다. 애플리케이션의 Cloud TPU 구성 매개변수를 조정하고, 성능을 제한하는 병목 현상을 식별하고 해결하여 Cloud TPU 성능을 개선할 수 있습니다.

Cloud TPU 성능을 극대화하기 위해 이 가이드에서 제시하는 방법은 다음과 같습니다.

이와 더불어 다음 리소스에서 자세한 방법을 확인할 수 있습니다.

모델 처리 성능

이 섹션에서는 모델 성능을 저하시킬 수 있는 일반적인 문제와 이를 해결하는 방법을 설명합니다.

  1. 데이터 사전 처리에 너무 많은 시간이 소요됨

    TensorFlow-TPU 소프트웨어 스택을 사용하면 데이터를 TPU에 공급하기 전에 CPU에서 복잡한 데이터 사전 처리를 수행 할 수 있습니다. TPU가 너무 빠르기 때문에 입력 데이터가 매우 크거나 복잡한 방식으로 처리되면 병목 현상이 생기기도 합니다.

    Cloud TPU 프로파일링 도구는 입력 처리가 모델의 병목 현상인지 확인하는 간단한 측정 방법입니다. 이 경우 학습하는 모든 모델의 모든 에포크를 발생시키는 대신 일회성 비용으로 오프라인에서 사전 처리를 수행합니다.

  2. 분할로 인해 배치 크기가 너무 작음 (코어간 배치 분할)

    학습 배치 크기 n을 사용하는 경우 8개의 TPU 코어에서 분할되거나 샤딩됩니다. n = 128이고 TPU를 단일 P100과 비교하는 경우 TPU가 최고 FLOP의 20% 에서만 실행되는 반면 GPU는 128이 아니라 최고 FLOP의 70%에서 실행됩니다(샤딩된 TPU 배치 크기는 실제로 16 (128 / 8)). 이 예에서 TPU 배치 크기가 1024이면 TPU 메모리를 더 잘 활용할 수 있습니다.

    메모리 사용량을 최적화하려면 메모리에 맞는 가장 큰 배치 크기를 사용하세요. 각 TPU 코어는 처리에 128x128 메모리 셀 행렬을 사용합니다. 일반적으로 배치 크기는 TPU 메모리를 가장 효과적으로 사용하기 위해 128로 균등하게 나눠야 합니다.

XLA 컴파일러 성능

XLA는 머신러닝 컴파일러로서 TPU, CPU, GPU, 기타 플랫폼의 바이너리를 생성하며, 표준 텐서플로우 코드 베이스의 일부입니다. Cloud TPU용 텐서플로우 모델은 XLA 그래프로 해석되고, XLA는 다시 TPU 실행 파일로 컴파일됩니다. XLA와 텐서플로우의 연동 방식에 관한 자세한 내용은 XLA 개요를 참조하세요.

Cloud TPU 하드웨어는 CPU 및 GPU와 다릅니다. 일반적으로 CPU는 고성능 스레드가 적다는 특징이 있고, GPU는 저성능 스레드를 매우 많이 포함한다는 특징이 있습니다. 128x128 행렬 단위가 포함된 Cloud TPU는 주기당 16,000회의 오퍼레이션을 수행할 수 있는 매우 강력한 단일 스레드이거나 파이프라인 방식으로 연결된 작고 단순한 128x128 소형 스레드 중 하나라고 볼 수 있습니다. 따라서 메모리를 처리할 때 8의 배수(부동 소수점)가 선호되며 행렬 단위를 대상으로 하는 오퍼레이션의 경우에는 128의 배수가 적합합니다.

타일 생성 결과

Cloud TPU의 배열은 타일로 나누어집니다. 여기에는 차원 중 하나를 8의 배수로 패딩하고 다른 차원을 128의 배수로 패딩하는 작업이 수반됩니다. XLA는 데이터 레이아웃 변환을 수행하고 데이터는 메모리에서 하드웨어가 효율적으로 데이터를 처리할 수 있는 방식으로 배열됩니다. 이러한 변환은 휴리스틱 방식으로 이루어집니다. 대부분의 경우 효과가 있지만 컴파일러에 오류가 발생할 가능성은 항상 존재합니다. 최고의 성능을 달성하려면 다양한 모델 구성을 사용하여 시험해 보는 것이 좋습니다.

일반적인 원칙: 패딩 비용 최소화

타일로 나눈 메모리 스키마를 사용하는 경우, 결과적으로 패딩 오버헤드에 소요되는 메모리 양에 따라 메모리 사용률이 달라지게 됩니다. 따라서 가장 효율적인 방식으로 Cloud TPU를 사용하려면 타일 생성의 오버헤드를 최소화하는 차원 크기를 사용해야 합니다.

예를 들어 합성곱 결과에는 (1) 배치, (2) 출력 특징, (3) 출력 공간 차원이 포함됩니다. 배치 또는 출력 특징 차원 중 하나는 8의 배수로 패딩되고 다른 차원은 128의 배수로 패딩됩니다. 출력 공간 차원은 패딩되지 않습니다.

일반적으로 공간 차원(tf.nn.pool, tf.conv2d 등)이 있는 작업의 경우 공간 차원이 패딩되지 않습니다.

일반적인 원칙: 배치 및 특징 차원의 효율 값 정하기

배치 및 특징 차원은 패딩의 영향을 받으므로 배치 및 특징 크기를 결정할 때 주의해야 합니다. 128x128 행렬 단위를 최대로 활용하려면 배치와 특징 중 하나 이상의 값을 합당한 수준에서 되도록 크게(128 이상) 설정하세요.

대부분의 경우 Cloud TPU 행렬 단위를 모두 채우는 데 128의 배치 크기면 충분합니다. 더 큰 배치 크기를 실행하는 것도 Cloud TPU에서 효과가 있지만 128의 배수인 배치 크기를 사용하는 것이 좋습니다. 모델이 이러한 구성으로 작동하지 않는 경우 8의 배수인 배치 크기를 사용하여 패딩의 영향을 최소화하세요.

마찬가지로 XLA 레이아웃 알고리즘의 결정에 따라 8 또는 128의 배수 중 하나로 패딩된 차원에도 특징 차원이 매핑됩니다. 즉, 특징 차원이 8 또는 128 중 하나의 배수로 최소한의 공간을 소비하게 됩니다.

퓨전

퓨전(fusion)은 XLA 컴파일러가 프로그램을 최적화하기 위해 사용하는 일반적인 기술입니다. 퓨전 오퍼레이션이란 실행할 여러 개의 오퍼레이션으로 구성된 조합을 의미합니다.

예를 들어 다음과 같이 연속된 오퍼레이션을 생각해 보세요.

tmp = tf.add(x, y)
result = tf.multiply(tmp, z)

이 코드는 다음 유사 코드와 거의 동일합니다.

for (i = 0; i < element_count; i++) {
  tmp[i] = x[i] + y[i];
}

for (i = 0; i < element_count; i++) {
  result = tmp[i] * z[i];
}

퓨전을 사용하면 배열 액세스가 동시에 발생합니다.

for (i = 0; i < element_count; i++) {
  result = (x[i] + y[i]) * z[i];
}

이 예제에서는 메모리 왕복 횟수가 줄어 XLA가 'tmp'에 공간을 할당할 필요가 없었습니다.

퓨전은 중요한 최적화 방식으로, 다음과 같은 여러 측면에서 Cloud TPU에 도움이 됩니다.

  • 기본 메모리에 중간 결과를 저장할 필요성을 없애(느린 속도의 원인) 메모리 전송량을 줄일 수 있습니다.
  • 이로써 하드웨어 단위의 활용도가 더욱 높아집니다.
  • 동시에 실행해야 하는 버퍼 수가 적기 때문에 모델의 메모리 사용률을 낮출 수 있습니다.

브로드캐스팅

서로 다르지만 호환 가능한 형태의 두 텐서를 조합할 때 암시적으로 브로드캐스팅이 발생합니다.

예를 들어 tf.add(vector, matrix)에서는 벡터에 행렬 형태 브로드캐스팅을 수행해야 합니다. 오퍼레이션의 결과, 행렬과 형태가 동일해집니다. 자세한 내용은 배열 브로드캐스팅 가이드를 참조하세요.

브로드캐스트를 소비 함수와 퓨전할 수 있는 경우도 있지만 브로드캐스트를 강제로 구체화하면 결과적으로 성능이 떨어지고 메모리 압박이 높아질 수 있습니다.

다음 예에서는 벡터 추가 시 브로드캐스트가 포함되며 행렬은 argmax와 퓨전되어 구체화된 브로드캐스트로 만들 수 없습니다.

tf.argmax(tf.add(vector, zero_matrix), axis=0)

텐서플로우 함수별 권장사항

Cloud TPU에서 사용 가능한 전체 텐서플로우 오퍼레이션 목록을 참조하세요.

tf.matmul

  • 피연산자 중 하나의 결과를 전치하는 것은 실질적으로 무료입니다.
  • tf.matmul은 입력 및 출력으로의 퓨전을 지원합니다. 즉, tf.matmul의 출력에 직접적으로 적용되는 활성화 함수 또는 편향의 오버헤드가 낮습니다.

tf.nn.conv_n_d, tf.nn.depthwise_conv2d, tf.nn.separable_conv2d

  • 활성화 함수의 경우 배치 및 특징 차원을 8 또는 128의 배수로 패딩합니다.
    • 최초 XLA는 모듈의 컨볼루션을 위해 가장 일반적인 배치 차원 크기를 추적합니다. 이는 정방향 컨볼루션, 활성화 기울기 컨볼루션, 커널 기울기 컨볼루션을 구분하는 데 도움이 됩니다.
    • 가장 일반적인 배치 크기가 64 이상인 경우:
      • 정방향 및 역방향 컨볼루션의 경우 배치는 128의 배수로 패딩되고 특징은 8의 배수로 패딩됩니다.
      • 기울기 업데이트 컨볼루션의 경우 배치는 8의 배수로 패딩되고 특징은 128의 배수로 패딩됩니다.
    • 가장 일반적인 배치 크기가 64 미만인 경우:
      • 정방향 및 역방향 컨볼루션의 경우 배치는 8의 배수로 패딩되고 특징은 128의 배수로 패딩됩니다.
      • 기울기 업데이트 컨볼루션의 경우 배치는 128의 배수로 패딩되고 특징은 8의 배수로 패딩됩니다.
      • 활성화 함수를 컨볼루션에 보내기 직전에 전치하는 것은 무료입니다(전치가 입력 특징과 배치 차원만 바꾸는 경우).
  • 커널의 경우 입력 특징과 출력 특징 차원은 8 또는 128의 배수로 패딩됩니다. 이에 대한 정확한 결정은 커널의 생산 함수 및 기타 소비 함수의 영향을 받습니다.
    • 커널을 컨볼루션에 보내기 직전에 전치하는 것은 무료입니다(전치가 입력 특징과 배치 차원만 바꾸는 경우).
  • 결과적으로 배치 및 특징 차원은 8 또는 128의 배수로 패딩됩니다.
    • 컨볼루션의 결과를 전치하는 것은 무료입니다(전치가 배치 및 출력 특징 차원만 바꾸는 경우).
  • tf.nn.conv_n_d는 결과, 활성화 함수 또는 커널에 대한 퓨전을 지원합니다. 즉, 출력에 직접적으로 적용되는 활성화 함수 또는 편향의 오버헤드가 낮습니다.

tf.nn.avg_pool tf.nn.max_pool

  • 패딩 규칙 적용: 공간 차원은 배치 및 특징보다 주요합니다. 각 배치 및 특징은 8 또는 128의 배수로 패딩될 수 있습니다.
  • 일반적으로 풀링 작업의 레이아웃은 입출력 시 발생하는 컨볼루션과 일치합니다.
  • tf.nn.max _ pool의 경사 계산은 해당되는 tf.nn.avg_pool에 비해 느릴 수 있습니다. 가능하다면 최대 풀링에서 평균 풀링으로 전환하는 것을 고려해 보세요.

tf.concat, tf.slice, tf.strided_slice

  • 불필요한 슬라이스 및 연결은 피하세요. 패딩된 차원의 슬라이스 및 연결에는 훨씬 많은 비용이 소요됩니다.
    • 슬라이스 차원에 패딩 오버헤드가 없는 경우 데이터 이동이 최소화됩니다.

tf.transpose

  • tf.matmul의 피연산자 또는 그 결과를 전치하는 것은 무료입니다.
  • tf.conv_n_d의 활성화 함수를 전치하는 것은 무료입니다(배치 및 입력 특징 차원을 바꾸는 경우).
  • tf.conv_n_d의 커널을 전치하는 것은 무료입니다(입력 및 출력 특징 차원을 바꾸는 경우).
  • tf.conv_n_d의 결과를 전치하는 것은 무료입니다(배치 및 출력 특징 차원을 바꾸는 경우).

tf.batch_to_space, tf.space_to_batch, tf.space_to_depth, tf.depth_to_space

  • 패딩된 차원에서 패딩되지 않은 차원으로 데이터를 이동해야 하므로 많은 비용이 소요됩니다(그 반대의 경우도 마찬가지임).

tf.reshape

  • 패딩된 차원에서 데이터를 이동하는 경우 Cloud TPU의 구조 변경에 많은 비용이 소요됩니다.
  • 패딩 양이 많은 경우에는 호스트에서 데이터의 형태를 R1로 변경하고 기기에서 더 높은 차원 형태로 되돌리는 방법이 유용할 수 있습니다. 호스트와 기기 간의 전송을 보다 효과적으로 만들 수 있습니다.
    • 꽉 찬 매개변수를 필요에 따라 비울 수 있기 때문에 최대 메모리 사용률에 도움이 될 수 있습니다.

tf.random_uniform, tf.distributions.Bernoulli, tf.random_normal, tf.multinomial

  • 균일 또는 베르누이 분포를 위한 유사 난수 생성은 매우 빠릅니다.
  • 일반 분포의 비용이 균일 또는 베르누이 분포보다 약간 더 높습니다.
  • 범주형/다항 분포를 위한 유사 난수 생성 비용은 훨씬 높습니다.

tf.reduce_all, tf.reduce_any, tf.reduce_logsumexp, tf.reduce_max, tf.reduce_min, tf.reduce_prod, tf.reduce_sum

  • 퓨전을 통해 동일한 입력 및 출력 형태의 여러 축소를 동시에 수행할 수 있습니다.
    • 가능한 경우 순차적인 연쇄 축소를 동시에 재작성해 보세요.
  • 축소는 요소별 작업을 출력이 아닌 입력에 퓨전할 수 있도록 지원합니다. 가능한 경우 표현식을 재작성하여 퓨전을 승격하세요. 예를 들면 다음과 같습니다.

        tf.multiply(tf.reduce_sum(x), y)
    

    변경 후:

        tf.reduce_sum(tf.multiply(x, y))
    

tf.nn.batch_normalization, tf.nn.fused_batch_norm, tf.layers.batch_normalization

  • Cloud TPU 컴파일러는 퓨전된 TensorFlow의 배치 정규화 변이를 효과적으로 낮출 수 있습니다. 이러한 변이를 사용하면 다른 방법보다 훨씬 효율적일 수 있습니다.
    • tf.nn.batch_normalization보다 tf.nn.fused_batch_norm을 선호합니다.
    • tf.layers.batch_normalization의 경우 '퓨전(fused)' 인수를 true로 설정합니다.

tf.nn.depthwise_conv2d tf.nn.separable_conv2d

  • 이들 인수는 아직 완전히 최적화되지 않았으며 동급의 정규 컨볼루션에 비해 성능이 저조할 수 있습니다.