Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

パフォーマンスモード (compat_mode)

DataStoreには、出力をpandas互換向けに整形するか、Raw SQLのパフォーマンス向けに最適化するかを制御する2つの互換性モードがあります。

概要

Mode compat_mode value Description
Pandas (デフォルト) "pandas" pandas の動作との完全な互換性。行順を保持し、MultiIndex、set_index、dtype の補正、安定ソート時のタイブレーク、-If/isNaN ラッパーに対応します。
Performance "performance" SQLファーストで実行します。pandas 互換性のためのオーバーヘッドをすべて排除。最大のスループットを実現しますが、結果の構造が pandas と異なる場合があります。

パフォーマンスモードで無効化される機能

Overhead Pandas mode behavior Performance mode behavior
行順序の保持 _row_id の挿入、rowNumberInAllBlocks()__orig_row_num__ のサブクエリ 無効 — 行順は保証されません
安定ソートのタイブレーク rowNumberInAllBlocks() ASC を ORDER BY に追加 無効 — 同順位の並び順は任意になる場合があります
Parquet の preserve_order input_format_parquet_preserve_order=1 無効 — Parquet の並列読み取りが可能になります
GroupBy の自動 ORDER BY ORDER BY group_key を追加 (pandas のデフォルト sort=True) 無効 — グループは任意の順序で返されます
GroupBy の dropna WHERE WHERE key IS NOT NULL を追加 (pandas のデフォルト dropna=True) 無効 — NULL のグループも含まれます
GroupBy の set_index グループキーをインデックスとして設定 無効 — グループキーはカラムのままになります
MultiIndex カラム agg({'col': ['sum','mean']}) は MultiIndex カラムを返します 無効 — フラットなカラム名 (col_sum, col_mean) になります
-If/isNaN ラッパー skipna のために sumIf(col, NOT isNaN(col)) を使用 無効 — 単純な sum(col) (ClickHouse はネイティブで NULL をスキップ)
count に対する toInt64 pandas の int64 に合わせるために toInt64(count()) を使用 無効 — ネイティブ SQL の Dtype が返されます
全 NaN の sum に対する fillna(0) すべて NaN の sum は 0 を返す (pandas の動作) 無効 — NULL を返します
Dtype 補正 abs() の unsigned→signed など 無効 — ネイティブ SQL の Dtype
インデックスの保持 SQL 実行後に元のインデックスを復元 無効
first()/last() argMin/argMax(col, rowNumberInAllBlocks()) any(col) / anyLast(col) — 高速ですが非決定論的です
単一 SQL 集約 ColumnExpr の groupby は中間 DataFrame をマテリアライズします 遅延実行の処理チェーンに LazyGroupByAgg を挿入 — 単一の SQL クエリ

パフォーマンスモードを有効にする

configオブジェクトを使用する

from chdb.datastore.config import config

# Enable performance mode
config.use_performance_mode()

# Back to pandas compatibility
config.use_pandas_compat()

# Check current mode
print(config.compat_mode)  # 'pandas' or 'performance'

モジュールレベルの関数を使用する

from chdb.datastore.config import set_compat_mode, CompatMode, is_performance_mode

# Enable performance mode
set_compat_mode(CompatMode.PERFORMANCE)

# Check
print(is_performance_mode())  # True

# Back to default
set_compat_mode(CompatMode.PANDAS)

便利なインポートの利用

from chdb import use_performance_mode, use_pandas_compat

use_performance_mode()
# ... high-performance operations ...
use_pandas_compat()

パフォーマンスモードを使うべき場面

次のような場合は、パフォーマンスモードを使用してください。

  • 大規模なデータセット (数十万〜数百万行) を処理する場合
  • 集約処理の多いワークロード (groupby、sum、mean、count) を実行する場合
  • 行順が重要でない場合 (例: 集計結果、レポート、ダッシュボード)
  • SQL のスループットを最大化し、オーバーヘッドを最小限に抑えたい場合
  • メモリ使用量が気になる場合 (Parquet の並列読み取り、中間 DataFrame なし)

次のような場合は、pandasモードのままにしてください。

  • pandas と完全に同じ挙動 (行順、MultiIndex、dtypes) が必要な場合
  • first()/last() が実際の最初/最後の行を返すことに依存している場合
  • 行順に依存する shift()diff()cumsum() を使用する場合
  • DataStore の出力を pandas と比較するテストを作成している場合

挙動の違い

行の順序

パフォーマンスモードでは、どの操作でも行の順序は保証されません。これには、次のものが含まれます。

  • Filter の結果
  • GroupBy の集計結果
  • 明示的に sort_values() を指定しない head() / tail()
  • first() / last() の集計結果

