use std::alloc::{GlobalAlloc, Layout, System};
use std::env;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use structured_zstd::encoding::{CompressionLevel, FrameCompressor};
const BUCKETS: usize = usize::BITS as usize + 1;
static RECORDING: AtomicBool = AtomicBool::new(false);
static COUNTS: [AtomicUsize; BUCKETS] = [const { AtomicUsize::new(0) }; BUCKETS];
static BYTES: [AtomicUsize; BUCKETS] = [const { AtomicUsize::new(0) }; BUCKETS];
struct Census;
unsafe impl GlobalAlloc for Census {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
record(layout.size());
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
record(new_size);
unsafe { System.realloc(ptr, layout, new_size) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
record(layout.size());
unsafe { System.alloc_zeroed(layout) }
}
}
#[inline]
fn record(size: usize) {
if !RECORDING.load(Ordering::Relaxed) {
return;
}
let bucket = size.leading_zeros() as usize;
COUNTS[bucket].fetch_add(1, Ordering::Relaxed);
BYTES[bucket].fetch_add(size, Ordering::Relaxed);
}
#[global_allocator]
static ALLOC: Census = Census;
fn main() {
let args: Vec<String> = env::args().collect();
let level: i32 = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(3);
let corpus = args
.get(2)
.map(String::as_str)
.unwrap_or("zstd/decodecorpus_files/z000033");
let data = std::fs::read(corpus)
.unwrap_or_else(|e| panic!("alloc_census_warm: cannot read {corpus}: {e}"));
let fresh = args.get(3).map(|s| s == "fresh").unwrap_or(false);
let mut out = Vec::new();
let mut warm: Option<FrameCompressor<&[u8], &mut Vec<u8>>> = None;
if !fresh {
let mut compressor: FrameCompressor<&[u8], &mut Vec<u8>> =
FrameCompressor::new(CompressionLevel::Level(level));
compressor.set_source(&data[..]);
compressor.set_drain(&mut out);
compressor.compress();
warm = Some(compressor);
}
let mut out2 = Vec::with_capacity(structured_zstd::encoding::compress_bound(data.len()));
RECORDING.store(true, Ordering::SeqCst);
match warm.as_mut() {
Some(compressor) => {
compressor.set_source(&data[..]);
compressor.set_drain(&mut out2);
compressor.compress();
}
None => {
let mut compressor: FrameCompressor<&[u8], &mut Vec<u8>> =
FrameCompressor::new(CompressionLevel::Level(level));
compressor.set_source(&data[..]);
compressor.set_drain(&mut out2);
compressor.compress();
}
}
RECORDING.store(false, Ordering::SeqCst);
let mut total_count = 0usize;
let mut total_bytes = 0usize;
let shape = if fresh { "fresh frame" } else { "warm frame" };
println!("level {level}, {} bytes in, {shape}:", data.len());
println!(
"{:>12} {:>7} {:>12} {:>9}",
"size class", "allocs", "bytes", "avg"
);
for bucket in 0..BUCKETS {
let count = COUNTS[bucket].load(Ordering::Relaxed);
if count == 0 {
continue;
}
let bytes = BYTES[bucket].load(Ordering::Relaxed);
total_count += count;
total_bytes += bytes;
let class_floor = if bucket == BUCKETS - 1 {
0
} else {
1usize << (usize::BITS as usize - 1 - bucket)
};
println!(
"{:>12} {:>7} {:>12} {:>9}",
format!(">= {class_floor}"),
count,
bytes,
bytes / count,
);
}
println!("{:>12} {total_count:>7} {total_bytes:>12}", "TOTAL");
println!("compressed to {} bytes", out2.len());
}