Spark에서 GPU를 사용해야하는 이유는 무엇입니까?

http://www.spark.tc/gpu-acceleration-on-apache-spark-2/

 

 

(구글번역 결과)

Spark에서 GPU를 사용해야하는 이유는 무엇입니까?

컴퓨팅 업계는 더 높은 성능과 더 높은 CPU 주파수를 가진 시스템을 선택한 다음 멀티 코어 및 멀티 스레드 프로세서 시스템과의 병렬 처리를 통해 점점 더 많은 성능을 추구하는 데 계속 진화 해 왔습니다.

그러나 증가하는 작업 부하 요구는 하나의 대형 시스템으로는 충족시킬 수없는 더 많은 처리 능력을 필요로합니다. 따라서 업계는 클러스터 컴퓨팅 소프트웨어를 사용하여 많은 작은 노드에서 실행을 분할, 배포 및 동기화하는 솔루션을 수평 확장했습니다. 이는 전력 소비를 크게 증가 시키며 시스템의 노드 수에 비례하여 네트워크 오버 헤드가 계속 증가합니다. 다른 해결책은 특수한 목적의 하드웨어 가속기를 활용하여 개별 클러스터 노드에 추가적인 계산 능력을 제공하는 것입니다. 이를 위해서는 더 적은 수의 클러스터 노드가 필요하며, 이는 배치 복잡성을 낮추고 전력 소비를 줄입니다.

수평 확장 된 분산 컴퓨팅의 경우 Apache Spark ™는 실시간으로 큰 데이터 분석을 위해 클러스터 된 응용 프로그램을 개발하는 데 사용되는 가장 널리 사용되는 소프트웨어 프레임 워크입니다. 특수 목적 하드웨어 가속 측면에서 NVIDIA의 CUDA GPU는 HPC 및 기계 학습을위한 가장 일반적인 하드웨어 가속입니다.

이 기사에서는 이러한 두 가지 기술인 Apache Spark와 NVIDIA GPU를 함께 사용하여 프로그래머가 Spark에서 GPU를 쉽게 활용할 수 있도록하는 방법에 대해 설명합니다. 이 문서에서는 관객에게 익숙한 가정 점화NVIDIA CUDA .

GPU와 스파크의 통합은 무엇입니까?

특정 작업을 GPU에 로딩하지 못하게하려면 여러 단계가 필요합니다.

  • GPU를 Spark 계산 프레임 워크로 가져 오는 첫 번째 단계는 GPU가 소비 할 수있는 형식으로 데이터를 가져 오는 문제를 해결하는 것입니다. Spark의 기본 프로그래밍 추상화는 Spark이 병렬 실행을 위해 데이터를 분할하는 데 사용하는 RDD (Resilient Distributed Dataset)입니다. 설계 상 Spark는 모든 변환에 대해 행별로 개별 RDD 파티션 배열 데이터를 계산합니다. 계산을 GPU로 오프로드하고 수천 개의 GPU 스레드가 RDD 파티션 데이터에서 병렬로 작동 할 수있게하려면 행 단위가 아닌 GPU에서 모든 데이터를 동시에 사용할 수 있어야합니다. 따라서 행 형식의 RDD를 GPU의 경우 열 형식으로 변환해야하며 GPU의 경우 열 형식의 RDD를 열 형식의 RDD로 변환해야합니다. 이러한 데이터는 GPU가 Java 객체에 직접 액세스 할 수 없으므로 오프 힙 메모리에서 사용할 수 있어야합니다. 예를 들어 배열의 다른 인덱스에서 병렬로 연산을 수행하기 위해 GPU 스레드가 만들어지는 입력 및 출력 매개 변수로 배열 포인터를 받아 들일 필요가 있습니다.
  • GPU는 먼저 시스템 메모리에서 로컬 메모리로 계산할 데이터를 읽은 다음 계산 결과를 로컬 메모리에서 시스템 메모리로 다시 써야합니다. (NVLINK 1.0 지원 POWER8 시스템에서의 움직임은 PCI 익스프레스에 비해 매우 높은 대역폭에서 발생할 수있는 이러한 데이터는. 미래 POWER9 시스템에서 NVLINK 2.0 , GPU가 시스템 메모리의 모든 전체 일관된 액세스 할 수 있습니다, 또한 GPU를 가능 무엇 확대 ) Spark을 사용하면 각 계산마다 변환 된 기둥 형 RDD 데이터를 CPU에서 GPU로 또는 그 반대로 전송할 필요가 있습니다. 어떤 경우에는 Spark의 블록 관리자 캐시와 비슷한 GPU 메모리 자체에 데이터를 캐시하는 것이 좋습니다. 이것은 기계 학습에서 발견되는 것과 같은 반복 처리 알고리즘에 도움이됩니다.
  • Spark 애플리케이션은 또한 GPU 지원이 있거나없는 Java 가상 머신 (작업자 노드)이 공존하는 이기종 환경에서 작동해야합니다. GPU가 작업자 노드에 연결되어있는 경우 실행 프로그램은 GPU를 초기화하고 사용자가 제공 한 원시 GPU 커널을로드 한 다음 관련 파티셔닝 된 RDD 데이터 만 GPU 메모리에 복사 할 수 있어야합니다. 성공하면 실행 프로그램은 GPU 계산을 수행하고 GPU 메모리의 결과를 CPU 메모리로 복사해야합니다. 실행 프로그램에 GPU가없는 경우 사용자가 제공 한 대체 람다 식을 실행하고 결과를 산출해야합니다. 자세한 내용은 이후 섹션에서 논의 할 것입니다.

