Skip to content

Commit 3ba36b6

Browse files
committed
Revert "[3.15] gh-152721: Fix quadratic RLE replay time in the profiling binary reader (GH-152722) (#158850)"
This reverts commit ebaca2a.
1 parent 5b872fc commit 3ba36b6

3 files changed

Lines changed: 24 additions & 93 deletions

File tree

‎Lib/test/test_profiling/test_sampling_profiler/test_binary_format.py‎

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import struct
88
import tempfile
99
import unittest
10-
from unittest import mock
1110
from collections import defaultdict
1211

1312
from test.support import captured_stderr
@@ -1504,77 +1503,6 @@ def test_alternating_threads_status_changes(self):
15041503
self.assertEqual(count, 100)
15051504
self.assert_samples_equal(samples, collector)
15061505

1507-
def test_rle_alternating_status_batches_correctly(self):
1508-
"""A repeat record whose status alternates every sample replays as N
1509-
single-status batches with the right cumulative timestamps."""
1510-
class BatchCollector:
1511-
def __init__(self):
1512-
self.batches = []
1513-
1514-
def collect(self, stack_frames, timestamps_us):
1515-
for interp in stack_frames:
1516-
for thread in interp.threads:
1517-
self.batches.append(
1518-
(thread.status, list(timestamps_us))
1519-
)
1520-
1521-
def export(self, filename):
1522-
pass
1523-
1524-
num_samples = 2000
1525-
frame = make_frame("rle.py", 42, "rle_func")
1526-
with tempfile.NamedTemporaryFile(suffix=".bin", delete=False) as f:
1527-
filename = f.name
1528-
self.temp_files.append(filename)
1529-
1530-
writer = BinaryCollector(filename, 1000, compression="none")
1531-
expected = []
1532-
for i in range(num_samples):
1533-
status = THREAD_STATUS_HAS_GIL if i % 2 else 0
1534-
ts = 1000 + i
1535-
expected.append((status, [ts]))
1536-
sample = [
1537-
make_interpreter(0, [make_thread(1, [frame], status)])
1538-
]
1539-
writer.collect(sample, timestamp_us=ts)
1540-
writer.export(None)
1541-
1542-
collector = BatchCollector()
1543-
with BinaryReader(filename) as reader:
1544-
count = reader.replay_samples(collector)
1545-
1546-
self.assertEqual(count, num_samples)
1547-
self.assertEqual(len(collector.batches), num_samples)
1548-
self.assertEqual(collector.batches, expected)
1549-
1550-
1551-
def test_rle_long_run_splits_batches(self):
1552-
# Construct a single repeat record larger than the writer's buffer.
1553-
num_samples = 8193
1554-
filename = self.create_binary_file([], compression="none")
1555-
data = bytearray(pathlib.Path(filename).read_bytes())
1556-
record = (struct.pack("=QIB", 1, 0, 0) # STACK_REPEAT
1557-
+ b"\x81\x40" # 8193 as a varint
1558-
+ b"\x01\x00" * num_samples) # delta=1, status=0
1559-
data[64:64] = record
1560-
struct.pack_into("=Q", data, 12, 0) # start timestamp
1561-
struct.pack_into("=Q", data, 28, num_samples)
1562-
struct.pack_into("=I", data, 36, 1) # thread count
1563-
for offset in (40, 48): # string and frame table offsets
1564-
old_offset = struct.unpack_from("=Q", data, offset)[0]
1565-
struct.pack_into("=Q", data, offset, old_offset + len(record))
1566-
struct.pack_into("=Q", data, len(data) - 24, len(data))
1567-
pathlib.Path(filename).write_bytes(data)
1568-
1569-
collector = mock.Mock()
1570-
with BinaryReader(filename) as reader:
1571-
count = reader.replay_samples(collector)
1572-
batches = [call.args[1] for call in collector.collect.call_args_list]
1573-
self.assertEqual(count, num_samples)
1574-
self.assertEqual([len(batch) for batch in batches], [8192, 1])
1575-
self.assertEqual([ts for batch in batches for ts in batch],
1576-
list(range(1, num_samples + 1)))
1577-
15781506

15791507
class TestBinaryStress(BinaryFormatTestBase):
15801508
"""Randomized stress tests for binary format."""

