Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions Doc/library/profiling.sampling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,8 @@ is unaware it is being profiled.
When profiling production systems, keep these guidelines in mind:

Start with shorter durations (10-30 seconds) to get quick results, then extend
if you need more statistical accuracy. The default 10-second duration is usually
sufficient to identify major hotspots.
if you need more statistical accuracy. By default, profiling runs until the
target process completes, which is usually sufficient to identify major hotspots.

If possible, profile during representative load rather than peak traffic.
Profiles collected during normal operation are easier to interpret than those
Expand Down Expand Up @@ -329,7 +329,7 @@ The default configuration works well for most use cases:
* - Default for ``--sampling-rate`` / ``-r``
- 1 kHz
* - Default for ``--duration`` / ``-d``
- 10 seconds
- Run to completion
* - Default for ``--all-threads`` / ``-a``
- Main thread only
* - Default for ``--native``
Expand Down Expand Up @@ -363,15 +363,14 @@ cost of slightly higher profiler CPU usage. Lower rates reduce profiler
overhead but may miss short-lived functions. For most applications, the
default rate provides a good balance between accuracy and overhead.

The :option:`--duration` option (:option:`-d`) sets how long to profile in seconds. The
default is 10 seconds::
The :option:`--duration` option (:option:`-d`) sets how long to profile in seconds. By
default, profiling continues until the target process exits or is interrupted::

python -m profiling.sampling run -d 60 script.py

Longer durations collect more samples and produce more statistically reliable
results, especially for code paths that execute infrequently. When profiling
a program that runs for a fixed time, you may want to set the duration to
match or exceed the expected runtime.
Specifying a duration is useful when attaching to long-running processes or when
you want to limit profiling to a specific time window. When profiling a script,
the default behavior of running to completion is usually what you want.


Thread selection
Expand Down Expand Up @@ -1394,7 +1393,7 @@ Sampling options

.. option:: -d <seconds>, --duration <seconds>

Profiling duration in seconds. Default: 10.
Profiling duration in seconds. Default: run to completion.

.. option:: -a, --all-threads

Expand Down
105 changes: 43 additions & 62 deletions Lib/profiling/sampling/_flamegraph_assets/flamegraph.css
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,10 @@ body.resizing-sidebar {
position: relative;
}

.summary-card:nth-child(1) { --i: 0; --card-color: 55, 118, 171; }
.summary-card:nth-child(2) { --i: 1; --card-color: 40, 167, 69; }
.summary-card:nth-child(3) { --i: 2; --card-color: 255, 193, 7; }
.summary-card:nth-child(4) { --i: 3; --card-color: 111, 66, 193; }
.summary-card:nth-child(1) { --i: 0; --card-color: var(--card-blue); }
.summary-card:nth-child(2) { --i: 1; --card-color: var(--card-green); }
.summary-card:nth-child(3) { --i: 2; --card-color: var(--card-yellow); }
.summary-card:nth-child(4) { --i: 3; --card-color: var(--card-purple); }

.summary-card:hover {
border-color: rgba(var(--card-color), 0.6);
Expand Down Expand Up @@ -405,43 +405,40 @@ body.resizing-sidebar {
text-overflow: ellipsis;
}

/* Efficiency Bar */
.efficiency-section {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--border);
}
/* --------------------------------------------------------------------------
Progress Bars
-------------------------------------------------------------------------- */

.efficiency-header {
.bar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
}

.efficiency-label {
.bar-label {
font-size: 9px;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.2px;
}

.efficiency-value {
.bar-value {
font-family: var(--font-mono);
font-size: 11px;
font-weight: 700;
color: var(--accent);
}

.efficiency-bar {
.bar {
height: 6px;
background: var(--bg-tertiary);
border-radius: 3px;
overflow: hidden;
}

.efficiency-fill {
.bar-fill {
height: 100%;
background: linear-gradient(90deg, #28a745 0%, #20c997 50%, #17a2b8 100%);
border-radius: 3px;
Expand All @@ -450,7 +447,7 @@ body.resizing-sidebar {
overflow: hidden;
}

.efficiency-fill::after {
.bar-fill::after {
content: '';
position: absolute;
top: 0;
Expand All @@ -467,68 +464,56 @@ body.resizing-sidebar {
}

/* --------------------------------------------------------------------------
Thread Stats Grid (in Sidebar)
Efficiency Section Container
-------------------------------------------------------------------------- */

.efficiency-section {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--border);
}

/* --------------------------------------------------------------------------
Thread Stats Progress Bars (in Sidebar)
-------------------------------------------------------------------------- */

.thread-stats-section {
display: block;
}

.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
.stats-container {
display: flex;
flex-direction: column;
gap: 10px;
}

.stat-tile {
background: var(--bg-primary);
border-radius: 8px;
padding: 10px;
text-align: center;
border: 2px solid var(--border);
transition: all var(--transition-fast);
.stat-item {
animation: fadeIn 0.4s ease-out backwards;
animation-delay: calc(var(--i, 0) * 0.05s);
}

.stat-tile:nth-child(1) { --i: 0; }
.stat-tile:nth-child(2) { --i: 1; }
.stat-tile:nth-child(3) { --i: 2; }
.stat-tile:nth-child(4) { --i: 3; }
.stat-item:nth-child(1) { --i: 0; }
.stat-item:nth-child(2) { --i: 1; }
.stat-item:nth-child(3) { --i: 2; }
.stat-item:nth-child(4) { --i: 3; }
.stat-item:nth-child(5) { --i: 4; }

.stat-tile:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-sm);
/* Color variants for bar-fill */
.bar-fill--green {
background: linear-gradient(90deg, #28a745 0%, #20c997 100%);
}

.stat-tile-value {
font-family: var(--font-mono);
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
line-height: 1.2;
.bar-fill--yellow {
background: linear-gradient(90deg, #ffc107 0%, #ffdb4d 100%);
}

.stat-tile-label {
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
color: var(--text-muted);
margin-top: 2px;
.bar-fill--purple {
background: linear-gradient(90deg, #6f42c1 0%, #9b6dd6 100%);
}

/* Stat tile color variants */
.stat-tile--green { --tile-color: 40, 167, 69; --tile-text: #28a745; }
.stat-tile--red { --tile-color: 220, 53, 69; --tile-text: #dc3545; }
.stat-tile--yellow { --tile-color: 255, 193, 7; --tile-text: #d39e00; }
.stat-tile--purple { --tile-color: 111, 66, 193; --tile-text: #6f42c1; }

.stat-tile[class*="--"] {
border-color: rgba(var(--tile-color), 0.4);
background: linear-gradient(135deg, rgba(var(--tile-color), 0.08) 0%, var(--bg-primary) 100%);
.bar-fill--red {
background: linear-gradient(90deg, #dc3545 0%, #ff6b7a 100%);
}
.stat-tile[class*="--"] .stat-tile-value { color: var(--tile-text); }

/* --------------------------------------------------------------------------
Hotspot Cards
Expand Down Expand Up @@ -985,10 +970,6 @@ body.resizing-sidebar {
.brand-info {
display: none;
}

.stats-grid {
grid-template-columns: 1fr;
}
}

/* --------------------------------------------------------------------------
Expand Down
24 changes: 19 additions & 5 deletions Lib/profiling/sampling/_flamegraph_assets/flamegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -742,24 +742,38 @@ function populateThreadStats(data, selectedThreadId = null) {
if (gilReleasedStat) gilReleasedStat.style.display = 'block';
if (gilWaitingStat) gilWaitingStat.style.display = 'block';

const gilHeldPct = threadStats.has_gil_pct || 0;
const gilHeldPctElem = document.getElementById('gil-held-pct');
if (gilHeldPctElem) gilHeldPctElem.textContent = `${(threadStats.has_gil_pct || 0).toFixed(1)}%`;
if (gilHeldPctElem) gilHeldPctElem.textContent = `${gilHeldPct.toFixed(1)}%`;
const gilHeldFill = document.getElementById('gil-held-fill');
if (gilHeldFill) gilHeldFill.style.width = `${gilHeldPct}%`;

const gilReleasedPctElem = document.getElementById('gil-released-pct');
// GIL Released = not holding GIL and not waiting for it
const gilReleasedPct = Math.max(0, 100 - (threadStats.has_gil_pct || 0) - (threadStats.gil_requested_pct || 0));
const gilReleasedPctElem = document.getElementById('gil-released-pct');
if (gilReleasedPctElem) gilReleasedPctElem.textContent = `${gilReleasedPct.toFixed(1)}%`;
const gilReleasedFill = document.getElementById('gil-released-fill');
if (gilReleasedFill) gilReleasedFill.style.width = `${gilReleasedPct}%`;

const gilWaitingPct = threadStats.gil_requested_pct || 0;
const gilWaitingPctElem = document.getElementById('gil-waiting-pct');
if (gilWaitingPctElem) gilWaitingPctElem.textContent = `${(threadStats.gil_requested_pct || 0).toFixed(1)}%`;
if (gilWaitingPctElem) gilWaitingPctElem.textContent = `${gilWaitingPct.toFixed(1)}%`;
const gilWaitingFill = document.getElementById('gil-waiting-fill');
if (gilWaitingFill) gilWaitingFill.style.width = `${gilWaitingPct}%`;
}

const gcPct = threadStats.gc_pct || 0;
const gcPctElem = document.getElementById('gc-pct');
if (gcPctElem) gcPctElem.textContent = `${(threadStats.gc_pct || 0).toFixed(1)}%`;
if (gcPctElem) gcPctElem.textContent = `${gcPct.toFixed(1)}%`;
const gcFill = document.getElementById('gc-fill');
if (gcFill) gcFill.style.width = `${gcPct}%`;

// Exception stats
const excPct = threadStats.has_exception_pct || 0;
const excPctElem = document.getElementById('exc-pct');
if (excPctElem) excPctElem.textContent = `${(threadStats.has_exception_pct || 0).toFixed(1)}%`;
if (excPctElem) excPctElem.textContent = `${excPct.toFixed(1)}%`;
const excFill = document.getElementById('exc-fill');
if (excFill) excFill.style.width = `${excPct}%`;
}

// ============================================================================
Expand Down
79 changes: 52 additions & 27 deletions Lib/profiling/sampling/_flamegraph_assets/flamegraph_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -159,21 +159,21 @@ <h3 class="section-title">Profile Summary</h3>
</div>
<!-- Efficiency Bar -->
<div class="efficiency-section" id="efficiency-section" style="display: none;">
<div class="efficiency-header">
<span class="efficiency-label">Sampling Efficiency</span>
<span class="efficiency-value" id="stat-efficiency">--</span>
<div class="bar-header">
<span class="bar-label">Sampling Efficiency</span>
<span class="bar-value" id="stat-efficiency">--</span>
</div>
<div class="efficiency-bar">
<div class="efficiency-fill" id="efficiency-fill"></div>
<div class="bar">
<div class="bar-fill" id="efficiency-fill"></div>
</div>
<div class="missed-samples-header">
<span class="efficiency-label">Missed samples</span>
<span class="efficiency-value" id="stat-missed-samples">--</span>
<div class="bar-header">
<span class="bar-label">Missed samples</span>
<span class="bar-value" id="stat-missed-samples">--</span>
</div>
<div class="efficiency-bar">
<div class="efficiency-fill" id="missed-samples-fill"></div>
<div class="bar">
<div class="bar-fill" id="missed-samples-fill"></div>
</div>

</div>
</div>
</section>
Expand All @@ -187,26 +187,51 @@ <h3 class="section-title">Runtime Stats</h3>
</svg>
</button>
<div class="section-content">
<div class="stats-grid">
<div class="stat-tile stat-tile--green" id="gil-held-stat">
<div class="stat-tile-value" id="gil-held-pct">--</div>
<div class="stat-tile-label">GIL Held</div>
<div class="stats-container">
<div class="stat-item" id="gil-held-stat">
<div class="bar-header">
<span class="bar-label">GIL Held</span>
<span class="bar-value" id="gil-held-pct">--</span>
</div>
<div class="bar">
<div class="bar-fill bar-fill--green" id="gil-held-fill"></div>
</div>
</div>
<div class="stat-tile stat-tile--red" id="gil-released-stat">
<div class="stat-tile-value" id="gil-released-pct">--</div>
<div class="stat-tile-label">GIL Released</div>
<div class="stat-item" id="gil-released-stat">
<div class="bar-header">
<span class="bar-label">GIL Released</span>
<span class="bar-value" id="gil-released-pct">--</span>
</div>
<div class="bar">
<div class="bar-fill bar-fill--red" id="gil-released-fill"></div>
</div>
</div>
<div class="stat-tile stat-tile--yellow" id="gil-waiting-stat">
<div class="stat-tile-value" id="gil-waiting-pct">--</div>
<div class="stat-tile-label">Waiting GIL</div>
<div class="stat-item" id="gil-waiting-stat">
<div class="bar-header">
<span class="bar-label">Waiting GIL</span>
<span class="bar-value" id="gil-waiting-pct">--</span>
</div>
<div class="bar">
<div class="bar-fill bar-fill--yellow" id="gil-waiting-fill"></div>
</div>
</div>
<div class="stat-tile stat-tile--purple" id="gc-stat">
<div class="stat-tile-value" id="gc-pct">--</div>
<div class="stat-tile-label">GC</div>
<div class="stat-item" id="gc-stat">
<div class="bar-header">
<span class="bar-label">GC</span>
<span class="bar-value" id="gc-pct">--</span>
</div>
<div class="bar">
<div class="bar-fill bar-fill--purple" id="gc-fill"></div>
</div>
</div>
<div class="stat-tile stat-tile--red" id="exc-stat">
<div class="stat-tile-value" id="exc-pct">--</div>
<div class="stat-tile-label">Exception</div>
<div class="stat-item" id="exc-stat">
<div class="bar-header">
<span class="bar-label">Exception</span>
<span class="bar-value" id="exc-pct">--</span>
</div>
<div class="bar">
<div class="bar-fill bar-fill--red" id="exc-fill"></div>
</div>
</div>
</div>
</div>
Expand Down
Loading
Loading