{ "cells": [ { "cell_type": "markdown", "id": "903704f7", "metadata": {}, "source": [ "# Compile-Time Analytics\n", "\n", "This notebook helps you:\n", "\n", "1. Run `ci/build_compile_time_bench.sh` from the repo root.\n", "2. Load and inspect an all-header processing CSV.\n", "3. Explore high-impact headers by TU coverage and average processing time.\n", "4. Build combined ranking scores to identify optimization targets.\n", "\n", "Expected CSV columns include:\n", "- `header_path`\n", "- `include_tu_count`\n", "- `avg_process_time_s`\n", "- `total_process_time_s`\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ad58db28", "metadata": {}, "outputs": [], "source": [ "%pip install pandas matplotlib plotly nbformat" ] }, { "cell_type": "code", "execution_count": null, "id": "16ba6808", "metadata": {}, "outputs": [], "source": [ "import os\n", "import shlex\n", "import sys\n", "from pathlib import Path\n", "\n", "import pandas as pd\n", "\n", "pd.set_option(\"display.max_colwidth\", 160)\n", "pd.set_option(\"display.width\", 200)\n", "pd.set_option(\"display.max_columns\", 20)\n", "\n", "REPO_ROOT = Path.cwd()\n", "while REPO_ROOT != REPO_ROOT.parent and not (REPO_ROOT / \".git\").exists():\n", " REPO_ROOT = REPO_ROOT.parent\n", "\n", "if not (REPO_ROOT / \".git\").exists():\n", " raise RuntimeError(\n", " \"Could not locate repo root (.git). Start notebook from inside the CCCL repo.\"\n", " )\n", "\n", "print(f\"Repo root: {REPO_ROOT}\")\n", "print(f\"Python executable: {sys.executable}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "373652c8", "metadata": {}, "outputs": [], "source": [ "# --- Run build_compile_time_bench.sh (file-processing mode) ---\n", "import subprocess\n", "from pathlib import Path\n", "\n", "if \"REPO_ROOT\" not in globals():\n", " REPO_ROOT = Path.cwd()\n", " while REPO_ROOT != REPO_ROOT.parent and not (REPO_ROOT / \".git\").exists():\n", " REPO_ROOT = REPO_ROOT.parent\n", "\n", "output_csv = Path(os.environ.get(\"COMPILE_TIME_CSV\", \"/tmp/compile_time.csv\"))\n", "cmd = [\n", " \"bash\",\n", " str(REPO_ROOT / \"ci\" / \"build_compile_time_bench.sh\"),\n", " *shlex.split(os.environ.get(\"COMPILE_TIME_BUILD_ARGS\", \"\")),\n", " \"--\",\n", " \"-f\",\n", " \"file-processing\",\n", " \"-e\",\n", " \"-n\",\n", " os.environ.get(\"COMPILE_TIME_TOP_N\", \"5000\"),\n", " \"--sort\",\n", " \"total\",\n", " \"--output-csv\",\n", " str(output_csv),\n", "]\n", "\n", "print(\"Command:\")\n", "print(\" \" + \" \".join(shlex.quote(x) for x in cmd))\n", "\n", "subprocess.run(cmd, cwd=REPO_ROOT, check=True)\n", "print(f\"\\nWrote: {output_csv}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "12ad4c5c", "metadata": {}, "outputs": [], "source": [ "# --- Load CSV ---\n", "csv_path = Path(os.environ.get(\"COMPILE_TIME_CSV\", \"/tmp/compile_time.csv\"))\n", "if not csv_path.exists():\n", " raise FileNotFoundError(f\"Missing CSV: {csv_path}. Run the script first.\")\n", "\n", "df = pd.read_csv(csv_path)\n", "\n", "event_summary_cols = {\n", " \"event_name\",\n", " \"event_key\",\n", " \"root_tu_count\",\n", " \"selected_avg_per_root_tu_s\",\n", " \"selected_total_s\",\n", "}\n", "required_cols = [\n", " \"header_path\",\n", " \"include_tu_count\",\n", " \"avg_process_time_s\",\n", " \"total_process_time_s\",\n", "]\n", "\n", "if event_summary_cols.issubset(df.columns):\n", " df[\"header_path\"] = df[\"event_key\"]\n", " df[\"include_tu_count\"] = df[\"root_tu_count\"]\n", " if (\n", " \"avg_inclusive_per_root_tu_s\" in df.columns\n", " and \"total_inclusive_s\" in df.columns\n", " ):\n", " df[\"avg_process_time_s\"] = df[\"avg_inclusive_per_root_tu_s\"]\n", " df[\"total_process_time_s\"] = df[\"total_inclusive_s\"]\n", " else:\n", " df[\"avg_process_time_s\"] = df[\"selected_avg_per_root_tu_s\"]\n", " df[\"total_process_time_s\"] = df[\"selected_total_s\"]\n", "\n", "missing = [c for c in required_cols if c not in df.columns]\n", "if missing:\n", " raise ValueError(\n", " f\"CSV is not all-header processing output. Missing columns: {missing}. \"\n", " \"Run the script cell above to regenerate /tmp/compile_time.csv.\"\n", " )\n", "\n", "for col in [\"include_tu_count\", \"avg_process_time_s\", \"total_process_time_s\"]:\n", " df[col] = pd.to_numeric(df[col], errors=\"coerce\").fillna(0)\n", "\n", "print(f\"Rows: {len(df):,}\")\n", "print(f\"Columns: {list(df.columns)}\")\n", "df.head(5)" ] }, { "cell_type": "code", "execution_count": null, "id": "1a9b36a8", "metadata": {}, "outputs": [], "source": [ "# --- Quick top-N views ---\n", "TOP_N = 5\n", "\n", "print(\"Top by avg_process_time_s\")\n", "display(\n", " df.nlargest(TOP_N, \"avg_process_time_s\")[\n", " [\n", " \"header_path\",\n", " \"avg_process_time_s\",\n", " \"include_tu_count\",\n", " \"total_process_time_s\",\n", " ]\n", " ]\n", ")\n", "\n", "print(\"\\nTop by include_tu_count\")\n", "display(\n", " df.nlargest(TOP_N, \"include_tu_count\")[\n", " [\n", " \"header_path\",\n", " \"include_tu_count\",\n", " \"avg_process_time_s\",\n", " \"total_process_time_s\",\n", " ]\n", " ]\n", ")\n", "\n", "print(\"\\nTop by impact score (include_tu_count * avg_process_time_s)\")\n", "df_score = df.copy()\n", "df_score[\"impact_score\"] = df_score[\"include_tu_count\"] * df_score[\"avg_process_time_s\"]\n", "display(\n", " df_score.nlargest(TOP_N, \"impact_score\")[\n", " [\n", " \"header_path\",\n", " \"impact_score\",\n", " \"include_tu_count\",\n", " \"avg_process_time_s\",\n", " \"total_process_time_s\",\n", " ]\n", " ]\n", ")" ] }, { "cell_type": "markdown", "id": "174eabd4", "metadata": {}, "source": [ "### How to read this plot\n", "\n", "- Each point is one header seen in all-mode profiling.\n", "- **X axis (`include_tu_count`)**: how many generated public-header TUs include this header at least once.\n", "- **Y axis (`avg_process_time_s`)**: average time spent processing that header per including TU.\n", "- Headers near the **upper-right** are usually the best optimization candidates because they are both widespread and expensive per include." ] }, { "cell_type": "code", "execution_count": null, "id": "9ddc0725", "metadata": {}, "outputs": [], "source": [ "# --- Interactive scatter (hover shows header name) ---\n", "\n", "import plotly.express as px\n", "\n", "plot_df = df.copy()\n", "\n", "total_headers = len(plot_df)\n", "total_public_headers = int(plot_df[\"include_tu_count\"].max())\n", "\n", "fig = px.scatter(\n", " plot_df,\n", " x=\"include_tu_count\",\n", " y=\"avg_process_time_s\",\n", " hover_name=\"header_path\",\n", " hover_data={\n", " \"include_tu_count\": True,\n", " \"avg_process_time_s\": \":.6f\",\n", " \"total_process_time_s\": \":.3f\",\n", " \"header_path\": False,\n", " },\n", " opacity=0.6,\n", " title=(\n", " f\"Header include count vs avg processing time ({total_headers:,} total headers; \"\n", " f\"coverage measured across {total_public_headers} public headers)\"\n", " ),\n", ")\n", "\n", "fig.update_layout(\n", " xaxis_title=(\n", " \"TU coverage: number of public headers that include this header \"\n", " f\"(out of {total_public_headers})\"\n", " ),\n", " yaxis_title=\"Average processing time per including TU (seconds)\",\n", " height=900,\n", ")\n", "fig.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "f429b934", "metadata": {}, "outputs": [], "source": [ "# --- Optional: run ctadvisor and parse expensive headers ---\n", "run_ctadvisor = os.environ.get(\"COMPILE_TIME_RUN_CTADVISOR\") == \"1\"\n", "CTADVISOR_ENTRIES = 10\n", "CTADVISOR_THREADS = os.cpu_count() or 8\n", "\n", "trace_root = (\n", " REPO_ROOT\n", " / \"build\"\n", " / os.environ.get(\"CCCL_BUILD_INFIX\", \"cuda13.1-gcc14\")\n", " / os.environ.get(\"CCCL_COMPILE_TIME_PRESET\", \"all-dev\")\n", " / \"compile_time\"\n", " / \"raw_traces\"\n", ")\n", "ctadvisor_cmd = [\n", " \"ctadvisor\",\n", " \"--trace-file-path\",\n", " str(trace_root),\n", " \"--header-advisor-entries\",\n", " str(CTADVISOR_ENTRIES),\n", " \"--thread-number\",\n", " str(CTADVISOR_THREADS),\n", "]\n", "\n", "if run_ctadvisor:\n", " print(\"Command:\")\n", " print(\" \" + \" \".join(shlex.quote(x) for x in ctadvisor_cmd))\n", "\n", " result = subprocess.run(\n", " ctadvisor_cmd, cwd=REPO_ROOT, check=True, capture_output=True, text=True\n", " )\n", " print(result.stdout)\n", "else:\n", " print(\"Skipping ctadvisor. Set COMPILE_TIME_RUN_CTADVISOR=1 to run it.\")" ] } ], "metadata": { "kernelspec": { "display_name": "cccl", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }