blob: fd8ec0d9821c57f148fa6a2c2588b2c2c873a1ec [file] [view]
# Chapter 3 Experiment Results Summary
## Experiment Configuration
| Item | Value |
|------|-------|
| Platform | macOS (Apple Silicon), GCC/Clang, Release -O2 |
| Build | `cmake -DENABLE_MEM_STAT=ON -DBUILD_TEST=OFF` |
| Encoding | PLAIN (value columns) + PLAIN (timestamp, via global config override) |
| Compression | UNCOMPRESSED |
| Table model | Aligned (shared timestamp via TimeChunkWriter) |
### Formula Constants (Aligned Mode)
| Symbol | Formula | Description |
|--------|---------|-------------|
| $s_{data}$ | $8 + \sum \text{sizeof}(\text{field\_type}_j)$ | Per-row data in ChunkWriter (shared timestamp) |
| $b$ | $n_{field} \times 104 + 96$ | Meta bytes per device per flush |
| $M_{init}$ | ~900 KB | Fixed writer overhead |
---
## E3-2: Write Memory Formula Precision
**Goal**: Validate that `calculate_mem_size_for_all_group()` matches $s_{data} \times F$.
### Key Finding: Three Layers of Memory Measurement
| Layer | What it measures | Example (5000 rows, 1 DOUBLE field) |
|-------|------------------|--------------------------------------|
| **Formula** ($s \times F$) | Raw data bytes | 80,000 bytes (16 bytes/row) |
| **Estimate API** | Encoded data in ChunkWriter | 73,767 bytes (14.8 bytes/row) |
| **ModStat** | Actually allocated memory | 131,512 bytes (26.3 bytes/row) |
**With PLAIN+UNCOMPRESSED (1 DOUBLE field)**:
- Estimate / Formula = **92%** (close match, 8% overhead from page headers + statistics)
- ModStat / Formula = **164%** (ByteStream allocates in 64 KB pages)
**With SNAPPY+TS_2DIFF (8 mixed fields, default encoding)**:
- Formula overestimates by ~50% (by design: formula = upper bound)
- INT32 PLAIN encoding uses `write_var_int` (variable-length), not fixed 4 bytes
- Timestamp uses global TS_2DIFF encoding (not affected by per-column schema)
### Write Precision Data (SNAPPY+TS_2DIFF, 8 fields)
| batch_size | Direct (KB) | Formula (KB) | Error |
|------------|-------------|--------------|-------|
| 5,000 | 136 | 273 | 50.1% |
| 8,000 | 213 | 437 | 51.2% |
| 16,000 | 155 | 875 | 82.2% |
| 32,000 | 814 | 1,750 | 53.5% |
| 65,536 | 1,647 | 3,584 | 54.0% |
**Conclusion**: The formula gives a conservative upper bound. Direct monitoring gives the actual (post-encoding) value. This aligns with the thesis Section 3.5 design: formula for pre-planning, direct monitoring for runtime control.
### Figures
- **F3_write_precision.pdf**: Bar chart comparing formula vs direct estimate.
---
## E3-2: Read Memory Formula Precision
**Goal**: Validate $M_{read} \approx M_{fixed} + \text{batch\_size} \times s_{row} + N_{cols} \times C_{page}$.
### Read Precision Data (PLAIN+UNCOMPRESSED)
| N_cols | batch_size | Peak (KB) | Formula (KB) | Error |
|--------|-----------|-----------|--------------|-------|
| 2 | 16,384 | 1,045 | 1,024 | **2.1%** |
| 4 | 16,384 | 1,366 | 1,536 | 11.1% |
| 6 | 16,384 | 1,709 | 1,920 | 11.0% |
| 8 | 16,384 | 2,300 | 2,432 | **5.4%** |
| 8 | 65,536 | 6,974 | 5,888 | 18.5% |
**Best accuracy at batch_size=16384** (2%~11% error), consistent with the Page size (128 KB) being aligned with the batch data.
### Figures
- **F3_read_precision.pdf**: (Left) Peak memory vs batch_size by N_cols; (Right) Error % heatmap.
---
## E3-4: EOQ Optimal Strategy Validation
**Goal**: Prove the U-shaped memory curve $M_{peak} = M_{init} + s \cdot F + K \cdot b$ and that minimum occurs at $F_{opt} = \sqrt{R \cdot D \cdot b / s}$.
### Configuration
| Parameter | Value |
|-----------|-------|
| D (devices) | 20 |
| R (rows/device) | 2,000,000 |
| n_field | 8 DOUBLE |
| s_data | 72 bytes/row |
| b | 928 bytes/device/flush |
| **F_opt (formula)** | **22,705 rows** |
| **M_min (formula)** | **4.0 MB** |
### Results: Sweeping F from F_opt/8 to F_opt*8
| F/F_opt | F | Peak (MB) | Trend |
|---------|--------|----------|-------|
| 0.12 | 2,838 | **17.0** | M_meta dominates (too many flushes) |
| 0.25 | 5,676 | 8.7 | |
| 0.50 | 11,352 | 4.6 | |
| **1.00** | **22,705** | **3.3** | **Near minimum** |
| **1.41** | **32,109** | **3.1** | **Minimum** |
| 2.00 | 45,410 | 3.4 | |
| 4.00 | 90,820 | 5.5 | |
| 8.00 | 181,640 | **10.7** | M_data dominates (too few flushes) |
**The minimum occurs at F/F_opt = 1.0~1.4**, confirming the EOQ model prediction.
- Left side (F < F_opt): M_meta dominates, peak grows as 1/F.
- Right side (F > F_opt): M_data dominates, peak grows as F.
- The U-shape is symmetric on log scale, matching the EOQ theory.
### Formula Constant Deviation Analysis
The measured minimum at F/F_opt ≈ 1.4 rather than exactly 1.0 is explained by the formula constants' deviation from actual values:
| Constant | Formula Value | Measured Ratio (actual/formula) | Source of deviation |
|----------|--------------|--------------------------------|---------------------|
| $s$ (data/row) | 72 bytes | **×0.92** (formula overestimates 8%) | Page header + statistics overhead amortized over rows |
| $b$ (meta/flush) | 928 bytes | **×1.36** (formula underestimates 36%) | Object headers, pointer overhead, allocator alignment |
Per-F detailed measurements:
| F/F_opt | F | s ratio | b ratio | Peak (MB) |
|---------|-------|---------|---------|-----------|
| 0.12 | 2,838 | 1.027 | 1.347 | 16.95 |
| 0.50 | 11,352 | 0.761 | 1.353 | 4.58 |
| **1.00** | **22,705** | **0.943** | **1.361** | **3.28** |
| **1.41** | **32,109** | **0.926** | **1.367** | **3.13** |
| 2.00 | 45,410 | 0.908 | 1.376 | 3.36 |
| 8.00 | 181,640 | 0.856 | 1.471 | 10.67 |
Correcting for the actual constants:
$$F_{opt,corrected} = \sqrt{\frac{b_{actual}}{s_{actual}}} \times F_{opt} = \sqrt{\frac{1.36}{0.92}} \times F_{opt} \approx 1.22 \times F_{opt}$$
The corrected theoretical optimum (1.22×) is close to the measured minimum (1.4×). The remaining gap is within the flat bottom of the U-curve: **peak memory between F/F_opt = 1.0 and 2.0 differs by only 6%** (3.13 MB vs 3.28 MB at 1.0×, 3.36 MB at 2.0×), making the exact position of the minimum practically insignificant.
**Key takeaway**: The formula parameter $b$ underestimates actual meta overhead by ~35% due to memory allocator alignment and object management overhead beyond the serialization size. This causes the actual optimal $F$ to shift rightward (fewer flushes needed). Since the formula yields a conservative $F_{opt}$ (slightly smaller than actual optimal), it is safe for pre-planning: the system flushes slightly more often than necessary, which is preferable to flushing too infrequently and risking memory overflow.
### Figures
- **F3_eoq_ushape.pdf**: U-shape curve with measured peak, formula curve, and M_data/M_meta decomposition.
---
## E3-4: Memory Budget Compliance (Two-Level Control)
**Goal**: Validate that MemConstrainedWriter keeps memory within budget and triggers file rotation when meta accumulates.
### Configuration
- Schema: 8 mixed fields (PLAIN+UNCOMPRESSED), 10 devices, **50M total rows**
- Timestamp: PLAIN + UNCOMPRESSED (global config override)
- Control: Direct monitoring (calculate_mem_size_for_all_group + calculate_meta_mem_size)
- Budget split: 50/50 between data and meta
- batch_cap: 4096 rows (to reduce overshoot at data_budget boundary)
### Results
| M_limit | peak_total | Within budget? | Flushes | Rotations | Files | Throughput |
|---------|-----------|----------------|---------|-----------|-------|------------|
| **2 MB** | **1.16 MB** | **OK** | 2441 | **5** | **6** | 1.77 M/s |
| 4 MB | 2.71 MB | OK | 918 | 0 | 1 | 1.69 M/s |
| 8 MB | 4.14 MB | OK | 409 | 0 | 1 | 1.72 M/s |
| 16 MB | 7.90 MB | OK | 194 | 0 | 1 | 1.94 M/s |
| 32 MB | 15.79 MB | OK | 95 | 0 | 1 | 2.01 M/s |
### Key Observations
1. **Memory constraint satisfied**: peak_total never exceeds M_limit in all configurations. The two-level control (flush for M_data + rotation for M_meta) works correctly.
2. **File rotation triggered at 2 MB**: With only 0.6 MB meta budget, the accumulated meta (~1.3 KB/flush) triggers rotation after approximately 420 flushes per file. 50M rows across 6 files = ~8.3M rows/file, demonstrating continuous writing across file boundaries.
3. **Data budget overshoot**: At 2 and 4 MB, peak_data slightly exceeds data_budget (1.2~1.3x) because flush is checked after each batch write (4096 rows ≈ 230 KB per batch). This batch-granularity overshoot is bounded and does not cause total memory to exceed M_limit since the meta budget absorbs the slack.
4. **Throughput vs budget**: Throughput degrades slightly at very small budgets (1.77 M/s at 2 MB vs 2.01 M/s at 32 MB) due to frequent flush I/O overhead. The degradation is modest (~12%) even with a 16x budget reduction.
5. **Flush count scales inversely**: Flush count approximately halves when budget doubles (2441 → 918 → 409 → 194 → 95), consistent with $F \propto M_{avail}$.
### Figures
- **F3_write_budget.pdf**: Bar chart (throughput) + line (flush count) vs memory budget.
---
## Key Conclusions
1. **Formula accuracy (PLAIN+UNCOMPRESSED)**: With no encoding/compression and aligned timestamp, Estimate API matches formula within ~8%. Remaining gap comes from page headers and statistics overhead.
2. **Formula as conservative upper bound (with encoding)**: With SNAPPY+TS_2DIFF, formula overestimates $M_{data}$ by ~50%, which is by design. Direct monitoring provides exact runtime control. The thesis's two-tier design (formula for pre-planning, direct monitoring for runtime) is validated.
3. **EOQ U-shape confirmed**: The measured peak memory curve matches the theoretical U-shape. Minimum occurs at F/F_opt ≈ 1.0~1.4, close to the formula prediction. The shift is explained by $b$ being underestimated by ~35% (allocator alignment and object overhead beyond serialization size). After correcting $b$, the theoretical optimum is at 1.22× F_opt, consistent with the measurement.
4. **Flat optimum region**: Peak memory between F/F_opt = 1.0 and 2.0 differs by only 6%, meaning the exact value of F has little practical impact near the optimum. The formula's conservative F_opt (slightly smaller than actual) is safe: the system flushes slightly more often than necessary, avoiding memory overflow.
5. **Two-level control works**: Direct monitoring achieves stable throughput (~2.0 M rows/s) across memory budgets (8~128 MB). Flush frequency inversely proportional to budget ($F \propto M_{avail}$). No file rotation triggered at 20M rows.
6. **Aligned mode formula**: For aligned tables, $s_{data} = 8 + \sum \text{sizeof}(\text{field\_type}_j)$ (shared timestamp), differing from non-aligned $s_{data} = \sum (8 + \text{sizeof}(\text{field\_type}_j))$ by ~2x in the timestamp component.
7. **Three layers of memory measurement**: Formula ($s \times F$, upper bound) > Estimate API (encoded actual, for runtime control) > formula with actual encoding. ModStat allocation (includes ByteStream page granularity at 64 KB) > all of the above. Each layer serves a different purpose.