일반적으로 GPU는 CPU 메모리 대역폭에 비해 훨씬 낮은 대역폭을 갖는 PCIe 카드 슬롯에 기계에 장착됩니다. 데이터 전송 시간은 결코 GPU 실행에서 얻는 계산상의 이익을 초과해서는 안됩니다. 데이터가 CPU 메모리와 GPU 메모리간에 너무 자주 전송되면이 문제가 발생할 수 있으므로 GPU 가속기 사용 목적을 무시하십시오. 이 경우 다시입니다 NVLINK의 POWER 시스템의 호스트 CPU 메모리에 연결 GPU를 함께 달려 될 수있는 문제 세트를 확장하는 데 도움이됩니다.

성능에 미치는 영향을 알아보기 위해 Power8 머신 (Firestone)의 CPU 및 GPU 실행 시간을 NVidia K80 카드와 비교하여 다음 매개 변수와 함께 “Logistic Regression”을 실행했습니다.

  • N – 샘플 수 : 1 백만 레코드
  • D – 지형지 물 수 : 400

대체

대체

이 계산 시간에 대한 성능 이득이 있음을 알 수있어 ~ 2.5 배의 데이터 변환 시간 (주상 형식 행 형식을 변경하고 GPU 메모리에 데이터를 이동에 소요되는 시간)을 포함. 그런 다음 GPU로 작업을 오프 로딩 한 후 계산 코어에 대한 게인의 양은 여러 배입니다. 우리의 경우, CPU에서 최대 애플리케이션 성능을 얻으려면 160 코어를 사용했지만, GPU로 작업량을 줄이면 단지 4 코어 만 사용했습니다. 그래서 CPU 코어의 이득은 주위 ~ 40 배 .

GPUEnabler 패키지가 작업에 사용 중입니다.

GPUEnabler 패키지 (사용 가능한 여기 : https://github.com/IBMSparkGPU/GPUEnabler는 ) 사용자 플러그인 스파크 코어에 이러한 기능을 제공합니다. 사용자 응용 프로그램은 다음과 같이이 패키지를 종속성 목록에 추가하여 GPU로 작업 부하를 투명하게 분산시킬 수 있습니다.

Using SBT:  
libraryDependencies += "com.ibm" %% "gpu-enabler_2.10" % "1.0.0"  
Using Maven:  
<dependency>  
  <groupId>com.ibm</groupId> 
  <artifactId>gpu-enabler_2.10</artifactId> 
  <version>1.0.0</version>
</dependency>  

이 패키지를 사용하려면 다음과 같은 선행 조건을 충족해야합니다.

  • 7.0 이상의 CUDA를 지원하는 NVIDIA GPU 카드.
  • 플랫폼에 맞는 CUDA 드라이버 및 런타임 드라이버를 설치해야합니다.

이 패키지는 두 개의 “변압기 API를”(추가 mapExtFuncCacheGpu )와 하나의 “작업 API”( reduceExtFunc RDD 인터페이스를). GPU에서 작업을 실행하면 네이티브 코드를 작성 포함이 패키지는 스칼라 사용하여 객체에 고유 심볼 / 기능을 매핑 할 수있는 방법 제공 CUDAFunction을 .

GPUEnabler 패키지 용 GPU CUDA 커널을 작성하는 방법을 살펴 보겠습니다. GPU 커널 함수의 입 / 출력 인수는 다음 지침을 준수해야합니다.

  1. 첫 번째 인수는 항상 프로세스에 요소의 수를 유지해야한다 (예를 들어 int *numelements)
  2. 두 번째 인수 집합은 GPU 커널에 대한 입력 인수로 간주됩니다. 이 집합은 하나 이상의 인수에 사용할 수 있습니다.
  3. 세 번째 인수 세트는 GPU 커널의 반환 값을 저장하는 것입니다. 이 집합은 하나 이상의 인수에 사용할 수 있습니다.
  4. 인수의 네 번째 세트에 전달 된 입력의 자유 변수 보유 MapGPUPartitionsRDD등을 inputFreeVariables. 이 집합은 비어 있거나 인수를 취할 수 있습니다.
  5. 다섯 번째 인수 집합은 차원 배열을 포함하며 결과가 선형 형식으로 배열 된 배열 인 경우에 사용됩니다. 이 집합은 단일 차원 배열에 대해 비어 있거나 하나의 인수를 취할 수 있습니다.
  6. 인수의 여섯 번째 세트에 전달 상수 변수 보유 CUDAFunction등을 constArgs. 이 집합은 비어 있거나 인수를 취할 수 있습니다.
  7. 일곱 번째 인수는 다중 단계에서 수행되는 축소 작업에 대한 현재 스테이지 번호를 유지합니다. 이 집합은 비어 있거나 하나의 인수를 취할 수 있습니다.
  8. 여덟 번째 인수는 다중 단계에서 수행되는 축소 작업에 대한 총 스테이지 수를 유지합니다. 이 집합은 비어 있거나 하나의 인수를 취할 수 있습니다.

CUDA 프로그램은 NVIDIA에서 제공하는 nvcc 컴파일러를 사용하여 커널을 준비합니다. 커널이 준비되면 다음과 같이 원시 심볼을 등록 할 수 있습니다.

// Import needed for the Spark GPU method to be added import com.ibm.gpuenabler.CUDARDDImplicits._
import com.ibm.gpuenabler.CUDAFunction

// Load a kernel function from the GPU kernel binary 
val ptxURL = SparkGPULR.getClass.getResource("/GpuEnablerExamples.pt x")

val mapFunction = new CUDAFunction(  
    "multiplyBy2", // Native GPU function to
multiple a given no. by 2 and return the result  
    Array("this"), // Input arguments
    Array("this"), // Output arguments 
    ptxURL)

val reduceFunction = new CUDAFunction(  
    "sum", // Native GPU function
to sum the input argument and return the result  
    Array("this"), // Input arguments 
    Array("this"), // Output arguments 
    ptxURL)

우리는 일단 CUDAFunction의 개체, 우리는 하나 “를 인수로 전달할 수 mapExtFunc “또는 ” reduceExtFunc CUDAFunction 오브젝트 맵핑 기본 기능에 대응하는 기능에 의해 참조되는 데이터 파티션을 이용하여 GPU로 실행되는 있도록”API를 해당 RDD.

// 1. Apply a transformation. (Multiply all the values of the RDD by 2.)
// (Note: Conversion of row based formatting to columnar format for consumption
// by the GPU is done internally.)
// 2. Trigger a reduction action (sum up all the 
values and return the result)  
   val output = sc.parallelize(1 to n, 1) 
      .mapExtFunc((x: Int) => 2 * x, mapFunction)
      .reduceExtFunc((x: Int, y: Int) => x + y,
reduceFunction)  

