https://codeberg.org/DanielvNiek/bench.zig
Would appreciate any feedback. This is the first zig I wrote from scratch, except for TrackingAllocator which i copied from the very popular zbench. Had to figure out allocators, io, clocks and file system interactions along the way. I feel confident that my implementation is solid but would appreciate any feedback (more on the complex zig topics mentioned above, than on the actual usefulness of this library)
6 Likes
what does your project do and how do you use it?
It lets you benchmark your functions (duration & memory allocations), something i do religiously as a Go developer in my 8-5 and would want to continue with on my Zig journey.
EDIT:
Refer to the test in root.zig for example usage. Basically writes benchmarking results to a file and even lets you test if your function performs within some limits, which is useful when changing logic to ensure not only it works (through other tests), but also that its performance did not degrade
thanks! this is (a subset of) the information i usually expect to find in the top post on a forum or in the README on a repository site, which is why i asked.
2 Likes
Hey, congrats on your first little project. Now you’ve got something to play with.
I’d be interested to know what language you’re coming from. I suspect, whatever it was, objects were your standard unit of structure. You seem to be repeating that style. You don’t need to in Zig.
Bench looks to me like it doesn’t need to be a data type. It’s really just a single function run(). I think it would probably make the code simpler if you moved what was in init()/deinit() into run(). You can then don’t have to sit with files open whilst you run your benchmark . You can open the file much more locally to where you use it. Then, because it will be getting big, refactor run() by pulling sections out into helper functions.
That will leave you with a much more procedural flow.
Benchmark looks to be more sensible as a type. It’s a collection of results with a function to write them out.
Other little ideas:
Benchmark should be called Result or something similar. The names Bench and Benchmark are too similar and I can see you getting confused where to put logic.
Rather than opening the file, run benchmark 1, write result, run benchmark 2, write result, …, close file. How about … Run all benchmarks and gather results, open file, write all results, close file.
You don’t need to have a new file for every struct. Yes, all files are structs, but not all structs are files. I would probably write this in 1 or 2 files max. Not 4.
Bench.log() shouldn’t exist. Put that code in the result type method.
1 Like
Valueable feedback thanks! I come from Go where I’ve got one foot in functional programming and the other in objects (structs). I actually had the run as a standalone function initially, but due to the nr of args (io, allocator, output_file_path, benchmark_name, function, function_args) and the fact that i wanted to open the file once and close it after running many benchmarks (need for deinit), I reached for a struct. But if i store the results and write once like you showed, i can solve the args issue with a simple single RunOptions struct; will think a bit on this. But even if i keep the Bench struct, I would change Benchmark to Result and move the log functionality as you suggested.
With regards to the file per struct, haha, again i actually had it all as one file, but something about the struct per file seemed very appealing to me; probably need a few more projects to really have an opinion on this.
2 Likes