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 upFix List.take causing stack overflow #659
Conversation
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
process-bot
Jul 12, 2016
Thanks for the pull request! 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
Jul 12, 2016
|
Thanks for the pull request! 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.
evancz
Jul 12, 2016
Member
I believe this version costs O(n) in all cases. So if I say List.take 2 [1..1000] it will traverse 1000 elements. I believe there is a way to write this tail recursively that is O(k) where k is the amount you want to take. That said, this will necessarily reverse the list, meaning you will have to do a reverse on it, increasing allocation.
It is not clear to me that this is a win. This problem exists in OCaml and SML as well. There is no real trick to fix it besides letting the stack be bigger or using a more appropriate data structure for your data set.
|
I believe this version costs O(n) in all cases. So if I say It is not clear to me that this is a win. This problem exists in OCaml and SML as well. There is no real trick to fix it besides letting the stack be bigger or using a more appropriate data structure for your data set. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
nphollon
Jul 12, 2016
Contributor
So I am building a collision detection library that uses a bunch of recursive algorithms. When I started encountering stack overflows, I wasn't surprised; I figured I'd probably done something dumb. I spent a couple days tracking down the problem (as chronicled on elm-discuss). Finding out that the issue was in the standard library was pretty annoying.
I think its a big win to get rid of the stack overflow. When somebody is debugging a runtime error, elm-lang/core is going to be very far down on the list of suspects. Even if this isn't encountered that often, its going to be a time sink.
Any of these I think would be better than the status quo:
- replacing
List.takewith a tail-recursive version that is O(k) but reverses the order of the items. - coupling the above function with a list reversal.
- removing
List.take - adding a note to the documentation that
List.takeonly works for small values ofn.
|
So I am building a collision detection library that uses a bunch of recursive algorithms. When I started encountering stack overflows, I wasn't surprised; I figured I'd probably done something dumb. I spent a couple days tracking down the problem (as chronicled on elm-discuss). Finding out that the issue was in the standard library was pretty annoying. I think its a big win to get rid of the stack overflow. When somebody is debugging a runtime error, elm-lang/core is going to be very far down on the list of suspects. Even if this isn't encountered that often, its going to be a time sink. Any of these I think would be better than the status quo:
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
evancz
Jul 13, 2016
Member
I think Yaron outlines a very attractive way to approach this kind of problem here. I read it ages ago, but only really remembered the "recursion unrolling" trick. I was going to suggest using this trick to make the stack deeper by 5x, but the post outlines a way better solution than that! If we can show the numbers work nicely in Elm, I think it makes sense to go in this direction across the board.
What do you think of that approach? Would you be willing to do some profiling to see if you can make the performance / memory tradeoffs work out favorably?
|
I think Yaron outlines a very attractive way to approach this kind of problem here. I read it ages ago, but only really remembered the "recursion unrolling" trick. I was going to suggest using this trick to make the stack deeper by 5x, but the post outlines a way better solution than that! If we can show the numbers work nicely in Elm, I think it makes sense to go in this direction across the board. What do you think of that approach? Would you be willing to do some profiling to see if you can make the performance / memory tradeoffs work out favorably? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
nphollon
Jul 13, 2016
Contributor
I think that approach sounds really nice. Gives a fast result for the most common use cases, and switches to an "at least it works" implementation for large lists. I will do some profiling and get back to you.
|
I think that approach sounds really nice. Gives a fast result for the most common use cases, and switches to an "at least it works" implementation for large lists. I will do some profiling and get back to you. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
evancz
Jul 13, 2016
Member
Cool, I am going to close this for now. New issue or PR when you find out more! Fine to continue discussion here if necessary though.
|
Cool, I am going to close this for now. New issue or PR when you find out more! Fine to continue discussion here if necessary though. |
nphollon commentedJul 12, 2016
Fixes #601
List.takecauses a runtime error if we take approx. 4000-5000 items.I think this is because the recursive definition of
List.takedoes not get optimized by the compiler. I have rewritten it to usefoldrinstead of recursion.