이 라이브러리는 또한을 통해 출시 작업 스파크 추가 할 수 있습니다 spark-shell또는 spark-submit사용하여 --packages명령 줄 옵션을. 예를 들어, Spark shell을 시작할 때 그것을 포함시키기 위해서 :

$ bin/spark-shell --packages com.ibm:gpu- enabler_2.10:1.0.0

사용 달리 --jars, 사용 --packages이 라이브러리와 그 종속성을 클래스 패스에 추가됩니다 보장하지만합니다. --packages인수도 함께 사용될 수있다 bin/spark-submit.

GPUEnabler를 사용하는 전체 프로그램은 아래에서 찾을 수 있습니다.

https://github.com/IBMSparkGPU/GPUEnabler/blob/master/examples/src/main/scala/com/ibm/gpuenabler/GpuEnablerExample.scala

위 애플리케이션에서 사용 된 CUDA 프로그램은 다음에서 찾을 수 있습니다.

https://github.com/IBMSparkGPU/GPUEnabler/blob/master/examples/src/main/resources/SparkGPUExamples.cu

이 샘플 프로그램을 실행하려면 다음 단계를 수행하십시오.

./bin/run-example GpuEnablerExample

현재 GPU 인 에이 블러 패키지에 대한 지원은 다음과 같습니다.

  • x86_64 및 ppc64le 아키텍처 지원
  • OpenJDK 및 IBM JDK 지원
  • CUDA로 NVIDIA GPU를 지원합니다 (CUDA 7.0에서 확인).
  • CUDA 7.0 및 7.5 지원 (CUDA 6.0 및 6.5에서 작동해야 함)
  • 원시 스칼라 유형 및 RDD의 프리미티브 배열에서 스칼라 변수 지원

소스에서이 패키지를 컴파일에 대한 자세한 내용을 알고 및 예제를 사용해, 아래에 패키지와 함께 제공되는 README.md 파일하십시오 https://github.com/IBMSparkGPU/GPUEnabler을 .

위의 github URL 아래에 게시 된 소스 코드에 액세스 할 때까지 어떻게 작동하는지 계속 신비가 있습니다.

그게 전부는 아니야, 여러분 :

Google은 GPUEnabler를 지속적으로 개선하고 개선하며 귀하의 의견 및 기여를 환영합니다. 이 프로젝트에 대한 기여도를 한 Kazuaki Ishizaki와 Randy Swanberg에게도 감사드립니다.

앞으로 진행될 몇 가지 개선 사항은 다음과 같습니다.

  • DataFrames 및 DataSets 와도 작동하도록 API 활성화
  • Spark MLlib / ML 및 Graph 알고리즘의 GPU 가속 버전.
  • 주어진 Spark 운영자를위한 CUDA 코드의 동적 생성.

You May Also Like

More From Author

+ There are no comments

Add yours