Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Profilage de DataStore

Le profileur de DataStore vous aide à mesurer le temps d’exécution et à identifier les goulots d’étranglement en matière de performances.

Démarrage rapide

from chdb import datastore as pd
from chdb.datastore.config import config, get_profiler

# Enable profiling
config.enable_profiling()

# Run your operations
ds = pd.read_csv("large_data.csv")
result = (ds
    .filter(ds['amount'] > 100)
    .groupby('category')
    .agg({'amount': 'sum'})
    .sort('sum', ascending=False)
    .head(10)
    .to_df()
)

# View report
profiler = get_profiler()
print(profiler.report())

Activer le profilage

from chdb.datastore.config import config

# Enable profiling
config.enable_profiling()

# Disable profiling
config.disable_profiling()

# Check if profiling is enabled
print(config.profiling_enabled)  # True or False

API du profileur

Obtenir le profileur

from chdb.datastore.config import get_profiler

profiler = get_profiler()

report()

Affiche un rapport de performances.

profiler.report(min_duration_ms=0.1)

Paramètres :

Paramètre Type Défaut Description
min_duration_ms float 0.1 Affiche uniquement les étapes d’une durée >= à cette valeur

Exemple de sortie :

======================================================================
EXECUTION PROFILE
======================================================================
   45.79ms (100.0%) Total Execution
     23.25ms ( 50.8%) Query Planning [ops_count=2]
     22.29ms ( 48.7%) SQL Segment 1 [ops=2]
       20.48ms ( 91.9%) SQL Execution
        1.74ms (  7.8%) Result to DataFrame
----------------------------------------------------------------------
      TOTAL:    45.79ms
======================================================================

Le rapport affiche :

  • La durée en millisecondes pour chaque étape
  • Le pourcentage du temps du parent par rapport au temps total
  • L’imbrication hiérarchique des opérations
  • Les métadonnées de chaque étape (par exemple, ops_count, ops)

step()

Mesurez manuellement le temps d’exécution d’un bloc de code.

with profiler.step("custom_operation"):
    # Your code here
    expensive_operation()

clear()

Supprime toutes les données de profilage.

profiler.clear()

summary()

Renvoie un dictionnaire associant les noms des étapes à leur durée (ms).

summary = profiler.summary()
for name, duration in summary.items():
    print(f"{name}: {duration:.2f}ms")

Exemple de sortie :

Total Execution: 45.79ms
Total Execution.Cache Check: 0.00ms
Total Execution.Query Planning: 23.25ms
Total Execution.SQL Segment 1: 22.29ms
Total Execution.SQL Segment 1.SQL Execution: 20.48ms
Total Execution.SQL Segment 1.Result to DataFrame: 1.74ms

Comprendre le rapport

Nom des étapes

Nom de l’étape Description
Total Execution Temps d’exécution total
Query Planning Temps consacré à la planification de la requête
SQL Segment N Exécution du segment SQL N
SQL Execution Exécution réelle de la requête SQL
Result to DataFrame Conversion des résultats en DataFrame pandas
Cache Check Vérification du cache de requêtes
Cache Write Écriture des résultats dans le cache

Durée

  • Étapes de planification (Planification de la requête) : généralement rapides
  • Étapes d’exécution (Exécution SQL) : c’est là que s’effectue le travail réel
  • Étapes de transfert (Résultat vers un DataFrame) : conversion des données en DataFrame pandas

Identifier les goulots d’étranglement

======================================================================
EXECUTION PROFILE
======================================================================
  200.50ms (100.0%) Total Execution
    10.25ms (  5.1%) Query Planning [ops_count=4]
   190.00ms ( 94.8%) SQL Segment 1 [ops=4]
     185.00ms ( 97.4%) SQL Execution    <- Main bottleneck
       5.00ms (  2.6%) Result to DataFrame
----------------------------------------------------------------------
      TOTAL:   200.50ms
======================================================================

Modèles de profilage

Profiler une seule requête

config.enable_profiling()
profiler = get_profiler()
profiler.clear()  # Clear previous data

# Run query
result = ds.filter(...).groupby(...).agg(...).to_df()

# View this query's profile
print(profiler.report())

