Tirc: a tiny multi-system calculator and my playground for rust
Table of Contents
Introduction (prologue)
Hi everyone, I will be documenting my journey developing Tirc and also documenting my setup to debug and develop rust (Maybe the setup will be moved to another entry in this blog!).
Overview
Read README file.
Setup
Criterion
I use this crate to do micro-benchmarks of my code.
Perf
I use this program to benchmark the final binary made by rust.
The big difference between perf and criterion is that the former allows me to benchmark rust code within rust code, and the latter allows to test the binary program as a whole.
Optimization
I have been spending most of my recent time optimizing the parser of tirc, it is a LA(N)LR parser.
No iterators in transform.rs
I tried to use only vectors in the transform phase of tirc but it looks like vectors are slower than iterators. This is due rust itself, vectors are converted to iterators in some situations (like \(for\) loops over vectors), and it looks like this is the case.
arbitrary-precision integers
Well… I had a lot of things to do (I still have them!) but I chose
to start adding arbitrary-precision integers to tirc
, which was
kinda trivial, or at least that was what I thought.
First and foremost, I wanted to implement a new arbitrary-precision integers library (for the sake of learning), I did not implement it due to time reason. So, I had to use an already existing library, there are a lot of libraries for this use case, and it was not trivial to chose one.
My options were: gmplib, MPFR, libbf. I ended up using libbf
, which
is… a good option?, if it was a good option or not is a matter that
should be discussed.
Why did I choose libbf? I just skimmed the source code of all my
options, libbf
looked like the smaller one, not the easiest one
though, but it also was the (and it still is) the less known library,
which means I could hone my skills reading someone else code and learn
how to make Rust binding for a C library.
Implementing rust bindings
During the development of the rust binding I found a lot of dilemmas in what was the best way to implement bindings, I even made two git branches, each one with different implementation. My biggest concern was "Should I make simple bindings and let the logic within rust or make complex binding and the logic within C?", the former let me learn more Rust's ffi, the later allows to avoid the complexity of Rust's ffi, save time, and use my knowledge in C. I took the later path, it helped to portability, or at least in theoretical way because it is not implemented in that way; and it allowed me to avoid complexity.
In the last paragraph I said I let all the logic run within C code for
portability's sake, It is represented in the big_int
structure. This
structure is the main reason why the program is not portable, but in
reality I do not need this structure since all the logic runs in C
code, the C code is already portable and I do not need to repeat the
structure in Rust and make it portable but… I repeat the structure
in rust, I thought it would be helpful to have this structure in rust
code since it could help me to debug, but it did not help me, I end up
debugging in C most of the time. Nonetheless, this decision opens up
an opportunity to easily add portability to the code. I could easily,
in theory, swap all the uses of big_int
to a C void pointer, this
would lead me to the deletion of the big_int
structure, which is my
current nuance for portability, but all of this is in theory, in
practice could be harder because since I need to comply with rustc
expectations.
Complains
My biggest problem with rust is cargo, I think cargo is dumb sometimes
and lacks flexibility, I still like build tools (like make or cmake)
more than cargo. While I was making my benches, with Criterion, I
found that cargo-criterion relies on cargo to compile benchmarks,
which was giving me problems. First, check the current state of tirc
and try to run cargo bench
, errors are raised due to cargo and how
it tries to solve dependencies; this does not happen when cargo test
or cargo build
is run, which means it is related to cargo, it turns
out that cargo bench
adds the flag --cfg test
or --test
to
rustc
which is why the errors are raised.
Final words (epilogue)
I find particularly difficult to translate C code to Rust bindings,
however, I think is kinda easy to interop C code within rust, without
complex binding, which means keeping the C logic within C. This does
not mean this is the best approach, tirc
is not enjoying rust
features at all, and that could be a problem for someone (not me, at
least for now). Making rust bindings may be hard, but there are tools
like bindgen that allows to automatically translate C headers to rust
bindings.
I might not continue developing tirc
, I already tested what I wanted
to test with rust and with this project. But, who knows? I could come
back and start hacking tirc
again.
Resources
Todo
Check todo.org file.