path_stack;
string key_stack;
bool start_object(size_t) {
path_stack.push_back(key_stack);
// Detect module boundary
if (path_stack.size() == 2
&& path_stack[0] == "modules") {
// Track first module
}
return true;
}
```
]
---
### ๐ง C++: Event Flow โ Building the Path
.pull-left[
.mermaid[
sequenceDiagram
participant P as Parser
participant H as Handler
participant S as State
P->>H: start_object()
H->>S: push "root"
P->>H: key("modules")
H->>S: key_stack = "modules"
P->>H: start_object(1)
H->>S: push "modules" โ ["modules"]
P->>H: key("top")
H->>S: key_stack = "top"
P->>H: start_object(3)
H->>S: push "top" โ ["modules","top"]
P->>H: key("ports")
H->>S: key_stack = "ports"
Note over H: Path = join(stack, ".")
]
]
.pull-right[
**Key insight:** The stack builds the same `.`-separated path that ijson provides natively.
]
---
### ๐ง C++: Net ID Collection
.pull-left[
**When a number arrives:**
```cpp
bool number_integer(int64_t val) {
if (val < 0) return true; // skip VCC/GND
auto p = path();
auto uval = uint32_t(val);
if (p.find(".connections.") != npos
&& in_array) {
cell_edges.push_back({cid, uval});
all_net_ids.insert(uval);
}
else if (p.find(".ports.") != npos
&& p.find(".bits") != npos) {
all_net_ids.insert(uval);
port_nets[current_port].insert(uval);
}
// ... netnames handling
}
```
]
.pull-right[
**Stopping at first module:**
```cpp
bool end_object() {
path_stack.pop_back();
if (found_first_module
&& path_stack.size() == 1
&& path_stack[0] == "modules") {
return false; // ๐ stop parsing
}
return true;
}
```
Returning `false` from any SAX callback
tells nlohmann to stop โ no more bytes read.
]
---
count: false
class: nord-light, middle, center
# โก Benchmarks
---
### โก Python: pytest-benchmark Results
**50 iterations** each, Yosys netlist files:
.pull-left[
.mermaid[
xychart-beta
title "Python DOM vs SAX (ms)"
x-axis ["yosys_and2\n(small)", "sphere\n(482 KB)", "sphere3hopf\n(528 KB)"]
y-axis "Mean (ms)" 0 --> 90
bar [0.38, 31.5, 59.2]
bar [0.39, 19.3, 88.3]
]
]
.pull-right[
| File | DOM (ms) | SAX (ms) | Winner |
|------|----------|----------|--------|
| `yosys_and2.json` | 0.38 | 0.39 | โ Tie |
| `sphere_netlist.json` | 31.5 | **19.3** | ๐ SAX (1.6ร) |
| `sphere3hopf` | **59.2** | 88.3 | ๐ DOM (1.5ร) |
]
---
### โก C++: std::chrono Benchmark
**50 iterations** each, Release build:
.pull-left[
| File | DOM (ms) | SAX (ms) | Winner |
|------|----------|----------|--------|
| `yosys_and2.json` | 0.38 | 0.39 | โ Tie |
| `sphere_netlist.json` | 24.1 | **4.8** | ๐ง SAX (5.0ร) |
| `sphere3hopf` | 30.1 | **18.9** | ๐ง SAX (1.6ร) |
]
.pull-right[
### ๐ Python
- ijson pure-Python event loop dominates
- SAX wins on smaller files
- DOM wins on larger (Python obj overhead)
### ๐ง C++
- nlohmann SAX is **native-speed**
- DOM tree allocation is expensive
- SAX wins **consistently** (1.6โ5.0ร)
]
---
### ๐ Cross-Language Comparison
.mermaid[
graph TD
subgraph Python["๐ Python"]
PD["DOM: json.load()"]
PS["SAX: ijson.parse()"]
end
subgraph Cpp["๐ง C++"]
CD["DOM: json::parse()"]
CS["SAX: json::sax_parse()"]
end
PD -->|"31 ms"| R1["Result"]
PS -->|"19 ms"| R1
CD -->|"24 ms"| R2["Result"]
CS -->|"5 ms"| R2
R1 -.->|"SAX 1.6x"| Note1["Mixed results"]
R2 -.->|"SAX 5.0x"| Note2["Consistent win"]
style CS fill:#A3BE8C,stroke:#81A1C1
style PS fill:#EBCB8B,stroke:#81A1C1
]
**Takeaway:** SAX shines when the execution environment is close to the metal.
Interpreted overhead ๐ buys DOM time; native speed ๐ง makes SAX dominate.
---
count: false
class: nord-light, middle, center
# ๐ก Insights
---
### ๐ก Does the Schema Help?
.pull-left[
#### โ
What the schema told us
.font-sm[
| Insight | Implementation decision |
|---------|------------------------|
| `connections: [int or string]` | Filter `is_number_integer()` |
| `ports`, `cells` are required | Know which sub-trees to watch |
| `attributes` is optional | No handlers needed |
| Top-level: `creator` + `modules` | Skip `creator` key |
| `netnames` may be absent | Conditional handling |
]
]
.pull-right[
#### โ What schema didn't help
- ๐ง State machine design
- ๐งฉ Callback sequencing
- ๐ฏ First-module-only isolation
- ๐งฎ Graph assembly algorithm
**Schema = design document ๐**
Not a runtime dependency.
Used to **understand the target**, not to validate input.
]
---
### ๐ Architecture Summary
.mermaid[
flowchart LR
subgraph Input["๐ Input"]
J["Yosys JSON File"]
end
subgraph Sax["โก SAX Parser"]
P["Event Stream"]
S["State Machine"]
C["Collected Data"]
end
subgraph Build["๐ Assembly"]
G["Bipartite Graph"]
N["Netlist Object"]
end
J -->|"read_yosys_json_sax()"| P
P --> S
S --> C
C --> G
G --> N
style Sax fill:#5E81AC,stroke:#88C0D0
style Build fill:#A3BE8C,stroke:#81A1C1
]
Two phases: **Stream** (SAX events โ raw data) then **Assemble** (data โ graph).
---
### ๐ฏ Key Takeaways
.pull-left[
#### 1๏ธโฃ **SAX โ always faster**
- Python: interpreted overhead hurts SAX
- C++: native execution makes SAX shine
- **File size matters:** crossover points exist
#### 2๏ธโฃ **Schema as specification**
- Schema โ design decisions, not runtime
- Understanding the format = correct parser
]
.pull-right[
#### 3๏ธโฃ **Dual-language pattern**
- Same algorithm, two languages
- Different trade-offs exposed
- **Benchmark both** before choosing
#### 4๏ธโฃ **State machine is the core**
- Path stack = context
- Callbacks = event handlers
- Assembly = post-processing
]
---
### ๐ Files Changed โ Summary
.font-sm[
| Language | Files | Key addition |
|----------|-------|-------------|
| ๐ Python | `netlist.py` | `read_yosys_json_sax()` (~110 lines) |
| ๐ Python | `test_yosys.py` | 7 new test cases |
| ๐ Python | `benches/test_bm_yosys.py` | 4 benchmark tests |
| ๐ Python | `setup.cfg` | `ijson` dependency |
| ๐ง C++ | `readwrite.hpp/.cpp` | `YosysSaxHandler` + function |
| ๐ง C++ | `test_readwrite.cpp` | 2 new test cases |
| ๐ง C++ | `bench/` | **New** benchmark program |
| ๐ง C++ | `xmake.lua` | `bench_yosys` target |
]
---
### ๐ Links & Resources
| Resource | URL |
|----------|-----|
| Yosys | https://github.com/YosysHQ/yosys |
| ijson | https://pypi.org/project/ijson/ |
| nlohmann/json SAX | https://json.nlohmann.me/features/sax_interface/ |
| netlistx (Python) | https://github.com/luk036/netlistx |
| netlistx-cpp | https://github.com/luk036/netlistx-cpp |
| Remark.js | https://remarkjs.com/ |
---
count: false
class: nord-dark, middle, center
# Q&A ๐ค
### JSON SAX Parsing for Yosys Netlists
Questions? Comments? Ideas?
@luk036 ๐จโ๐ป