Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upFaster dict implementation by improving code generation #879
Comments
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
process-bot
Jun 24, 2017
Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!
Here is what to expect next, and if anyone wants to comment, keep these things in mind.
process-bot
commented
Jun 24, 2017
|
Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it! Here is what to expect next, and if anyone wants to comment, keep these things in mind. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
eeue56
Jun 24, 2017
Contributor
Switch ordering really matters, which surprised me. In the case of Dict.get it matters a lot as the EQ case is only triggered once, if at all, while LT and GT cases are triggered "all the time".
Loops not optimized properly, for instance because of complex loop-logic, can hinder other optimizations. Changing the switch ordering so GT comes right after LT, without removing labels and continue-statements, have no effect. Only when removing the label and continue statements is performance similar.
This is easy to do, right now -- the last case in a case..of is always used as default. Simply changing the ordering in the Elm file will make that work: https://github.com/elm-lang/core/blob/master/src/Dict.elm#L107
This is easy to do, right now -- the last case in a case..of is always used as |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Skinney
Jun 24, 2017
Contributor
I know. But as mentioned, it has no effect on performance without removing the label and continue-statements first. I think it might be because it prevents optimizing the switch statement or something :/
|
I know. But as mentioned, it has no effect on performance without removing the label and |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
eeue56
Jun 24, 2017
Contributor
We discussed on Slack and weren't able to find/think of a place where continue...label would be required, based on the current compiler. Even edgecases like a TCO function being defined inside the body of another TCO function do not need the continue. V8 seems to do no form of optimisation for continue/label jumps like this, as far as I can tell from the code. This might be relevant too.
|
We discussed on Slack and weren't able to find/think of a place where |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Skinney
Jun 25, 2017
Contributor
I just realized that I posted this in the wrong repo, this was supposed to go to the elm-lang/compiler repo :(
I'll try to move it.
|
I just realized that I posted this in the wrong repo, this was supposed to go to the elm-lang/compiler repo :( I'll try to move it. |
Skinney commentedJun 24, 2017
So I ran a simple benchmark, and got some interesting results. Here is the benchmark:
And here are the results:
Retrieving the last/highest/greatest element in a dictionary is slower than retrieving the first/lowest/least. This seemed odd, so I did some investigating. I'm not going to list up everything I tried, but those that made a difference.
Every change I did was made to the compiled javascript of
Dict.get. It originally looks like this:If one removes the
continue get;statements of that code, as well as the get label above the while-statement, this is the result:That's a hefty performance improvement for the "first/lowest/least" case. To improve performance for "last/greatest/highest", one needs to change the order of the switch statements, so that the function now looks like this:
Then you get the following performance:
I also tried removing the label and
continue-statements from other recursive functions likeList.foldland (my implementation of)Array.get, but saw no performance difference.A couple of takeways:
break-statement from theinitializeFromListfunction in the new Array implementation confirms this, as performance increased 2-3 times. In other cases, it would simply reduce code size.Dict.getit matters a lot as theEQcase is only triggered once, if at all, whileLTandGTcases are triggered "all the time".GTcomes right afterLT, without removing labels andcontinue-statements, have no effect. Only when removing the label andcontinuestatements is performance similar.I don't know how difficult this is to implement, but it seems worthwhile :)
Also, it would be nice if someone could confirm my results. Here is a repo which makes it easy to get started: https://github.com/Skinney/elm-dict-exploration