Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible optimization: tree of closures #32

Open
cosmos72 opened this issue May 10, 2022 · 3 comments
Open

Possible optimization: tree of closures #32

cosmos72 opened this issue May 10, 2022 · 3 comments
Labels
enhancement New feature or request

Comments

@cosmos72
Copy link

cosmos72 commented May 10, 2022

Directly interpreting the abstract syntax tree is slow, as you point out.
And Go is not really well-suited to write bytecode interpreters.

An approach that works quite well in Go, as I found out for my Go interpreter gomacro, is to convert the abstract syntax tree to a tree of closures (lambdas) and then execute it.

@eigenhombre
Copy link
Owner

@cosmos72 thank you for the idea! gomacro looks cool.

Why is Go ... not really well-suited to write bytecode interpreters?

@cosmos72
Copy link
Author

cosmos72 commented May 11, 2022

Because it's a too high-level language:

  • there's no way to suggest the compiler which variables should be kept in registers,
  • there's no way to declare global register variables. See for example https://gcc.gnu.org/onlinedocs/gcc/Global-Register-Variables.html for C/C++ counterpart.
  • it does not have computed gotos. See for example https://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables
  • it does not have tail-call optimization. There is a known technique, where VM state is kept in registers which are passed around using tail calls. It requires that language ABI passes function arguments in registers (which Go added recently) and tail call optimization (which Go core team does not want to implement). Unluckily I cannot find any detailed description for it at the moment.

So the core loop of any bytecode interpreter will either:

  1. call a lot of small functions (up to one function per bytecode to be executed)
  2. or contain a huge switch, which is very difficult to optimize for any compiler, and register allocation will suffer a lot
    Both solutions cause a lot state being kept in memory and loaded/stored very often, instead of being kept in memory

Anyway, if you are start optimizing l1 heavily, you will soon notice that keeping everything wrapped in reflect.Value introduces a large overhead too.

@eigenhombre
Copy link
Owner

Interesting, thanks. However, for the (short-running) toy problems I've looked at so far l1 has been surprisingly fast so far. I will probably hold off on any optimizations until the language gets a little farther along, at least through macros. But it's nice to start thinking about how one might optimize things if/when that makes sense.

@eigenhombre eigenhombre added the enhancement New feature or request label Aug 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants