> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-detect-table-modification.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 성능 가이드

> DataStore와 pandas의 성능 최적화 팁

DataStore는 많은 작업에서 pandas보다 훨씬 뛰어난 성능을 제공합니다. 이 가이드에서는 그 이유와 워크로드를 최적화하는 방법을 설명합니다.

<div id="why-faster">
  ## DataStore가 더 빠른 이유
</div>

<div id="sql-pushdown">
  ### 1. SQL 푸시다운
</div>

연산이 데이터 소스로 푸시다운됩니다:

```python theme={null}
# pandas: 모든 데이터를 먼저 로드한 다음 메모리에서 필터링합니다
df = pd.read_csv("huge.csv")       # 10GB를 로드합니다
df = df[df['year'] == 2024]        # Python에서 필터링합니다

# DataStore: 소스에서 필터링
ds = pd.read_csv("huge.csv")       # 메타데이터만 로드
ds = ds[ds['year'] == 2024]        # SQL에서 필터링
df = ds.to_df()                    # 필터링된 데이터만 로드
```

<div id="column-pruning">
  ### 2. 컬럼 프루닝
</div>

필요한 컬럼만 읽습니다:

```python theme={null}
# DataStore: name, age 컬럼만 읽습니다
ds = pd.read_parquet("wide_table.parquet")
result = ds.select('name', 'age').to_df()

# pandas와 비교: 100개 컬럼을 모두 읽은 후 선택합니다
```

<div id="lazy-evaluation">
  ### 3. 지연 평가
</div>

여러 연산이 하나의 쿼리로 컴파일됩니다:

```python theme={null}
# DataStore: 최적화된 SQL 쿼리 하나로 실행
result = (ds
    .filter(ds['amount'] > 100)
    .groupby('region')
    .agg({'amount': 'sum'})
    .sort('sum', ascending=False)
    .head(10)
    .to_df()
)

# 변환 결과:
# SELECT region, SUM(amount) FROM data
# WHERE amount > 100
# GROUP BY region ORDER BY sum DESC LIMIT 10
```

***

<div id="benchmark">
  ## 벤치마크: DataStore와 pandas 비교
</div>

<div id="test-environment">
  ### 테스트 환경
</div>

* 데이터: 1,000만 행
* 하드웨어: 일반 노트북
* 파일 포맷: CSV

<div id="results">
  ### 결과
</div>

| 연산                | pandas (ms) | DataStore (ms) | 우위                     |
| ----------------- | ----------- | -------------- | ---------------------- |
| GroupBy 개수 계산     | 347         | 17             | **DataStore (19.93x)** |
| 결합 연산             | 1,535       | 234            | **DataStore (6.56x)**  |
| 복잡한 파이프라인         | 2,047       | 380            | **DataStore (5.39x)**  |
| 다중 필터+정렬+Head     | 1,963       | 366            | **DataStore (5.36x)**  |
| 필터+정렬+Head        | 1,537       | 350            | **DataStore (4.40x)**  |
| Head/Limit        | 166         | 45             | **DataStore (3.69x)**  |
| 매우 복잡함(10개 이상 연산) | 1,070       | 338            | **DataStore (3.17x)**  |
| GroupBy 집계        | 406         | 141            | **DataStore (2.88x)**  |
| Select+필터+정렬      | 1,217       | 443            | **DataStore (2.75x)**  |
| 필터+GroupBy+정렬     | 466         | 184            | **DataStore (2.53x)**  |
| 필터+Select+정렬      | 1,285       | 533            | **DataStore (2.41x)**  |
| 정렬(단일)            | 1,742       | 1,197          | **DataStore (1.45x)**  |
| 필터(단일)            | 276         | 526            | 유사                     |
| 정렬(다중)            | 947         | 1,477          | 유사                     |

<div id="insights">
  ### 핵심 인사이트
</div>

1. **GroupBy 작업**: DataStore가 최대 **19.93배 더 빠름**
2. **복잡한 파이프라인**: DataStore가 **5\~6배 더 빠름** (SQL 푸시다운의 이점)
3. **단순한 슬라이스 작업**: 성능이 비슷하며 차이가 미미함
4. **가장 적합한 사용 사례**: groupby/집계를 포함하는 다단계 작업
5. **zero-copy**: `to_df()`에는 데이터 변환 오버헤드가 없음

***

<div id="when-datastore-wins">
  ## DataStore가 강점을 발휘하는 경우
</div>

<div id="heavy-aggregations">
  ### 대규모 집계
</div>

```python theme={null}
# DataStore 우세: 19.93배 빠름
result = ds.groupby('category')['amount'].sum()
```

<div id="complex-pipelines">
  ### 복잡한 파이프라인
</div>

```python theme={null}
# DataStore 우수: 5-6배 빠름
result = (ds
    .filter(ds['date'] >= '2024-01-01')
    .filter(ds['amount'] > 100)
    .groupby('region')
    .agg({'amount': ['sum', 'mean', 'count']})
    .sort('sum', ascending=False)
    .head(20)
)
```

<div id="large-file-processing">
  ### 대용량 파일 처리
</div>

```python theme={null}
# DataStore: 필요한 데이터만 로드합니다
ds = pd.read_parquet("huge_file.parquet")
result = ds.filter(ds['id'] == 12345).to_df()  # 빠릅니다!
```

<div id="multiple-column-operations">
  ### 여러 컬럼에 대한 작업
</div>

```python theme={null}
# DataStore: 단일 SQL로 결합됨
ds['total'] = ds['price'] * ds['quantity']
ds['is_large'] = ds['total'] > 1000
ds = ds.filter(ds['is_large'])
```

***

<div id="when-pandas-wins">
  ## pandas와 성능이 비슷한 경우
</div>

대부분의 상황에서 DataStore는 pandas와 비슷하거나 더 나은 성능을 보입니다. 다만 다음과 같은 특정 경우에는 pandas가 약간 더 빠를 수 있습니다:

<div id="small-datasets">
  ### 소규모 데이터셋 (\<1,000행)
</div>

```python theme={null}
# 매우 작은 데이터셋에서는 둘 다 오버헤드가 거의 없습니다
# 성능 차이는 거의 없습니다
small_df = pd.DataFrame({'x': range(100)})
```

<div id="simple-slice-operations">
  ### 간단한 슬라이싱 연산
</div>

```python theme={null}
# 집계 없는 단순 슬라이스 연산
df = df[df['x'] > 10]  # pandas가 약간 더 빠름
ds = ds[ds['x'] > 10]  # DataStore와 비슷한 수준
```

<div id="custom-python-functions">
  ### 사용자 정의 Python 람다 함수
</div>

```python theme={null}
# 사용자 지정 Python 코드에는 pandas가 필요함
def complex_function(row):
    return custom_logic(row)

df['result'] = df.apply(complex_function, axis=1)
```

<Info>
  **중요**

  DataStore가 "더 느린" 경우에도 성능은 일반적으로 **pandas와 거의 비슷한 수준**이며, 실제 사용에서는 차이가 거의 없습니다. 복잡한 작업에서는 DataStore의 장점이 이러한 예외적인 경우를 훨씬 상회합니다.

  실행을 세밀하게 제어하려면 [실행 엔진 구성](/ko/products/chdb/configuration/execution-engine)을 참조하십시오.
</Info>

***

<div id="zero-copy">
  ## zero-copy DataFrame 통합
</div>

DataStore는 pandas DataFrame을 읽고 쓸 때 **zero-copy** 방식을 사용합니다. 이는 다음을 뜻합니다:

```python theme={null}
# to_df()는 데이터를 복사하지 않습니다. 즉, zero-copy 작업입니다
result = ds.filter(ds['x'] > 10).to_df()  # 데이터 변환 오버헤드 없음

# DataFrame에서 DataStore를 생성할 때도 마찬가지입니다
ds = DataStore(existing_df)  # 데이터 복사 없음
```

**주요 시사점:**

* `to_df()`는 사실상 비용이 들지 않습니다 - 직렬화나 메모리 복사가 없습니다
* pandas DataFrame에서 DataStore를 생성하는 작업은 즉시 이루어집니다
* DataStore와 pandas 뷰는 메모리를 공유합니다

***

<div id="tips">
  ## 최적화 팁
</div>

<div id="use-performance-mode">
  ### 1. 고부하 워크로드용 Performance Mode 활성화
</div>

정확한 pandas 출력 형식(행 순서, MultiIndex 컬럼, dtype 보정)이 필요하지 않은 집계 중심 워크로드라면, 최대 처리량을 위해 Performance Mode를 활성화하세요:

```python theme={null}
from chdb.datastore.config import config

config.use_performance_mode()

# 이제 모든 연산이 pandas 오버헤드 없이 SQL 우선 실행으로 처리됩니다:
# - 병렬 Parquet 읽기 (preserve_order 없음)
# - 단일 SQL 집계 (filter+groupby를 하나의 쿼리로 처리)
# - 행 순서 보존 오버헤드 없음
# - MultiIndex 없음, dtype 보정 없음
result = (ds
    .filter(ds['amount'] > 100)
    .groupby('region')
    .agg({'amount': ['sum', 'mean', 'count']})
)
```

**예상 개선 효과**: filter+groupby 워크로드에서 최대 2-8배 더 빨라지고, 대용량 Parquet 파일의 메모리 사용량이 줄어듭니다.

자세한 내용은 [Performance Mode](/ko/products/chdb/configuration/performance-mode)를 참조하십시오.

<div id="use-parquet">
  ### 2. CSV 대신 Parquet 사용하기
</div>

```python theme={null}
# CSV: 더 느리고, 파일 전체를 읽습니다
ds = pd.read_csv("data.csv")

# Parquet: 더 빠르고, 열 지향이며, 압축되어 있습니다
ds = pd.read_parquet("data.parquet")

# 한 번 변환하면 계속해서 이점을 얻습니다
df = pd.read_csv("data.csv")
df.to_parquet("data.parquet")
```

**예상되는 개선 효과**: 읽기 성능 3\~10배 향상

<div id="filter-early">
  ### 3. 가능한 한 일찍 필터링하기
</div>

```python theme={null}
# 좋음: 먼저 필터링한 후 집계
result = (ds
    .filter(ds['date'] >= '2024-01-01')  # 초기에 데이터 줄이기
    .groupby('category')['amount'].sum()
)

# 비효율적: 모든 데이터 처리
result = (ds
    .groupby('category')['amount'].sum()
    .filter(ds['sum'] > 1000)  # 필터링 시점이 너무 늦음
)
```

<div id="select-only-needed-columns">
  ### 4. 필요한 컬럼만 선택하기
</div>

```python theme={null}
# 좋음: 컬럼 프루닝
result = ds.select('name', 'amount').filter(ds['amount'] > 100)

# 덜 최적: 모든 컬럼 로드
result = ds.filter(ds['amount'] > 100)  # 모든 컬럼을 로드함
```

<div id="leverage-sql-aggregations">
  ### 5. SQL 집계 활용하기
</div>

```python theme={null}
# GroupBy에서 DataStore의 진가가 드러납니다
# 최대 20배까지 빨라집니다!
result = ds.groupby('category').agg({
    'amount': ['sum', 'mean', 'count', 'max'],
    'quantity': 'sum'
})
```

<div id="use-head">
  ### 6. 전체 쿼리 실행 대신 head() 사용
</div>

```python theme={null}
# 일부 데이터만 필요한 경우 전체 결과를 로드하지 마십시오
result = ds.filter(ds['type'] == 'A').head(100)  # LIMIT 100

# 대용량 결과에는 이 방식을 사용하지 마십시오
# result = ds.filter(ds['type'] == 'A').to_df()  # 전체 데이터를 로드함
```