順序どおりの結果が必要な場合は、明示的に sort_values() を追加してください。

config.use_performance_mode()

ds = pd.read_csv("data.csv")

# Unordered (fast)
result = ds.groupby("region")["revenue"].sum()

# Ordered (still fast, just adds ORDER BY)
result = ds.groupby("region")["revenue"].sum().sort_values()

GroupBy の結果

観点 Pandasモード パフォーマンスモード
グループキーの位置 インデックス (set_index経由) 通常のカラム
グループの順序 キーでソート (デフォルト) 任意の順序
NULL のグループ 除外 (デフォルトはdropna=True) 含まれる
カラムのフォーマット 複数集約では MultiIndex フラットな名前 (col_func)
first()/last() 決定論的 (行の順序) 非決定論的 (any()/anyLast())

集計

config.use_performance_mode()

# Sum of all-NaN group returns NULL (not 0)
# Count returns native uint64 (not forced int64)
# No -If wrappers: sum() instead of sumIf()
result = ds.groupby("cat")["val"].sum()

単一SQLでの実行

パフォーマンスモードでは、ColumnExpr の groupby 集計 (例: ds[condition].groupby('col')['val'].sum()) は、pandasモードで使われる 2 段階の処理ではなく、単一のSQLクエリとして実行されます。

config.use_performance_mode()

# Pandas mode: two SQL queries (filter → materialize → groupby)
# Performance mode: one SQL query (WHERE + GROUP BY in same query)
result = ds[ds["rating"] > 3.5].groupby("category")["revenue"].sum()

# Generated SQL (single query):
# SELECT category, sum(revenue) FROM data WHERE rating > 3.5 GROUP BY category

これにより、中間DataFrameを実体化する必要がなくなり、メモリ使用量と実行時間を大幅に削減できます。


実行エンジンとの比較

パフォーマンスモード (compat_mode) と実行エンジン (execution_engine) は、それぞれ独立した設定軸です。

Config Controls Values
execution_engine どのエンジンで計算を実行するか auto, chdb, pandas
compat_mode pandas 互換性のために出力を整形するかどうか pandas, performance

compat_mode='performance' を設定すると、execution_engine='chdb' も自動的に設定されます。これは、パフォーマンスモードが SQL 実行向けに設計されているためです。

from chdb.datastore.config import config

# These are independent
config.use_chdb()              # Force chDB engine, keep pandas compat
config.use_performance_mode()  # Force chDB + remove pandas overhead

パフォーマンスモードでのテスト

パフォーマンスモード向けのテストを作成する際は、結果の行順やデータ構造のフォーマットが pandas と異なる場合があります。次の方法を使用してください。

ソートして比較 (集計、フィルター)

# Sort both sides by the same columns before comparing
ds_result = ds.groupby("cat")["val"].sum()
pd_result = pd_df.groupby("cat")["val"].sum()

ds_sorted = ds_result.sort_index()
pd_sorted = pd_result.sort_index()
np.testing.assert_array_equal(ds_sorted.values, pd_sorted.values)

値範囲チェック (先頭/末尾)

# first() with any() returns an arbitrary element from the group
result = ds.groupby("cat")["val"].first()
for group_key in groups:
    assert result.loc[group_key] in group_values[group_key]

スキーマと件数 (ORDER BY なしの LIMIT)

# head() without sort_values: row set is non-deterministic
result = ds.head(5)
assert len(result) == 5
assert set(result.columns) == expected_columns

ベストプラクティス

1. スクリプトの冒頭で有効化する

from chdb.datastore.config import config

config.use_performance_mode()

# All subsequent operations benefit
ds = pd.read_parquet("data.parquet")
result = ds[ds["amount"] > 100].groupby("region")["amount"].sum()

2. 順序が重要な場合は明示的にソートを指定する

# For display or downstream processing that expects order
result = (ds
    .groupby("region")["revenue"].sum()
    .sort_values(ascending=False)
)

3. バッチ/ETLワークロードで使用する

config.use_performance_mode()

# ETL pipeline — order doesn't matter, throughput does
summary = (ds
    .filter(ds["date"] >= "2024-01-01")
    .groupby(["region", "product"])
    .agg({"revenue": "sum", "quantity": "sum", "rating": "mean"})
)
summary.to_df().to_parquet("summary.parquet")

4. セッション内でモードを切り替える

# Performance mode for heavy computation
config.use_performance_mode()
aggregated = ds.groupby("cat")["val"].sum()

# Back to pandas mode for exact-match comparison
config.use_pandas_compat()
detailed = ds[ds["val"] > 100].head(10)

Navigation