UP | HOME

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.