<div id="batch-operations">
  ### 7. 배치 작업
</div>

```python theme={null}
# 좋은 예: 한 번만 실행
result = ds.filter(ds['x'] > 10).filter(ds['y'] < 100).to_df()

# 나쁜 예: 여러 번 실행
result1 = ds.filter(ds['x'] > 10).to_df()  # 실행
result2 = result1[result1['y'] < 100]       # 다시 실행
```

<div id="use-explain">
  ### 8. explain()을 사용해 최적화하기
</div>

```python theme={null}
# 실행 전 쿼리 계획 확인
query = ds.filter(...).groupby(...).agg(...)
query.explain()  # 연산이 푸시 다운되는지 확인

# 실행
result = query.to_df()
```

***

<div id="profiling">
  ## 워크로드 프로파일링
</div>

<div id="enable-profiling">
  ### 프로파일링 활성화
</div>

```python theme={null}
from chdb.datastore.config import config, get_profiler

config.enable_profiling()

# 워크로드 실행
result = your_pipeline()

# 보고서 보기
profiler = get_profiler()
profiler.report()
```

<div id="identify-bottlenecks">
  ### 병목 지점 파악
</div>

```text theme={null}
성능 보고서
==================
단계                    소요 시간    % 합계
----                    --------    -------
SQL execution           2.5s        62.5%     <- 병목 지점!
read_csv                1.2s        30.0%
기타                    0.3s        7.5%
```

<div id="compare-approaches">
  ### 접근 방식 비교
</div>

```python theme={null}
# 접근 방식 1 테스트
profiler.reset()
result1 = approach1()
time1 = profiler.get_steps()[-1]['duration_ms']

# 접근 방식 2 테스트
profiler.reset()
result2 = approach2()
time2 = profiler.get_steps()[-1]['duration_ms']

print(f"Approach 1: {time1:.0f}ms")
print(f"Approach 2: {time2:.0f}ms")
```

***

<div id="summary">
  ## 모범 사례 요약
</div>

| 권장 사항             | 영향                   |
| ----------------- | -------------------- |
| 성능 모드 활성화         | 집계 워크로드에서 2\~8배 더 빠름 |
| Parquet 파일 사용     | 읽기 속도 3\~10배 향상      |
| 초기에 필터링           | 데이터 처리량 감소           |
| 필요한 컬럼 선택         | I/O 및 메모리 사용량 감소     |
| GroupBy/집계 활용     | 최대 20배 더 빠름          |
| 배치 작업 활용          | 반복 실행 방지             |
| 최적화 전에 프로파일링      | 실제 병목 지점 파악          |
| explain() 사용      | 쿼리 최적화 검증            |
| 샘플 확인에는 head() 사용 | 전체 테이블 스캔 방지         |

***

<div id="decision">
  ## 빠른 결정 가이드
</div>

| 워크로드                | 권장 사항                |
| ------------------- | -------------------- |
| GroupBy/집계          | DataStore 사용         |
| 복잡한 다단계 파이프라인       | DataStore 사용         |
| 필터가 적용된 대용량 파일      | DataStore 사용         |
| 단순한 슬라이스 작업         | 둘 다 가능(성능 비슷)        |
| 사용자 정의 Python 람다 함수 | pandas를 사용하거나 나중에 변환 |
| 매우 작은 데이터(\<1,000행) | 둘 다 가능(차이 거의 없음)     |

<Tip>
  자동으로 최적의 엔진을 선택하려면 `config.set_execution_engine('auto')`를 사용하세요(기본값).
  집계 워크로드에서 최대 처리량을 얻으려면 `config.use_performance_mode()`를 사용하세요.
  자세한 내용은 [실행 엔진](/ko/products/chdb/configuration/execution-engine) 및 [성능 모드](/ko/products/chdb/configuration/performance-mode)를 참조하세요.
</Tip>
