-
Notifications
You must be signed in to change notification settings - Fork 372
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
Looks like replace
does unnecessary work
#1519
Comments
Using a mutable data structure for Hy code is a bit dangerous, but convenient in macros. If the compiler has to look at the same code object more than once, but mutates it during the compilation process, that could cause bugs. There's nothing stopping a macro from returning multiple references to the same Ideally, we'd be using immutable data structures like Clojure does, so we wouldn't have to worry about it. But relying on Pyrsistent or something would add another dependency. And if we used a simple singly-linked list like most other Lisps, we'd lose most of the nice list methods that Python programmers are used to. That means we need to be very careful about mutations in the compiler, and I'm already not certain if we've tested this well enough. |
I think making You shouldn't see any loops in the data structures, so the Python reference counter should be just fine. The mutation was a quick hack to see what would happen. For all I knew the everything would have outright blown up. |
One thing to note: compile times are pretty slow to begin with, so we'd have to be pretty careful using immutable data structures, and definitely not linked lists. |
Yeah, but immutable should be faster, not because immutable datastructures are faster, but because using pop to iterate through a list is slow:
I suspect making HyExpression an immutable tuple, and dealing with the occasional mutability when needed will perform a lot faster than constantly mutating lists. Some initial work I've done seems promising like it might improve compile times, but I haven't quite got Hy working fully yet. Where we need lookahead, we can built it into the iterator instead: https://github.com/erikrose/more-itertools/blob/master/more_itertools/more.py#L135 (we could always borrow the code too instead of adding another dependency) |
In fact, it created #1537, and probably a lot of other subtle bugs we haven't found yet. |
See #1406.
Performance claims need actual measurements. I don't know where the compiler bottlenecks are. We could try different approaches and profile, but changing the Hy model interface seems like a lot of work because it's a pretty deep change. Linked lists seem to work fine for other Lisps. But maybe they have some kind of background optimizations for that kind of thing. Something like the Clojure seq abstraction might be an option. We could back it with any iterable for performance and still treat it like an immutable linked list by caching the returned values. hylang/hyrule#32 Hy also has a cons. We'd be able to integrate that better with seq. But maybe inheriting from tuple is good enough. In macros, you'd usually use the quasiquote syntax anyway. But if you did want to construct code using a listcomp or something, you could use a normal list and still convert it afterwards to the tuple form using the |
Calling `replace` can trigger a call to `replace_hy_obj`, which will try to also wrap values inside a `HyObject`. However, this means when we try to recusively replace inside a nested object, like a `HyList`, we should try and capture these new wrapped objects. Hy still works if we don't, but it results in the object getting rewrapped multiple times during a compile. Closes hylang#1519
Calling `replace` can trigger a call to `replace_hy_obj`, which will try to also wrap values inside a `HyObject`. However, this means when we try to recusively replace inside a nested object, like a `HyList`, we should try and capture these new wrapped objects. Hy still works if we don't, but it results in the object getting rewrapped multiple times during a compile. Closes hylang#1519
Now it looks like we always use the return value of |
I think I'm seeing some unnecessary work going on:
HyList.replace
callsreplace_hy_obj
, which in turn canwrap_value
. But the result ofreplace_hy_obj
is discarded and thus, depending on how a particular expression is constructed, its possiblewrap_value
is evaluated 3 or 4 times on the same object.Fixing
HyList.replace
so it instead self mutates:Wrapping only happens once now. But this seems to have some consequences with using iterables in unquote. The following expression now does this:
instead of
(which can't be compiled: TypeError: Don't know how to wrap a <class 'generator'> object to a HyObject)
It also means
unquote-splice
's compiler logic can be simplified a bit as well.Are there any other concerns or thoughts on the topic before I dig further into this? I couldn't find any other open issues around this, but maybe I just don't know what to look for?
The text was updated successfully, but these errors were encountered: