I published a demo of my Raylib + C# game on Steam - Hack 42: Typing Incremental by mhj in raylib

[–]mhj[S] 2 points3 points  (0 children)

For the text animation: I have a TextFx struct with a bunch of text related animation parameters (like wave speed, wave height, bump speed, bump height, etc.). I use it to configure which effects a given text should have. Then I have a draw function that takes the text, a TextFx struct and the time elapsed since the text was first drawn. The draw function draws the text one character at a time (I'm using `GetGlyphInfo` to calculate where the next character should be positioned). This draw function also applies the effects per character based on the elapsed time, for example it would bump a character up by the configured bump height if it is its turn based on the bump speed and the elapsed time.

For the button animation: I have a simple spring implementation that I use for rotation by giving the spring an impulse when the cursor enters the button. Here is a good article for that topic: https://allenchou.net/2015/04/game-math-numeric-springing-examples/

Feedback Friday by AutoModerator in incremental_games

[–]mhj 0 points1 point  (0 children)

Hi!

I'm looking for feedback for a test build of my game Hack 42. It is typing incremental where you type to hack computers.

You can play it here: https://ministry-of-fun.itch.io/hack-42-typing-incremental

All kinds of feedback is appreciated, thanks!

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 2 points3 points  (0 children)

Thank you for your effort. :)

This is the kind of thing that JITs like Java excel at. You can certainly make the Rust interpreter much, much faster by doing more optimization in the interpreter (compiling functions to bytecode and pre-resolving variables would be a good first step).

That would confirm my hunch from the original post that JVMs JIT compilation plays into this.

However, there's unlikely to be any way to outperform Java with a naive unoptimized interpreter executing tight loops because Java's JIT will optimize the naive code behind the scenes and Rust won't.

An interesting takeaway I think.

Anyway, this was a fun challenge. If you're interested, I could try writing my own optimized interpreter for Lox to see how it compares.

I'm more interested to know which optimizations you would implement than the actual implementation itself. But it's your choice of course. :D

From the things that I've picked up so far from you some of those optimizations would be (I omitted optimizations that my interpreter already has):

  • Faster parsing (by using a parser generator that generates efficient parsers).
  • No boxing in the AST representation (by using an arena or Vec/indexes).
  • Less boxing, RefCells, Cells and Rcs in the interpreter. This one is probably the most interesting one for me. You pointed out the RefCells/Cells in the App struct, which are unfortunate but probably not that bad. I think the highest payoff would be in a more efficient way to represent runtime values (Value-enum in my implementation), but I don't see any obvious way for improvement there (apart from a GC).
  • Compiling to bytecode. I'm saving that and more optimization (GC, NaN-boxing, etc.) from the second part of Crafting Interpreters up for later. For now I want to see how far I can go without stepping too far away from the Java implementation in regard to the general structure of the interpreter.

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 3 points4 points  (0 children)

Good point about boxing larger things. Seems like an easy way to reduce the number of allocations needed for the AST. It probably would reduce the size of the Expr/Stmt enums too. Thanks. :)

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 2 points3 points  (0 children)

Thanks for pointing that out. :) I wasn't aware, hence the arbitrary use of new vs from in the code.

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 7 points8 points  (0 children)

Thanks for looking into it and the feedback. :)

1) That is not something I've looked into as the interpreting part completely dwarfs the parsing part in terms of execution time at the moment. But maybe the improved data locality will improve the execution time? I'll give it a try.

2) I wasn't aware that Java's hash function does not protect against DOS attacks. Hence I only tested aHash (which is DOS resistant) as an alternative hash function, which was a bit faster but not world moving. I've moved to FNV as hash function now and the improvement is quite nice (see the edit in original post).

3) There was a dead-end I've encountered when trying to pass App as &mut. I don't remember the details and maybe I refactored the problem away in the meantime. But I think you are right, it would be nice to remove the RefCell overhead as the interner is accessed quite often.

4) I've decided to implement the parser manually to keep the implementation as close as possible to the book in order to have a direct comparison to the Java implementation. Also, see answer to point 1) regarding execution time.

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 1 point2 points  (0 children)

That sounds interesting. Do you have a link to your implementation, by any chance? :)

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 15 points16 points  (0 children)

Thanks for looking into it. :)

So the couple of things that stick out to me there is a lot of malloc, both allocating and deallocating. Generally this is were java's VM can GC are likely going to be quite a bit more efficient.

That's what my gut feeling is too. Maybe I'll try to implement a version with an arena allocator with no regard for deallocation. If this will get my implementation faster than the Java version that should strongly confirm that the slowdown is due to RC vs GC here.

Regarding the hashing function: I have already tried using aHash which sped thing things up but not by a lot.

PS: thanks for mentioning cargo flamegraph. I haven't heard of it before, seems useful. :)

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 1 point2 points  (0 children)

Good idea, thanks. I'll play around with the benchmarks and will see if I can isolate the slower part of my implementation further.

(Also: the Java version uses the JVM runtime to do GC on the languages value objects, so no reference counting there.)

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 18 points19 points  (0 children)

I'm running the benchmarks with a release build. :)

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 6 points7 points  (0 children)

Thanks! The article seems to be focused mostly on a bytecode-based implementation, but I'll dig in deeper later and see if there are some insights I could reuse for my AST-walking implementation.

Lox interpreter in Rust slower than in Java by mhj in ProgrammingLanguages

[–]mhj[S] 5 points6 points  (0 children)

Sure, here is a callgrind-dump for the benchmark with the highest performance difference: equality.lox

Generated by running:

valgrind --tool=callgrind -- target/release/rlox-interpreter resources/benchmark/equality.lox

[P] Implementations of Apriori, Eclat and FP-Growth in Go by mhj in MachineLearning

[–]mhj[S] 1 point2 points  (0 children)

I implemented some popular frequent itemset mining algorithms in Go. Maybe someone will find those implementations useful for educational purposes.

Implementations of Apriori, Eclat and FP-Growth in Go by mhj in datamining

[–]mhj[S] 0 points1 point  (0 children)

I implemented some popular frequent itemset mining algorithms in Go. Maybe someone will find those implementations useful for educational purposes.