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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ Cargo.lock
parcode.txt
road.md
.gitignore
CONTRIBUTING.md
CONTRIBUTING.md
internal_todo.md
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This document tracks pending features, architectural optimizations, and API improvements planned for future versions of Parcode.

## 🟢 High Priority (Immediate - v0.4.x)
## 🟢 High Priority (Immediate - v0.5.x)

### 1. Serialization Backend Migration

Expand Down
4 changes: 0 additions & 4 deletions benches/lazy_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,22 @@ fn bench_lazy(c: &mut Criterion) {

let mut group = c.benchmark_group("Lazy Access");

// Caso A: Carga Completa (Estándar)
group.bench_function("full_load", |b| {
b.iter(|| {
let loaded: Root = Parcode::load(&path).expect("Failed to read parcode data");
black_box(loaded.child_a.meta);
});
});

// Caso B: Carga Lazy (Solo metadatos)
group.bench_function("lazy_meta_only", |b| {
b.iter(|| {
let file_handle = Parcode::open(&path).expect("Failed to open file");
let lazy = file_handle.root::<Root>().expect("Failed to read lazy");
// Accedemos a meta profundo A y B
let sum = lazy.child_a.meta + lazy.child_b.meta;
black_box(sum);
});
});

// Caso C: Carga Parcial (Meta A + Payload A)
group.bench_function("lazy_partial_load", |b| {
b.iter(|| {
let file_handle = Parcode::open(&path).expect("Failed to open file");
Expand Down
10 changes: 1 addition & 9 deletions benches/map_access.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// benches/map_access.rs
//! PLACEHOLDER
//!
#![allow(missing_docs)]
use criterion::{Criterion, criterion_group, criterion_main};
use parcode::{Parcode, ParcodeObject};
Expand All @@ -10,13 +7,10 @@ use tempfile::NamedTempFile;

#[derive(Serialize, Deserialize, ParcodeObject)]
struct MapContainer {
/// PLACEHOLDER
#[parcode(map)] // Optimized
opt_map: HashMap<u64, u64>,
// #[parcode(chunkable)] // Standard Blob (Unoptimized for random access)
// #[parcode(chunkable)]
// std_map: HashMap<u64, u64>,
// Nota: Para comparar justo, deberíamos usar otro campo o struct,
// ya que HashMap sin 'map' flag se guarda como blob.
}

fn bench_map(c: &mut Criterion) {
Expand All @@ -34,14 +28,12 @@ fn bench_map(c: &mut Criterion) {
let mut group = c.benchmark_group("Map Random Access");

group.bench_function("optimized_lookup", |b| {
// Setup reader once per batch to simulate persistent app
let file_handle = Parcode::open(&path).expect("Failed to open file");
let lazy = file_handle
.root::<MapContainer>()
.expect("Failed to read lazy");

b.iter(|| {
// Lookup key 50,000 (Middle)
let val = lazy.opt_map.get(&50_000).expect("Failed to get value");
std::hint::black_box(val);
});
Expand Down
110 changes: 32 additions & 78 deletions benches/performance.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,24 @@
// ===== benches\performance.rs =====
#![allow(missing_docs)]

use criterion::{Criterion, Throughput, criterion_group, criterion_main};
use parcode::{Parcode, ParcodeObject, graph::*, visitor::ParcodeVisitor};
use parcode::{Parcode, ParcodeObject};
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::hint::black_box;
use std::io::BufWriter;
use tempfile::NamedTempFile;

// --- SETUP ---

#[derive(Clone, Serialize, Deserialize, ParcodeObject)]
#[derive(Clone, Serialize, Deserialize, ParcodeObject, Debug)]
struct BenchItem {
id: u64,
payload: Vec<u64>, // 1KB payload
}

// Wrapper to satisfy Parcode traits
#[derive(Clone, Serialize, Deserialize)]
struct BenchCollection(Vec<BenchItem>);

// Minimal Manual Implementation for Benchmarking
impl ParcodeVisitor for BenchCollection {
fn visit<'a>(
&'a self,
graph: &mut TaskGraph<'a>,
parent_id: Option<ChunkId>,
config_override: Option<JobConfig>,
) {
// Delegate to the internal Vec, propagating the configuration
self.0.visit(graph, parent_id, config_override);
}

fn create_job<'a>(
&'a self,
config_override: Option<JobConfig>,
) -> Box<dyn SerializationJob<'a> + 'a> {
let base = Box::new(ContainerJob);
if let Some(cfg) = config_override {
Box::new(parcode::rt::ConfiguredJob::new(base, cfg))
} else {
base
}
}
#[parcode(chunkable)]
payload: Vec<u64>,
}

#[derive(Clone)]
struct ContainerJob;
impl SerializationJob<'_> for ContainerJob {
fn execute(&self, _: &[parcode::format::ChildRef]) -> parcode::Result<Vec<u8>> {
Ok(vec![])
}
fn estimated_size(&self) -> usize {
0
}
#[derive(Clone, Serialize, Deserialize, ParcodeObject, Debug)]
struct BenchCollection {
#[parcode(chunkable)]
data: Vec<BenchItem>,
}

fn generate_data(count: usize) -> BenchCollection {
Expand All @@ -64,15 +28,15 @@ fn generate_data(count: usize) -> BenchCollection {
payload: vec![i as u64; 128], // ~1KB
})
.collect();
BenchCollection(items)
BenchCollection { data: items }
}

// --- BENCHMARKS ---

fn bench_writers(c: &mut Criterion) {
let item_count = 200_000;
let item_count = 100_000;
let data = generate_data(item_count);
let raw_data = &data.0; // For bincode
let raw_data = &data.data; // For bincode

println!("Writers Item count: {}", item_count);

Expand All @@ -93,7 +57,7 @@ fn bench_writers(c: &mut Criterion) {
});
});

// 2. Parcode (Parallel Graph Engine)
// 2. Parcode
group.bench_function("parcode_save", |b| {
b.iter(|| {
let file = NamedTempFile::new().expect("Failed to create temp file");
Expand All @@ -105,7 +69,7 @@ fn bench_writers(c: &mut Criterion) {
}

fn bench_readers(c: &mut Criterion) {
let item_count = 200_000;
let item_count = 100_000;

println!("Readers Item count: {}", item_count);

Expand All @@ -114,7 +78,7 @@ fn bench_readers(c: &mut Criterion) {
// Setup files
let bincode_file = NamedTempFile::new().expect("Failed to create temp file");
bincode::serde::encode_into_std_write(
&data.0,
&data.data,
&mut BufWriter::new(&bincode_file),
bincode::config::standard(),
)
Expand Down Expand Up @@ -150,44 +114,34 @@ fn bench_readers(c: &mut Criterion) {
group.bench_function("parcode_random_access_10", |b| {
b.iter(|| {
let file_handle = Parcode::open(&parcode_path).expect("Failed to open file");
let root = file_handle.root_node().expect("Failed to get root");
let root = file_handle
.root::<BenchCollection>()
.expect("Failed to get root");

for i in (0..10).map(|x| x * (item_count / 10)) {
let _obj: BenchItem = root.get(i).expect("Failed to get item");
for i in (0..10).map(|x| x * (item_count / 20)) {
let _obj: BenchItem = root.data.get(i).expect("Failed to get item");
}
});
});

// 3. Parcode: Full Scan (Manual Shard Iteration)
group.bench_function("parcode_full_scan_manual", |b| {
// 3. Parcode: Parallel Stitching
group.bench_function("parcode_read_all_parallel", |b| {
b.iter(|| {
let file_handle = Parcode::open(&parcode_path).expect("Failed to open file");
let root = file_handle.root_node().expect("Failed to get root");

// Get the Shards (direct children)
let shards = root.children().expect("Failed to get children");

for shard_node in shards {
// Deserialize the complete Shard (Vec<BenchItem>)
let items: Vec<BenchItem> = shard_node.decode().expect("Failed to decode shard");

// Iterate items in memory (simulating usage)
for item in items {
black_box(item);
}
}
let _res: BenchCollection = Parcode::load(&parcode_path).expect("Failed to open file");
});
});

// 4. Parcode: Parallel Stitching (La nueva joya)
// Añadimos esto para probar la velocidad de reconstrucción total
group.bench_function("parcode_read_all_parallel", |b| {
// 4. Parcode: lazy iterator
group.bench_function("parcode_read_all_iter", |b| {
b.iter(|| {
let file_handle = Parcode::open(&parcode_path).expect("Failed to open file");
let root = file_handle.root_node().expect("Failed to get root");
let _res: Vec<BenchItem> = root
.decode_parallel_collection()
.expect("Failed to decode parallel");
let file = Parcode::open(&parcode_path).expect("Failed to open file");
let data = file
.load_lazy::<BenchCollection>()
.expect("Some error was ocurred");
for item in data.data.iter_lazy().expect("Some error was ocurred") {
let i = item.expect("Some error was ocurred");
black_box(i);
}
});
});

Expand Down
Loading