tlopex commented on PR #806:
URL: https://github.com/apache/tvm-ffi/pull/806#issuecomment-5755851378

   Benchmark comparison between C++ and Rust.
   
   Measured on an AMD Threadripper 3970X, pinned to one CPU core. Rust uses 
1.97.1 with release optimization and thin LTO; C++ uses Clang 18.1.3 with 
`-O3`. Both use the same native runtime. Results are medians of seven 
measurements, reported per complete traversal; lower is better. Input 
construction, warmup, and output validation are outside the timer.
   
   For an array of **4,096 integers**, with no additional owner retained, times 
are in **µs/traversal**:
   
   | Operation | C++ | Rust |
   | --- | ---: | ---: |
   | Visit | 12.50 | 9.26 |
   | Walk, pre-order | 41.41 | 8.97 |
   | Walk, post-order | 42.11 | 8.87 |
   | Map, pre-order, increment integers | 95.24 | 13.37 |
   | Map, post-order, increment integers | 134.55 | 13.41 |
   | Mutate, increment integers | 27.83 | 9.87 |
   | Mutate, return `Unchanged` | 11.56 | 9.41 |
   | Walk, pre-order + context policy | 42.20 | 31.32 |
   | Mutate, increment integers + context policy | 27.73 | 9.89 |
   
   What these operations measure:
   
   - **Visit:** integer callbacks record values and counts; unmatched 
containers use default recursion. Visit callbacks can control recursion 
themselves.
   - **Walk:** the engine schedules callbacks before children (pre-order) or 
after children (post-order). These callbacks only collect statistics.
   - **Map:** the engine schedules callbacks and recursion; each integer 
callback returns `value + 1`. Both traversal orders produce the same values in 
this workload.
   - **Mutate:** callbacks control rewriting and can request default recursion. 
Here, integer callbacks return `value + 1`, while unmatched containers descend 
automatically. Uniquely owned arrays are eligible for in-place reuse.
   - **`Unchanged`:** integer callbacks still run but return the no-replacement 
marker. This measures traversal and result handling when nothing changes.
   - **Context policy:** increments shared depth before default descent and 
restores it afterward; callbacks also record depth. It adds context management 
without changing the rewrite rules.
   
   Other input shapes and ownership cases, also in **µs/traversal**. These rows 
have **no context policy**:
   
   | Workload | C++ | Rust |
   | --- | ---: | ---: |
   | Shared 4,096-element array, mutate | 61.22 | 66.59 |
   | Reflected binary tree, mutate | 105.83 | 109.40 |
   | Reflected binary tree, closure callbacks returning `Unchanged` | 71.98 | 
80.33 |
   | Shared-node DAG, mutate | 180.34 | 200.32 |
   
   - **Shared array:** retain another owning reference during each traversal, 
exercising copying instead of modifying the shared input in place.
   - **Reflected binary tree:** 1,023 objects with registered `left`/`right` 
fields and 1,024 integer leaves, exercising field reflection. The closure row 
uses Rust closure callbacks; the other rows above use macro-generated Rust 
dispatchers.
   - **Shared-node DAG:** an array holds 4,096 references to 512 distinct DAG 
objects, each referenced eight times. Each object has an integer field. This 
exercises identity remapping and repeated references. Changed cases increment 
integers by one.
   
   The larger workload simplifies **128 index expressions with four nested 
div/mod expansion levels**. It includes rules such as `(x // 8) * 8 + x % 8 → 
x`, nested division folding, constant folding, and removing `+ 0` / `* 1`. 
Times are in **ms/traversal**:
   
   | Operation | C++ | Rust |
   | --- | ---: | ---: |
   | Map simplification | 3.157 | 2.408 |
   | Map simplification + context policy | 3.169 | 3.963 |
   | Mutate simplification | 3.056 | 3.162 |
   | Mutate simplification + context policy | 3.053 | 4.155 |
   
   Map uses post-order callbacks. Mutate callbacks explicitly descend first, 
then apply the same rewrite rules. The expression policy tracks 
binary-expression nesting. Validation checks eight variable assignments, the 
resulting expression structure, and callback/rewrite counters.
   
   The complete run covered **186 configurations and 4,158 measurements**, 
including smaller/nested arrays, closures, shared expression DAGs, and patterns 
that should not simplify. Output checks and callback/state counters matched 
across implementations. The measurement total includes an additional GCC 
comparison for expression cases; the tables above use Clang.
   
   These are local workload results on a shared machine. Rust is faster in the 
array cases without an additional owner, while shared/reflected mutation 
remains slower. Expression mutation with a policy takes about 36% longer than 
C++.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to