Live data from Hacker News

Unladen Swallow Retrospective

qinsb.blogspot.com

11–20 of 34 posts

Re: Unladen Swallow Retrospective

#11
The points about LLVM being designed for "static C-like languages" isn't totally on-point. There as an impedance mismatch between Python and LLVM, but it's less about dynamic versus static than it is about the nature of the stack frame.

In LLVM, most optimizations operate on SSA values (the Value class in the LLVM IR). There is some support for CSE-ing loads, etc, but the effectiveness of that depends heavily on your alias information. So to get good optimization out of LLVM, you've got to represent things as Value's.

This is hard to do in Python. Python's lexical scoping semantics are a little bit wonky and there are lots of scenarios in which the thread's stack frame is reified as an object. So without some heavy analysis, you end up keeping your local variables in an activation frame object on the heap, and at that point you're toast. The local variables themselves won't be represented as SSA Value's, and most of the LLVM optimizations won't do anything with them.

This is not a "dynamic language" thing per se. Lisp, which is also a dynamic language, actually maps quite cleanly to LLVM. Every binding introduced by LET is already in SSA form unless it is either closed-over, assigned-to, or both.

1) If the value is just closed-over, you demote it to the function's environment and replace uses of the variable with a load from the environment vector.

2) If the value is just assigned-to, you just demote it to a stack slot via ALLOCA and LLVM's mem2reg pass will take care of re-promoting it to a Value. This latter technique is exactly what Clang does for all C local variables, so LLVM is highly-tuned for handling this scenario. In C, variables that have their address taken cannot be promoted, but this cannot happen in Lisp so every assigned-to value demoted to an ALLOCA should be promotable.

3) If a value is both assigned-to and closed over, you demote it to a heap-allocated box and replace all uses with heap references.

After this transformation, nearly every Lisp variable is an SSA Value, and the optimizers can work with them. Even if you use function calls to do generic arithmetic, etc, LLVM will happily CSE them for you as long as you mark those functions as readnone (ie: pure).

Now, there are some things LLVM won't do for you. It can't const-propagate generic arithmetic because it does't know the semantics. It can't do reassociation, etc, because you're not using the ADD/SUB, etc instructions. I don't see anything that would prevent you from doing it yourself in a custom pass, however.

In short, the criticism isn't so much that LLVM has an impedance mismatch with dynamic languages as it is that it only handles the bottom of the optimization stack. You still need to do the high-level language-specific optimizations before handing things to LLVM.

Re: Unladen Swallow Retrospective

#12
post #8
post #7

Earlier quoted context omitted.

Out of curiosity what makes this such a big win for Python, but not so much for Lua or C-type languages?

I honestly don't know why it's not such a big deal for Lua, but given LuaJIT's performance, and knowing that it doesn't do escape analysis I know it must not be a big deal :) In Python it's a big deal because everything is boxed, and your inner loops just get bogged down with allocations, which are expensive compared to arithmetic operations.

LuaJIT being a trace compiler, escape analysis in implicit. As long as a value stays (say) int or float, the code to handle it stays int or float. If for whatever reason it switches type, the code will change to accommodate that -- but as long as the types (and/or values) stay the same, the machine code to handle them will take advantage of that.

Re: Unladen Swallow Retrospective

#14
post #8
post #7

Earlier quoted context omitted.

Out of curiosity what makes this such a big win for Python, but not so much for Lua or C-type languages?

I honestly don't know why it's not such a big deal for Lua, but given LuaJIT's performance, and knowing that it doesn't do escape analysis I know it must not be a big deal :) In Python it's a big deal because everything is boxed, and your inner loops just get bogged down with allocations, which are expensive compared to arithmetic operations.

It's not a big deal for LuaJIT because it uses a clever tagging format that can hold a boxed 64-bit float in a register.

Re: Unladen Swallow Retrospective

#15
ONLINE STORE: ====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

Air jordan(1-24)shoes $30

Handbags(Coach l v f e n d i d&g) $35

Tshirts (Polo ,ed hardy,lacoste) $15

Jean(True Religion,ed hardy,coogi) $30

Sunglasses(Oakey,coach,gucci,A r m a i n i) $15

New era cap $12

Bikini (Ed hardy,polo) $20

accept paypal and free shipping

====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

====== ( http://www.fullmalls.com )====

Re: Unladen Swallow Retrospective

#17
post #12
post #8

Earlier quoted context omitted.

I honestly don't know why it's not such a big deal for Lua, but given LuaJIT's performance, and knowing that it doesn't do escape analysis I know it must not be a big deal :) In Python it's a big deal because everything is boxed, and your inner loops just get bogged down with allocations, which are expensive compared to arithmetic operations.

LuaJIT being a trace compiler, escape analysis in implicit. As long as a value stays (say) int or float, the code to handle it stays int or float. If for whatever reason it switches type, the code will change to accommodate that -- but as long as the types (and/or values) stay the same, the machine code to handle them will take advantage of that.

It's absolutely not true that escape analysis is implicit in tracing: https://bitbucket.org/pypy/extradoc/raw/63e4617062b2/talk/pe...

Re: Unladen Swallow Retrospective

#18
post #13

"Most Python code at Google isn't performance critical." What about AppEngine? The primary 'cloud' platform at Google.

You would think that such a product would in fact be an excellent candidate for high performance but I believe that group strives more toward process scalability than any particular low level optimization.

Re: Unladen Swallow Retrospective

#19
post #5
post #4

Somewhat relevant to the topic - Mike Pall discusses (2009) the usage of llvm for lua - he's not ditching the approach, just pointing out the difficulties, and why he took on making luajit the way it is: http://lua-users.org/lists/lua-l/2009-06/msg00071.html

A big one Mike misses (because it isn't as critical for Lua AFAIK) is escape analysis. For PyPy our aggressive escape analysis can bring huge wins on things like numeric code and some types of string processing, but no static compilers really do this type of optimization, because it's not very important in C-type languages.

Mike said he'll be working on escape analysis to decrease the garbage collection if I remember correctly...

Re: Unladen Swallow Retrospective

#20
post #11

The points about LLVM being designed for "static C-like languages" isn't totally on-point. There as an impedance mismatch between Python and LLVM, but it's less about dynamic versus static than it is about the nature of the stack frame. In LLVM, most optimizations operate on SSA values (the Value class in the LLVM IR). There is some support for CSE-ing loads, etc, but the effectiveness of that depends heavily on your…

Huh, first time I've seen the word "reified".

http://en.wikipedia.org/wiki/Reification_(computer_science)

Post reply on HN