Skip to content

Commit 8828b33

Browse files
Store Tachyon samples in bounded columnar blocks
Tachyon currently stores all samples in a single compressed stream. Replay has to decompress the complete sample section before it can process any samples, so temporary memory usage grows with the length of the capture. The stream also mixes data with different patterns and repeatedly stores values such as thread and interpreter IDs. Change the file format to split samples into independently decodable blocks, each limited to 1 MiB of decoded data. Strings and frames remain shared across the whole file, preserving deduplication across the complete capture. Within each block, separate the sample data into columns for stream identifiers, timestamps, statuses, stack changes, and frame indices. Store each thread and interpreter pair once and have samples refer to those pairs by index. Encode timestamps relative to the expected sampling time for each stream, and store status bits common to all samples in the block once while packing the bits that vary into bitmaps. Encode stacks by reusing unchanged stacks, retaining common outer frames when only part of a stack changes, and referring back to complete stacks already seen in the block. Store frame indices using the smallest byte width needed by the block. These encodings remove repeated values and group similar data together before compression. Compress each column independently as raw bytes, a repeated byte, or zstd, whichever is smallest. The block header contains the information needed to locate, validate, and decode these columns independently. Replay can then decode one block at a time and reuse its buffers for subsequent blocks. This bounds temporary sample decompression memory by the block size rather than the total capture length, although the shared string and frame tables still grow with the number of unique entries. This change only applies to profiles exported to files. Live streaming will need a different approach because it cannot rely on string and frame tables written at the end of the file, and is left for gh-145464.
1 parent 1512f78 commit 8828b33

12 files changed

Lines changed: 2248 additions & 997 deletions

File tree

‎InternalDocs/profiling_binary_format.md‎

Lines changed: 198 additions & 98 deletions
Large diffs are not rendered by default.

‎Lib/profiling/sampling/binary_collector.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def get_stats(self):
146146
"""Get encoding statistics.
147147
148148
Returns:
149-
Dict with encoding statistics including repeat/full/suffix/pop-push
149+
Dict with encoding statistics including repeat/full/suffix/reuse
150150
record counts, frames written/saved, and compression ratio.
151151
"""
152152
return self._writer.get_stats()

‎Lib/profiling/sampling/sample.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,22 +411,22 @@ def _print_binary_stats(self, collector):
411411
repeat_samples = stats.get('repeat_samples', 0)
412412
full_records = stats.get('full_records', 0)
413413
suffix_records = stats.get('suffix_records', 0)
414-
pop_push_records = stats.get('pop_push_records', 0)
414+
reuse_records = stats.get('reuse_records', 0)
415415
total_records = stats.get('total_records', 0)
416416

417417
if total_records > 0:
418418
repeat_pct = repeat_records / total_records * 100
419419
full_pct = full_records / total_records * 100
420420
suffix_pct = suffix_records / total_records * 100
421-
pop_push_pct = pop_push_records / total_records * 100
421+
reuse_pct = reuse_records / total_records * 100
422422
else:
423-
repeat_pct = full_pct = suffix_pct = pop_push_pct = 0
423+
repeat_pct = full_pct = suffix_pct = reuse_pct = 0
424424

425425
print(f" Records: {total_records:,}")
426426
print(f" RLE repeat: {repeat_records:,} ({ANSIColors.GREEN}{repeat_pct:.1f}%{ANSIColors.RESET}) [{repeat_samples:,} samples]")
427427
print(f" Full stack: {full_records:,} ({full_pct:.1f}%)")
428428
print(f" Suffix match: {suffix_records:,} ({suffix_pct:.1f}%)")
429-
print(f" Pop-push: {pop_push_records:,} ({pop_push_pct:.1f}%)")
429+
print(f" Stack reuse: {reuse_records:,} ({reuse_pct:.1f}%)")
430430

431431
frames_written = stats.get('total_frames_written', 0)
432432
frames_saved = stats.get('frames_saved', 0)

0 commit comments

Comments
 (0)