HackerTrans
TopNewTrendsCommentsPastAskShowJobs

ergeysay

no profile record

Submissions

Safe Made Easy Pt.2: Don't Fear the Ref

ergeysay.github.io
6 points·by ergeysay·letzten Monat·0 comments

Safe Made Easy Pt.1: Single Ownership Is (Not) Optional

ergeysay.github.io
9 points·by ergeysay·vor 2 Monaten·0 comments

Optimizing Interpreters: Fusion

ergeysay.github.io
7 points·by ergeysay·vor 2 Jahren·0 comments

Dagor Engine: the game engine powering War Thunder

github.com
6 points·by ergeysay·vor 3 Jahren·1 comments

comments

ergeysay
·vor 2 Jahren·discuss
I found sokuza-kanren [0] to be a wonderful introduction. It starts with the absolute basics and builds into a minimal logic system through a series of well-documented steps.

[0] https://github.com/miniKanren/sokuza-kanren
ergeysay
·vor 2 Jahren·discuss
> loops like this are easy for compilers to analyze, in a way that makes them not representative of real code

Which makes it a perfectly fine benchmark to measure whether a particular compiler implements these optimisations. The benchmark also highlights fine implementation details. I did not know about Dart's interrupt checks, for instance.

I see these microbenchmarks as genuinely useful, as I can analyse them, the logic behind them, and apply results in interpreter design. Consider [0] for example. Any sane compiler would do this kind of optimisation, but I've seen only one production interpreter (daScript) doing it.

[0] https://ergeysay.github.io/optimising-interpreters-fusion.ht...
ergeysay
·vor 2 Jahren·discuss
As you said, OpenTelemetry and friends can help. I had great success with these.

I am curious, what were implementation issues you have encountered?
ergeysay
·vor 3 Jahren·discuss
Curly braces are optional and equivalent to indentation but you have to use semicolons if you use curly braces.

The docs are sorely lacking, but there is good test coverage though, so you may refer to tests for examples: https://github.com/GaijinEntertainment/daScript/blob/master/...
ergeysay
·vor 3 Jahren·discuss
Actually, when I first saw this, I set out to investigate. I managed to reproduce the same performance in an isolated environment and I has been sitting on a post on how this works for quite a time. Hopefully, I will have time to finish it this weekend.

Yes, it is that fast.

Long story short, the trick is a completely novel way of implementing interpreters.

It is tree-based and I should stress that the tree is not an AST tree but a highly specialized tree-based representation produced from the AST tree. A node is basically equivalent to an "instruction" in a bytecode-based interpreter. Usual arguments against tree-based interpreters do not apply: there is no pointer-chasing, since the nodes sit tightly in the CPU cache. Dispatch overhead is just a virtual function call so should be comparable to a computed goto-based interpreter.

The nodes operate on machine-native primitive types and the values are passed around between nodes in registers most of the time - oftentimes the interpeter doesn't even need to access the main memory. There is no encoding or decoding of values and no typechecking during evaluation. The interpreter even goes at lengths to avoid conversion of values in most paths, i.e. if your code operates on uint32_ts, they will be passed around as such up until a node that requires them to be cast, effectively creating typed execution paths.

Before the interpreter there is also a "compilation" phase which performs a ton of optimizations, of which I guess the most interesting is the node fusion. As you may guess, it replaces smaller nodes with larger and more specialized ones. In my tests I implemented it for a naive AST-walker-like intepreter for a 3x performance improvement.

I can go on but I better finish the post.

I would also add that writing an interpreter in this style is far simpler than writing even a simplest JIT, but it does impose certain requirements on the language itself.

All in all, the design is nothing short of genius.
ergeysay
·vor 3 Jahren·discuss
Could be dynamic frequency scaling. To minimize the impact of it when benchmarking one can pin the process to a single core and warm it up before running the benchmark.