Hacker Newsnew | comments | show | ask | jobs | submitlogin
Let's Write an LLVM Specializer for Python (stephendiehl.com)
276 points by tenslisi 1 day ago | 40 comments




Erwin 1 day ago | link

Interesting. I currently use Python's AST to convert some nested logical query expression (in a syntax unique to my application) into bytecode executed by a specialized VM (I originally tried using V8 and LuaJit for this but performance wise that was unsuccessful; the project replaced some old Boost::Python C++ code). This article should make it easy to get started attempting an LLVM replacement.

reply

travisoliphant 16 hours ago | link

Yes, LLVM is a great approach for doing code-gen from arbitrary specialized VMs. And the Python interface to it makes it easy to experiment. We no longer use llvmpy for Numba (we use a simpler interface llvmlite) and so llvmpy could use a maintainer.

reply

walkamages 1 day ago | link

An excellent article! I had wanted to get back into some python recently after seeing the changes in 3.4, I had also wanted to become more familiar with LLVM, and this does both.

reply

gtirloni 1 day ago | link

there is this discussion (flame war?) about python3 not bringing too many benefits. i haven't made my mind yet. could you elaborate what you saw in 3.4 that was nice?

reply

exDM69 1 day ago | link

Simply put: it's a better language. The whole "discussion" is whether it makes sense to migrate since many parts of the ecosystem (many important libraries and frameworks) have not made the transition. And many distros ship Python 2 by default, Python 3 is optional. Python 3 only is not feasible.

To me, the killer feature is better lazy evaluation (generators). In particular, important builtins like map, filter, zip, enumerate, etc are generators, instead of returning lists. This makes it feasible to write things like

    (process(line) for line in map(str.upper, open('giantfile.txt')) if line.lstrip()[0] != '#')
Some of the above can also be done with itertools package in Python 2, but not everything.

Python 3.4 changelog is here, it contains e.g. asynchronous io facilities (asyncio module): https://www.python.org/downloads/release/python-342/

edit: added enumerate() in the example above, for line in open(filename) returns a generator in Python 2.x too.

edit2: enumerate is lazy in python2, I replaced it with map(str.upper)

reply

halflings 1 day ago | link

The example you gave works perfectly in Python 2.7 (would also be a generator, and you're not using map filter or else); but I agree: those should've been generators from day 1, especially zip and enumerate since they make more elegant code but often come with a performance overhead in Python 2.7

reply

maxerickson 1 day ago | link

Python 2 enumerate returns an 'enumerate' object that is more or less a light weight wrapper of the sequence that was passed in.

Generators provide a convenient syntax to implement that sort of object.

reply

exDM69 1 day ago | link

D'oh. I put in a map(), that returns a list.

reply

danohuiginn 1 day ago | link

Turning them into generators would have broken a lot of existing code, though, so it's reasonable to leave them until a major version change. Rather, these are the kind of small, obviously-useful changes that should have come immediately in 3.0, giving people some encouragement to switch.

reply

exDM69 1 day ago | link

Most of the breaking changes (including making map, zip, filter, etc lazy) were done in Python 3.0.

reply

exDM69 1 day ago | link

Ah, you're absolutely right. for line in open(filename) returns a generator. I added map(str.upper, xxx) there to make it more it make sense.

But the point should be obvious, without generators that would potentially consume a lot of memory.

reply

danohuiginn 1 day ago | link

        (process(line) for line in open('giantfile.txt') if line.lstrip()[0] != '#')
Is that line really using any new features in Python 3? The lazy evaluation there is in the file object and the generator expression, both of which have long been present in python2.

reply

exDM69 1 day ago | link

No, it wasn't. I added map(str.upper, xxx), now it does.

reply

masklinn 1 day ago | link

Then again a lazy map is just an import away in P2. It removes pitfalls from Python but the improvement is… very limited (as opposed to e.g. `yield from` which is a big convenience, or for much more specialised uses the ellipsis literal being universal)

reply

dalke 18 hours ago | link

  (process(line.upper()) for line in open('giantfile.txt') if line.lstrip()[0] != '#')

reply

exDM69 11 hours ago | link

That's besides the point. It's trivial to write that as a for loop or in a million different ways to avoid the issue. It's a contrived example written to demonstrate a difference.

Here's another one you can't change that easily:

    tests_pass = all(process(input) == output for (input, output) in zip(open('inputs.txt'), open('outputs.txt'))

reply

andreasvc 5 hours ago | link

You can change that easily, with izip from itertools.

The fact that a bunch of builtins and the values/items methods of dictionaries have become iterators is not very siginificant IMHO. Python 2 code could already be written to use iterators or generator expressions, so in the parts where it was crucial it was already done. In this regard Python 3 has not added new functionality but only changed defaults.

The unicode change is the big one.

reply

smazga 1 day ago | link

We went to Python 3 for the multiprocessing module. At the time it was 3.3, but now 3.4 has all that async magic. I wish I still worked on that project.

reply

chrisheller 1 day ago | link

You'll get an IndexError exception on that if there are any blank lines in the file.

Changing that to line.lstrip().startswith('#') would be an alternate approach.

reply

exDM69 1 day ago | link

You're right but that is irrelevant, it's a somewhat contrived example anyway. It's not like I spent a lot of time trying it out.

reply

ak217 1 day ago | link

Without listing any of the modules or improvements that are in the standard library in 3 but backported as PyPI modules to 2 (of which there are many), here are the features that I actually use in Python 3: unicode handling that isn't insane, function annotations, async improvements, exception chaining, enums, single-dispatch generics, better SSL support, generator delegation, better int-bytes conversion support, unittest module improvements.

The key point is that 2.7 is a language frozen in time, while 3.4+ is continuing to develop and improve. And most of the hand-wringing was before the critical mass of third-party modules was ported to 3.x.

https://docs.python.org/3/whatsnew/3.4.html

https://docs.python.org/3/whatsnew/3.3.html

https://docs.python.org/3/whatsnew/3.2.html

https://docs.python.org/3/whatsnew/3.1.html

https://docs.python.org/3/whatsnew/3.0.html

reply

andreasvc 5 hours ago | link

I can't say I disagree with such sentiments, but over time I've ran into a few issues (features and performance improvements) which where only addressed in Python 3. This is reason enough to try working with Python 3.

reply

ngoldbaum 23 hours ago | link

My favorite new feature is PEP-442 [0]. Basically, it's now safe to add a __del__ method to a class without worrying about memory leaks caused by reference cycles.

[0] https://www.python.org/dev/peps/pep-0442/

reply

pekk 22 hours ago | link

Most people should not be writing __del__ methods at all, especially if what they are trying to do is deterministic cleanup.

reply

mkesper 1 day ago | link

Saner handling of Unicode, for example.

reply

chc 1 day ago | link

This alone is pretty wonderful. I've only been working in Python a few months and the number of issues I've had to debug in 2.7 that came down to Unicode handling is kind of nuts.

reply

gamesbrainiac 1 day ago | link

I'd like to know too. Are there any LLVM specific enhancements that python 3.4 brings to the table?

reply

exDM69 1 day ago | link

No. AFAIK, there's nothing related to LLVM in core Python. And not in 3.4 changes either.

reply

bkeroack 22 hours ago | link

Add to the list:

- function annotations (allows runtime type checking via third party modules)

- asyncio (not as easy to use as Go's goroutines but still vastly superior to the multiprocessing module)

reply

baq 1 day ago | link

much better exceptions and sane unicode are the biggest improvements.

reply

travisoliphant 16 hours ago | link

This is a great tutorial about first-generation Numba. The author learned a lot about LLVM and llvmpy while working with several of our devs. If you are interested in the "Further work" in his article, come join the Numba project.

reply

tadlan 4 hours ago | link

What is second generation numba? And any plans to branch numba out of pure numeric application s?

reply

jonstewart 1 day ago | link

I really appreciate the length and detail in this blog post. It's comprehensive, not just showing off.

reply

ch0wn 23 hours ago | link

Stephen Diehl continues to blow my mind on a regular basis. His latest work in progress "Wrote You a Haskell"[0] is also worth keeping an eye on. I've worked through the first couple of chapters and it's fantastic.

[0] http://dev.stephendiehl.com/fun/

reply

wedesoft 10 hours ago | link

I did something similar in Ruby but using GCC as a "JIT" compiler for image processing (software [1], thesis [2]). I can really recommend JIT compilation for doing array processing.

[1] http://www.wedesoft.de/hornetseye-api/ [2] http://www.wedesoft.de/downloads/thesis_wedekind.pdf

EDIT: In my approach I didn't go through the Ruby AST though. Rather I used the approach of injecting "GCCVariables" which emit C code instead of doing the actual computation.

reply

chrisseaton 8 hours ago | link

You should submit your thesis to the Ruby Bibliography http://rubybib.org

reply

wedesoft 2 hours ago | link

Ok, will do. Cheers :)

reply

cyberneticcook 4 hours ago | link

Could this be used to ahead-of-time compile Python code ? I'm more interested in getting to a native executable or library.

reply

illumen 1 day ago | link

Very nice article! :)

Storing types via traces could be another step for gathering types. As well as using the more advanced static type checking code that is around for python.

Now I have something to work through on the weekend. Looking forward to part 2!

reply

travisoliphant 16 hours ago | link

The numba code-base implements quite a bit of this. We actually moved away from the AST approach and went back to the byte-code approach because the AST approach quickly becomes unwieldy as the number of Visitors that you apply grows. Compile times are also slower.

The author is definitely helping people learn about LLVM and how it can be used with Python --- which is great, because this is exactly what Numba is: http://numba.pydata.org. But, please don't start another "Numba". Just come help us improve the current one.

reply




Guidelines | FAQ | Support | Lists | Bookmarklet | DMCA | Y Combinator | Apply | Contact

Search: