Dask 모범 사례 — 시작은 작게, 성능 지키기

Dask 모범 사례

Dask API는 시작하기 쉽지만, 잘 쓰려면 경험이 필요해요. 이 페이지는 모든 Dask API에 공통으로 적용되는 모범 사례와 자주 겪는 문제의 해결책을 정리해요.

출처: https://docs.dask.org/en/stable/best-practices.html

작게 시작하기

병렬화는 복잡성과 오버헤드를 가져와요. 큰 문제에만 필요할 때가 많아서, Dask를 도입하기 전에 대안을 먼저 고려해요.

  • 더 나은 알고리즘·자료구조: NumPy, pandas, scikit-learn에 더 빠른 함수가 있는지 확인
  • 더 나은 파일 포맷: 랜덤 액세스를 지원하는 효율적 이진 포맷(Parquet, ORC, Zarr, HDF5, GeoTIFF)
  • 컴파일 코드: Numba·Cython으로 컴파일하면 병렬화가 불필요해질 수 있음
  • 샘플링: 데이터가 많아도 일부만 써서 같은 통찰을 얻을 수 있는지
  • 프로파일링: 느린 이유를 먼저 파악

대시보드 활용

Dask 대시보드로 워커 상태를 이해하면 효율적인 해를 찾기 쉬워져요. 병렬·분산 컴퓨팅에선 새 비용이 생기므로 기존 직관이 틀릴 수 있어요.

매우 큰 파티션 피하기

청크는 한 워커 메모리에 여러 개가 들어갈 만큼 작아야 해요. 예를 들어 100GB 램·10코어 머신이면 1GB대 청크를 고려할 수 있어요. 너무 작은 청크도 피해야 해요.

  • Dask DataFrame: 파티션 크기
  • Dask Array: 청크 크기

매우 큰 그래프 피하기

모든 연산은 파티션 수만큼 태스크를 만들 수 있어요. 태스크 하나당 200us~1ms 오버헤드가 있으므로, 수백만 태스크가 되면 스케줄러를 압도할 수 있어요. 해결책:

  • 청크 크기 증가: 1000GB 데이터를 10MB 청크로 나누면 100,000 파티션 → 1GB대로 키우면 오버헤드가 크게 줄어요
  • 연산 융합: da.map_blocks·dd.map_partitions로 복잡한 연산을 하나의 파이썬 함수로 묶기
  • 컴퓨테이션 분할: 매우 큰 워크로드는 조각으로 나눠 순차 제출

Dask로 데이터 로드하기

큰 객체를 클라이언트에서 만든 뒤 컴퓨테이션에 넣으면 네트워크로 여러 번 보내야 하고 스케줄러에 부담이 돼요. 대신 Dask가 읽도록 해요.

ddf = dd.read_parquet(...)
ddf2 = dd.read_csv(filenames)
ddf = dd.concat([ddf, ddf2])

NumPy 배열도 마찬가지:

f = h5py.File(...)
x = da.from_array(f["x"])   # Dask가 직접 읽게

compute 반복 호출 피하기

compute 호출은 완료까지 클라이언트를 블록해요. 루프 안에서 호출하면 병렬화를 막고 중간 결과 공유도 못 해요.

foo = ...
results = []
for i in range(...):
    results.append(foo.select(...))   # 여기서 compute 하지 않기
results = dask.compute(*results)

이렇게 하면 공유 부분(foo)을 한 번만 계산하고 서로 다른 select도 병렬로 돌려요.

더 알아보기