Profiler plusieurs requêtes

config.enable_profiling()
profiler = get_profiler()
profiler.clear()

# Query 1
with profiler.step("Query 1"):
    result1 = query1.to_df()

# Query 2
with profiler.step("Query 2"):
    result2 = query2.to_df()

print(profiler.report())

Comparer les approches

profiler = get_profiler()

# Approach 1: Filter then groupby
profiler.clear()
with profiler.step("filter_then_groupby"):
    result1 = ds.filter(ds['x'] > 10).groupby('y').sum().to_df()
summary1 = profiler.summary()
time1 = summary1.get('filter_then_groupby', 0)

# Approach 2: Groupby then filter
profiler.clear()
with profiler.step("groupby_then_filter"):
    result2 = ds.groupby('y').sum().filter(ds['x'] > 10).to_df()
summary2 = profiler.summary()
time2 = summary2.get('groupby_then_filter', 0)

print(f"Approach 1: {time1:.2f}ms")
print(f"Approach 2: {time2:.2f}ms")
print(f"Winner: {'Approach 1' if time1 < time2 else 'Approach 2'}")

Conseils d’optimisation

1. Vérifiez le temps d’exécution SQL

Si SQL execution est le goulot d’étranglement :

  • Ajoutez davantage de filtres pour réduire le volume de données
  • Utilisez Parquet plutôt que CSV
  • Vérifiez la présence d’index adaptés (pour les sources de type base de données)

2. Vérifiez le temps d’E/S

Si read_csv ou read_parquet constitue le goulot d’étranglement :

  • Utilisez Parquet (format colonnaire, compressé)
  • Lisez uniquement les colonnes nécessaires
  • Filtrez à la source si possible

3. Vérifier le transfert des données

Si to_df est lent :

  • Le jeu de résultats est peut-être trop volumineux
  • Ajoutez davantage de filtres ou réduisez la limite
  • Utilisez head() pour un aperçu

4. Comparer les moteurs

from chdb.datastore.config import config

# Profile with chdb
config.use_chdb()
profiler.clear()
result_chdb = query.to_df()
time_chdb = profiler.total_duration_ms

# Profile with pandas
config.use_pandas()
profiler.clear()
result_pandas = query.to_df()
time_pandas = profiler.total_duration_ms

print(f"chdb: {time_chdb:.2f}ms")
print(f"pandas: {time_pandas:.2f}ms")

Bonnes pratiques

1. Profiler avant d’optimiser

# Don't guess - measure!
config.enable_profiling()
result = your_query.to_df()
print(get_profiler().report())

2. Nettoyer entre les tests

profiler.clear()  # Clear previous data
# Run test
print(profiler.report())

3. Utilisez min_duration_ms pour mieux cibler

# Only show operations >= 100ms
profiler.report(min_duration_ms=100)

4. Profilez des données représentatives

# Profile with real-world data sizes
# Small test data may not show real bottlenecks

5. Désactiver en production

# Development
config.enable_profiling()

# Production
config.set_profiling_enabled(False)  # Avoid overhead

Exemple : session de profilage complète

from chdb import datastore as pd
from chdb.datastore.config import config, get_profiler

# Setup
config.enable_profiling()
config.enable_debug()  # Also see what's happening
profiler = get_profiler()

# Load data
profiler.clear()
print("=== Loading Data ===")
ds = pd.read_csv("sales_2024.csv")  # 10M rows
print(profiler.report())

# Query 1: Simple filter
profiler.clear()
print("\n=== Query 1: Simple Filter ===")
result1 = ds.filter(ds['amount'] > 1000).to_df()
print(profiler.report())

# Query 2: Complex aggregation
profiler.clear()
print("\n=== Query 2: Complex Aggregation ===")
result2 = (ds
    .filter(ds['amount'] > 100)
    .groupby('region', 'category')
    .agg({
        'amount': ['sum', 'mean', 'count'],
        'quantity': 'sum'
    })
    .sort('sum', ascending=False)
    .head(20)
    .to_df()
)
print(profiler.report())

# Summary
print("\n=== Summary ===")
print(f"Query 1: {len(result1)} rows")
print(f"Query 2: {len(result2)} rows")
Navigation