‎Misc/NEWS.d/next/Library/2026-07-01-18-00-00.gh-issue-152721.rlequad.rst‎

Lines changed: 0 additions & 2 deletions
This file was deleted.

‎Modules/_remote_debugging/binary_io_reader.c‎

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@
3333
/* Progress callback frequency */
3434
#define PROGRESS_CALLBACK_INTERVAL 1000
3535

36-
/* Cap per-batch RLE samples to bound the timestamp list (gh-151378) */
37-
#define MAX_RLE_BATCH_SAMPLES 8192
38-
3936
/* ============================================================================
4037
* BINARY READER IMPLEMENTATION
4138
* ============================================================================ */
@@ -1086,6 +1083,21 @@ emit_sample(RemoteDebuggingState *state, PyObject *collector,
10861083
return 0;
10871084
}
10881085

1086+
/* Helper to trim timestamp list and emit batch. Returns 0 on success, -1 on error. */
1087+
static int
1088+
emit_batch(RemoteDebuggingState *state, PyObject *collector,
1089+
uint64_t thread_id, uint32_t interpreter_id, uint8_t status,
1090+
const uint32_t *frame_indices, size_t stack_depth,
1091+
BinaryReader *reader, PyObject *timestamps_list, Py_ssize_t actual_size)
1092+
{
1093+
/* Trim list to actual size */
1094+
if (PyList_SetSlice(timestamps_list, actual_size, PyList_GET_SIZE(timestamps_list), NULL) < 0) {
1095+
return -1;
1096+
}
1097+
return emit_sample(state, collector, thread_id, interpreter_id, status,
1098+
frame_indices, stack_depth, reader, timestamps_list);
1099+
}
1100+
10891101
/* Helper to invoke progress callback, returns -1 on error */
10901102
static inline int
10911103
invoke_progress_callback(PyObject *callback, Py_ssize_t current, uint64_t total)
@@ -1214,18 +1226,17 @@ binary_reader_replay(BinaryReader *reader, PyObject *collector, PyObject *progre
12141226
ts->prev_timestamp += delta;
12151227

12161228
/* Start new batch on first sample or status change */
1217-
if (i == 0 || status != batch_status
1218-
|| batch_idx >= MAX_RLE_BATCH_SAMPLES) {
1229+
if (i == 0 || status != batch_status) {
12191230
if (timestamps_list) {
1220-
int rc = emit_sample(state, collector, thread_id, interpreter_id,
1221-
batch_status, ts->current_stack, ts->current_stack_depth,
1222-
reader, timestamps_list);
1231+
int rc = emit_batch(state, collector, thread_id, interpreter_id,
1232+
batch_status, ts->current_stack, ts->current_stack_depth,
1233+
reader, timestamps_list, batch_idx);
12231234
Py_DECREF(timestamps_list);
12241235
if (rc < 0) {
12251236
return -1;
12261237
}
12271238
}
1228-
timestamps_list = PyList_New(0);
1239+
timestamps_list = PyList_New(count - i);
12291240
if (!timestamps_list) {
12301241
return -1;
12311242
}
@@ -1238,20 +1249,14 @@ binary_reader_replay(BinaryReader *reader, PyObject *collector, PyObject *progre
12381249
Py_DECREF(timestamps_list);
12391250
return -1;
12401251
}
1241-
int append_rc = PyList_Append(timestamps_list, ts_obj);
1242-
Py_DECREF(ts_obj);
1243-
if (append_rc < 0) {
1244-
Py_DECREF(timestamps_list);
1245-
return -1;
1246-
}
1247-
batch_idx++;
1252+
PyList_SET_ITEM(timestamps_list, batch_idx++, ts_obj);
12481253
}
12491254

12501255
/* Emit final batch */
12511256
if (timestamps_list) {
1252-
int rc = emit_sample(state, collector, thread_id, interpreter_id,
1253-
batch_status, ts->current_stack, ts->current_stack_depth,
1254-
reader, timestamps_list);
1257+
int rc = emit_batch(state, collector, thread_id, interpreter_id,
1258+
batch_status, ts->current_stack, ts->current_stack_depth,
1259+
reader, timestamps_list, batch_idx);
12551260
Py_DECREF(timestamps_list);
12561261
if (rc < 0) {
12571262
return -1;

0 commit comments

Comments
 (0)