BigData/Spark & Spark Tuning
[Spark] 프로그래밍 언어 별 Spark 성능, 속도 차이 (UDF와 직렬화)
스파이디웹
2025. 1. 10. 17:30
728x90
Spark에서는 지원할 수 있는 언어가 5(7)가지가 있습니다.
- Scala
- Python
- Java
- R
- SQL
- GO (4.x version 부터 지원)
- Rush(4.x version 부터 지원)
관련하여 속도 차이는 어떤지 비교하고 왜 속도 차이가 나는지 정리해보겠습니다.
1. Catalyst Optimizer와 Tungsten 엔진 사용
- Spark는 Catalyst Optimizer라는 내부 최적화 엔진을 사용하여, DataFrame, Dataset, 그리고 Spark SQL 쿼리를 모두 동일한 논리적 실행 계획(Logical Plan)으로 변환
- Catalyst Optimizer는 쿼리 최적화, 필터 푸시다운, 프로젝트 제거, 조인 최적화 등을 수행
- 최적화된 논리적 계획은물리적 실행 계획(Physical Plan)으로 변환되며, 이 계획은 Tungsten 엔진에서 실행
- Tungsten 엔진은 바이트코드 생성 및 메모리 관리 최적화를 통해 모든 연산을 고성능으로 처리
Tunsten 엔진이란?
Apache Spark에서 CPU와 메모리 효율성을 극대화하기 위해 도입된 물리적 실행 엔진
Spark 1.5 버전에서 처음 등장했으며, Spark의 성능을 크게 향상시키기 위해 JVM의 한계를 극복하고 Spark의 데이터 처리 파이프라인을 최적화하는 데 초점이 맞춰져 있습니다.
[Tungsten 엔진의 주요 목표]
1. CPU 효율 극대화: 현대적인 CPU 아키텍처를 최대한 활용하여 Spark 작업의 속도를 높이는 것.
2. 메모리 및 네트워크 효율 개선: 메모리 관리 및 데이터 직렬화를 최적화하여 Spark 작업 중 메모리 사용량을 줄이고 네트워크 성능을 향상.GC(가비지 컬렉션)
3. 최소화: JVM의 가비지 컬렉션 문제를 줄이기 위해, 객체 대신 원시 데이터 형식(primitive data types)을 사용하는 방식을 도입.
[Tungsten 엔진의 주요 기능]
1. 바이너리 처리(Binary Processing):데이터를 JVM 객체(Object) 대신 메모리에 직접 저장된 바이너리 포맷으로 관리합니다.이로 인해 메모리 사용량이 줄어들고, CPU 캐시 효율이 개선되어 성능이 향상됩니다.
2. 바이트코드 생성(Code Generation):Tungsten 엔진은 실행 시점에 쿼리를 Java 바이트코드로 컴파일합니다.반복문이나 함수 호출 등 CPU 오버헤드를 줄이고, 최적화된 실행 코드를 생성합니다.예: SQL 쿼리, UDF(사용자 정의 함수) 등이 동적으로 컴파일됨.
3. 메모리 관리 최적화:Tungsten은 JVM의 기본 메모리 관리(GC) 대신 고정 크기 메모리 블록을 직접 관리합니다.객체 생성을 줄이고, 메모리 할당과 해제를 수동으로 제어하여 가비지 컬렉션 오버헤드를 줄입니다.
4.단일 명령어 다중 데이터(SIMD) 최적화:현대 CPU의 SIMD(같은 연산을 여러 데이터에 동시에 수행) 기능을 활용하여 벡터화(vectorization)된 연산을 지원합니다.
5. 특화된 정렬과 셔플 알고리즘:Tungsten은 Spark의 정렬과 셔플 과정을 효율적으로 수행하는 알고리즘을 포함합니다.메모리 및 디스크 IO를 최소화하는 방식으로 대규모 데이터 작업 성능을 향상시킵니다.
2. 언어 독립적 표현
- DataFrame과 Dataset은 API 계층에서 차이가 있을 뿐, 실행 시에는 Spark의 내부 표현인 RDBMS 스타일의 Logical Plan으로 변환됨
- Python, Scala, Java에서 작성된 코드라도 최종적으로 동일한 Spark의 물리적 실행 계획으로 변환되기 때문에 언어 간 성능 차이가 없음
- Spark SQL 역시 SQL 문장을 Logical Plan으로 변환하므로, API 방식과 SQL 방식 간에도 차이가 없음
3. JVM 기반에서의 효율적 실행
- Spark의 실행 엔진은 기본적으로 JVM(Java Virtual Machine) 위에서 동작
- Python과 R 같은 언어는 PySpark나 SparkR를 통해 Spark의 JVM 코드와 상호작용
- 이러한 과정에서도 Catalyst Optimizer가 최적화를 수행하며, 결국 JVM에서 동일한 실행 계획이 실행되기 때문에 성능은 언어와 무관하게 동일
예외적인 경우(RDD에서의 Python, R)
Python 및 R API
- PySpark와 SparkR는 JVM기반의 언어가 아니므로 JVM과의 상호작용 때문에 약간의 오버헤드가 발생할 수 있지만, DataFrame 및 Spark SQL 연산은 대부분 Catalyst 최적화와 Tungsten 엔진으로 처리되므로 성능 차이는 미미
- 하지만 RDD level에서의 동작은 다른데, Pyspark는 Python 코드와 JVM 기반 Spark 엔진 사이에서 데이터를 직렬화(Serialization)하고 역직렬화(DeSerialization)하는 과정에 의해 IPC(Inter-Process Communication) 오버헤드가 발생
- 직렬화 시, 데이터를 JSON, Pickle, 또는 Arrow와 같은 형식으로 변환
- 데이터 변환 비용: 데이터 포맷 변경(예: JVM 객체 → JSON/Arrow)
- 네트워크 또는 IPC 비용: Python과 JVM 간 데이터 교환 시 발생
사용자 정의 함수(UDF)
- Python 및 R의 UDF는 JVM 외부에서 실행되므로 Scala/Java UDF에 비해 성능이 떨어질 수 있음
- Python의 경우 인터프리터를 통해 실행되므로 Spark의 JVM 엔진과 Python 인터프리터 간의 데이터 교환이 필요
- Python UDF는 JVM 내부 최적화(예: Catalyst Optimizer, Tungsten Engine)를 활용할 수 없고, 처리 속도가 느림
- Spark는 UDF를 실행할 때, 데이터를 JVM에서 Python 또는 R로 변환하고, 결과를 다시 JVM으로 변환해야 함, 이 과정이 매 row마다 적용
- 반면, Scala/Java UDF는 JVM 내부에서 실행되므로 이러한 변환 과정이 필요 없기 때문에, 실행 속도가 더 빠름
- PySpark의 Pandas UDF(Vectorized UDF)를 사용하게 되면 데이터 처리를 벡터화하여 Python에서 발생하는 성능 병목을 줄일 수 있음 (Python UDF 대신에 Pandas UDF를 사용하기)
Python UDF의 실행 과정 vs Scala에서의 UDF 실행 과정
Python UDF는 DataFrame에서 사용되지만, 실제 실행 과정은 내부적으로 RDD와 유사한 방식으로 처리
1) Python UDF 실행 과정
- DataFrame의 데이터가 RDD로 변환됨
- Spark는 Python UDF를 호출할 때 DataFrame의 데이터를 RDD 형태로 변환
- 내부적으로 데이터를 JVM에서 Python으로 직렬화
- RDD 데이터를 Python UDF로 전달
- RDD의 각 파티션 데이터를 Python 인터프리터로 전달
- Python 인터프리터에서 UDF를 실행해 데이터를 처리
- Python에서 처리된 결과를 JVM으로 반환
- Python UDF가 처리한 결과를 다시 직렬화하여 JVM으로 반환
- DataFrame으로 재구성
- Python UDF의 결과를 기반으로 새로운 DataFrame을 생성
2) Scala UDF 실행 과정
- Scala에서는 UDF 실행 시 RDD로 변환하지 않음
- Scala UDF는 Spark의 내부 최적화 시스템과 긴밀하게 연동되며, Catalyst Optimizer와 Tungsten 엔진의 최적화를 유지한 채 실행
결론
- 모든 언어에서의 DataFrame, Dataset, SQL의 속도는 동일함
- 파이썬이나 R을 사용해서 UDF을 정의하면,JVM 외부에서 수행되기 때문에 RDD로 한번 변환하는 작업을 거치게 되고 Tungsten 엔진의 최적화를 사용하지 못하므로 성능 저하가 생김. 즉, Scala나 Java를 사용하여 UDF를 생성하는 것이 좋음
- 파이썬에서 RDD코드를 사용했을 때 느린 이유는, 파이썬 프로세스를 오가는 많은 데이터를 직렬화 하는 과정에서 데이터 변환, 네트워크 또는 IPC 비용에 의한 성능 저하가 생길 수 있음
728x90