Skip to content

Commit 4fe8fd7

Browse files
committed
Allow %i substitution for process ID for multi-GPU environments. Logging level above 3 now automatically include cudnn and cublas API logging
1 parent 8a42c84 commit 4fe8fd7

File tree

1 file changed

+80
-1
lines changed

1 file changed

+80
-1
lines changed

flashinfer/api_logging.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,58 @@
2525
import torch
2626

2727

28+
# Helper function to substitute %i with process ID in file paths
29+
def _substitute_process_id(path: str) -> str:
30+
"""
31+
Replace %i with the current process ID in a path.
32+
33+
This is useful for multi-process/multi-GPU environments where each process
34+
needs its own log file.
35+
36+
Example: "flashinfer_log_%i.txt" -> "flashinfer_log_12345.txt"
37+
"""
38+
if "%i" in path:
39+
return path.replace("%i", str(os.getpid()))
40+
return path
41+
42+
2843
# Read environment variables once at module load time
2944
_API_LOG_LEVEL = int(os.environ.get("FLASHINFER_LOGLEVEL_DBG", "0"))
30-
_API_LOG_DEST = os.environ.get("FLASHINFER_LOGDEST_DBG", "stdout")
45+
_API_LOG_DEST = _substitute_process_id(
46+
os.environ.get("FLASHINFER_LOGDEST_DBG", "stdout")
47+
)
48+
49+
# Enable cuDNN, cuBLAS, and cuBLASLt API logging when FlashInfer logging level >= 3
50+
# Only override if the user hasn't already configured the logging switch
51+
# If the switch is not set, we override both the switch and destination as a bundle
52+
if _API_LOG_LEVEL >= 3:
53+
# cuBLAS logging: Check switch, set both switch and destination
54+
if "CUBLAS_LOGINFO_DBG" not in os.environ:
55+
os.environ["CUBLAS_LOGINFO_DBG"] = "1"
56+
os.environ["CUBLAS_LOGDEST_DBG"] = _substitute_process_id(
57+
"flashinfer_cublas_log_%i.txt"
58+
)
59+
60+
# cuBLASLt logging: Check switch, set both switch and destination
61+
if "CUBLASLT_LOG_LEVEL" not in os.environ:
62+
os.environ["CUBLASLT_LOG_LEVEL"] = "2"
63+
os.environ["CUBLASLT_LOG_FILE"] = _substitute_process_id(
64+
"flashinfer_cublaslt_log_%i.txt"
65+
)
66+
67+
# cuDNN backend logging: Check switch, set both switch and destination
68+
if "CUDNN_LOGLEVEL_DBG" not in os.environ:
69+
os.environ["CUDNN_LOGLEVEL_DBG"] = "2.5"
70+
os.environ["CUDNN_LOGDEST_DBG"] = _substitute_process_id(
71+
"flashinfer_cudnn_backend_log_%i.txt"
72+
)
73+
74+
# cuDNN frontend logging: Check switch, set both switch and destination
75+
if "CUDNN_FRONTEND_LOG_INFO" not in os.environ:
76+
os.environ["CUDNN_FRONTEND_LOG_INFO"] = "1"
77+
os.environ["CUDNN_FRONTEND_LOG_FILE"] = _substitute_process_id(
78+
"flashinfer_cudnn_frontend_log_%i.txt"
79+
)
3180

3281
# Create logger using Python's logging library
3382
_logger = logging.getLogger("flashinfer.api")
@@ -129,6 +178,28 @@ def _log_system_info():
129178
# PyTorch version
130179
lines.append(f"PyTorch version: {torch.__version__}")
131180

181+
# cuDNN/cuBLAS/cuBLASLt logging status
182+
if _API_LOG_LEVEL >= 3:
183+
lines.append("")
184+
lines.append("cuDNN/cuBLAS/cuBLASLt Logging: Enabled (Level 3)")
185+
cublas_info = os.environ.get("CUBLAS_LOGINFO_DBG", "not set")
186+
cublas_dest = os.environ.get("CUBLAS_LOGDEST_DBG", "not set")
187+
cublaslt_level = os.environ.get("CUBLASLT_LOG_LEVEL", "not set")
188+
cublaslt_file = os.environ.get("CUBLASLT_LOG_FILE", "not set")
189+
cudnn_level = os.environ.get("CUDNN_LOGLEVEL_DBG", "not set")
190+
cudnn_dest = os.environ.get("CUDNN_LOGDEST_DBG", "not set")
191+
cudnn_fe_info = os.environ.get("CUDNN_FRONTEND_LOG_INFO", "not set")
192+
cudnn_fe_file = os.environ.get("CUDNN_FRONTEND_LOG_FILE", "not set")
193+
194+
lines.append(f" CUBLAS_LOGINFO_DBG={cublas_info}")
195+
lines.append(f" CUBLAS_LOGDEST_DBG={cublas_dest}")
196+
lines.append(f" CUBLASLT_LOG_LEVEL={cublaslt_level}")
197+
lines.append(f" CUBLASLT_LOG_FILE={cublaslt_file}")
198+
lines.append(f" CUDNN_LOGLEVEL_DBG={cudnn_level}")
199+
lines.append(f" CUDNN_LOGDEST_DBG={cudnn_dest}")
200+
lines.append(f" CUDNN_FRONTEND_LOG_INFO={cudnn_fe_info}")
201+
lines.append(f" CUDNN_FRONTEND_LOG_FILE={cudnn_fe_file}")
202+
132203
except Exception as e:
133204
lines.append(f"Error gathering system information: {e}")
134205

@@ -460,6 +531,7 @@ def flashinfer_api_log(func: Callable = None) -> Callable:
460531
- "stdout": Log to standard output
461532
- "stderr": Log to standard error
462533
- <path>: Log to specified file path
534+
- Use %i in path for process ID substitution (e.g., "log_%i.txt" -> "log_12345.txt")
463535
464536
Examples
465537
--------
@@ -482,6 +554,13 @@ def flashinfer_api_log(func: Callable = None) -> Callable:
482554
- **CUDA Graph Compatibility**: At level 3, tensor statistics (min/max/mean/nan_count)
483555
are automatically skipped during CUDA graph capture to avoid synchronization issues.
484556
The message "[statistics skipped: CUDA graph capture in progress]" will be logged.
557+
- **cuDNN/cuBLAS/cuBLASLt Integration**: At level 3, if not already set by the user, the following
558+
environment variables are automatically configured to enable cuDNN, cuBLAS, and cuBLASLt logging:
559+
- CUBLAS_LOGINFO_DBG=1, CUBLAS_LOGDEST_DBG=flashinfer_cublas_log_%i.txt
560+
- CUBLASLT_LOG_LEVEL=2, CUBLASLT_LOG_FILE=flashinfer_cublaslt_log_%i.txt
561+
- CUDNN_LOGLEVEL_DBG=2.5, CUDNN_LOGDEST_DBG=flashinfer_cudnn_backend_log_%i.txt
562+
- CUDNN_FRONTEND_LOG_INFO=1, CUDNN_FRONTEND_LOG_FILE=flashinfer_cudnn_frontend_log_%i.txt
563+
The %i pattern is automatically replaced with the process ID for multi-process environments.
485564
- The logger does not propagate to the root logger to avoid duplicate logs.
486565
"""
487566
# If logging is disabled, return original function with zero overhead

0 commit comments

Comments
 (0)