[HN Gopher] Mypyc: Compile type-annotated Python to C
       ___________________________________________________________________
        
       Mypyc: Compile type-annotated Python to C
        
       Author : mvolfik
       Score  : 170 points
       Date   : 2021-02-23 15:21 UTC (7 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | fwsgonzo wrote:
       | It would be a dream-come-true to be able to compile Python (or
       | some kind of very-close Python) down to a static binary. I want
       | to run it like a Go binary.
        
         | colejohnson66 wrote:
         | You already could?[0] Or are you asking about something else?
         | 
         | [0]: https://stackoverflow.com/questions/39913847/is-there-a-
         | way-...
        
       | jd115 wrote:
       | This sounds awesome.
       | 
       | What does it do?
        
         | lvass wrote:
         | It compiles type-annotated Python to C
        
           | optimalsolver wrote:
           | Does the resulting code run as fast as native C?
        
             | sitkack wrote:
             | > Mypyc is a compiler that compiles mypy-annotated,
             | statically typed Python modules into CPython C extensions.
             | Currently our primary focus is on making mypy faster
             | through compilation -- the default mypy wheels are compiled
             | with mypyc. Compiled mypy is about 4x faster than without
             | compilation.
             | 
             | My wager is that it does not. It _may_ if you have math
             | intensive code, but if you have an algorithm that touches
             | lots of python built in datatypes, access to those types
             | will be the bottleneck.
        
       | jagtesh wrote:
       | It seems really interesting that the mypy team went to such
       | lengths to create a binary version of their linter.
       | 
       | The big draw with mypyc has got to be direct integration with
       | other source code in C.
       | 
       | Can anyone answer if it's possible to replace PyPy's VM backend
       | with LLVM for AOT compilation? I wonder if that will results in
       | any performance improvements.
        
       | bratao wrote:
       | Here is one recent benchmark. Looks very promising.
       | https://github.com/mypyc/mypyc-benchmark-results/blob/master...
        
       | rtpg wrote:
       | Actually spent the evening trying to compile black through mypyc.
       | The tooling is there (blacks setup.py has a thing) but most
       | recent revisions of mypyc with black aren't quite working for me
       | 
       | The biggest issue right now seems to be miscompiles and the
       | resulting errors being a bit inscrutable. It leaves you in the
       | "am I wrong or is the system what's wrong?" stuff a bit still.
       | 
       | But overall I think the techniques are really sound and I believe
       | this is the most promising way forward for perf in Python.
        
       | asah wrote:
       | IMHO it makes little sense to compile complete Python programs vs
       | just compiling the slow parts. Some of the best reasons to choose
       | Python are precisely the ones that preclude compilation,
       | including:
       | 
       | - "batteries included" including a massive set of libraries (any
       | one of which won't be supported by the compiler)
       | 
       | - dynamism which makes it easy to wrangle syntax to your needs
       | (including the creation of domain-specific languages), but which
       | destroys the performance improvement of compilation, even if the
       | compiler can handle all the crazy introspection.
        
         | heavyset_go wrote:
         | > _IMHO it makes little sense to compile complete Python
         | programs vs just compiling the slow parts._
         | 
         | It makes sense for distribution of apps to end users, which is
         | a particular pain point with Python.
        
         | dragonwriter wrote:
         | > IMHO it makes little sense to compile complete Python
         | programs
         | 
         | Which is why this compiles specified modules, which can freely
         | call noncompiled modules, not "complete Python programs".
        
         | mlthoughts2018 wrote:
         | I think this extends outside of Python. Performance and safety
         | are trade offs, not absolutes, and the balance of needing
         | safety or performance vs extensibility vs ease of development
         | may result in dozens or hundreds of different trade off needs
         | in different parts of a single application.
         | 
         | One consequence is that it _never_ makes sense to use static
         | typing or compilation as application-wide absolutes for _any_
         | language or paradigm.
         | 
         | You should virtually never be writing whole applications in
         | Rust, C, C++, Java, Haskell, etc. It is a huge sign of bad
         | premature optimization and dogmatism. Compiling _subsets_ in
         | these languages and then exposing them through extension
         | modules in other languages that don't force those constant
         | trade offs is almost _always_ superior, and it's very telling
         | about poor engineering culture when this results in debates or
         | vitriolic dismissiveness from people with prior dogmatic
         | commitments to static typing or AOT compilation.
        
         | Twirrim wrote:
         | This isn't about compiling an entire program, this is about
         | compiling the individual libraries that you may be consuming,
         | if they already have type hint coverage. A "free" performance
         | boost.
         | 
         | If I have a pure python, fully type hinted library I'm
         | consuming, hats off to them, and they choose to use this,
         | awesome.
        
       | cabalamat wrote:
       | > Classes are compiled into extension classes without __dict__
       | (much, but not quite, like if they used __slots__)
       | 
       | Is there any way to say "no, a really want a __dict__ class here,
       | please"?
        
         | dragonwriter wrote:
         | > Is there any way to say "no, a really want a __dict__ class
         | here, please"?
         | 
         | Write it in a module you aren't compiling, and import it, since
         | this supports compiled modules using noncompiled ones.
        
       | optimalsolver wrote:
       | Does the resulting code run as fast as native C?
       | 
       | Would love to see some benchmarks on this.
        
         | dragonwriter wrote:
         | > Does the resulting code run as fast as native C?
         | 
         | The motivating use case is mypy, so I guess if someone wants to
         | hand code mypy in native C we can assess this. But _not_ doing
         | that is as much, I would expect, of the motivation as speeding
         | up mypy is.
        
       | mikepurvis wrote:
       | Somewhat related, I had a devil of a time a little bit ago trying
       | to ship a small Python app as a fully standalone environment
       | runnable on "any Linux" (but for practical purposes, Ubuntu
       | 16.04, 18.04, and 20.04). It turns out that if you don't want to
       | use pip, and you don't want to build separate bundles for
       | different OSes and Python versions, it can be surprisingly tricky
       | to get this right. Just bundling the whole interpreter doesn't
       | work either because it's tied to a particular stdlib which is
       | then linked to specific versions of a bunch of system
       | dependencies, so if you go that route, you basically end up
       | taking an entire rootfs/container with you.
       | 
       | After evaluating a number of different solutions, I ended up
       | being quite happy with pex: https://github.com/pantsbuild/pex
       | 
       | It basically bundles up the wheels for whatever your workspace
       | needs, and then ships them in an archive with a bootstrap script
       | that can recreate that environment on your target. But
       | critically, it natively supports the idea of targeting multiple
       | OS and Python versions, you just explicitly tell it which ones to
       | include, eg:
       | --platform=manylinux2014_x86_64-cp-38-cp38   # 16.04
       | --platform=manylinux2014_x86_64-cp-36-cp36m  # 18.04
       | --platform=manylinux2014_x86_64-cp-35-cp35m  # 20.04
       | 
       | Docs on this:
       | https://pex.readthedocs.io/en/latest/buildingpex.html#platfo...
       | 
       | And you can see the tags in use for any package on PyPI which
       | ships compiled parts, eg: https://pypi.org/project/numpy/#files
       | 
       | I don't know that this would be suitable for something like a
       | game, but in my case for a small utility supporting a commercial
       | product, it was perfect.
        
         | jtdev wrote:
         | > "if you don't want to use pip"
         | 
         | Why wouldn't you want to use pip?
        
           | mikepurvis wrote:
           | Pip is suitable for use by developers working in python,
           | setting up python workspaces with python sources and python
           | dependencies, but it's a UX fiasco for an end-user who just
           | wants to run a black box application and not have to care.
           | 
           | In my particular case the "application" was in fact
           | interactive bootstrap/install scripts for a large,
           | proprietary blob which wouldn't have been suitable for
           | publishing on PyPI, anyway. Setting up a separate, possibly
           | authenticated PyPI instance, and then training end users how
           | to use it, vs just shipping everything together in a single
           | package? Total non-starter.
        
             | jtdev wrote:
             | Interesting, sounds like a very unique use case. Is
             | containerizing not a possible solution?
        
               | mvolfik wrote:
               | This sounds a bit like a GUI application, so containers
               | would bring their own problems. Also you again force end
               | user install docker etc
        
               | mikepurvis wrote:
               | That would have worked, but it would have made the whole
               | thing a lot bigger-- even a featherweight base image
               | would have added more than what pex was able to do. It
               | complicates the usage side too, as then you need to be
               | root to chroot/nspawn/docker/whatever your way into the
               | container.
               | 
               | Definitely a complicating factor was that all of this was
               | meant to be usable by non-nerds and in an environment
               | with limited or possibly no internet access. It wouldn't
               | have been acceptable to download your installer package
               | at the hotel, and then get to site and invoke it only to
               | discover that you were on the hook for a few GBs of
               | transfer from docker.io.
        
         | mianos wrote:
         | I recently just used pyinstaller and pip on an Ubuntu 16.04
         | build machine. Everything works for 16, 18, 20 and even some
         | late Redhat versions with no work. Installed it on 3000 servers
         | with paramiko under prefect. Aside from the odd individual
         | server issue it all worked.
        
       | dvh wrote:
       | Murus
        
         | gligorot wrote:
         | The downvotes probably came from non-slavic readers. I read it
         | as Murus too haha.
        
       | syastrov wrote:
       | As a bit of background info, mypyc is "not really" ready for
       | broader use yet. The devs are planning a soft-launch:
       | https://github.com/mypyc/mypyc/issues/780
       | 
       | It is quite promising though, if it becomes more robust and
       | compatible. I also believe they have still only scratched the
       | surface of possible optimizations.
        
         | mvolfik wrote:
         | Yes, this. Actually I first shared it here, because I thought
         | that's cool and could work quite cleanly since mypy works well,
         | but when I actually tried compiling one of my Advent of Code
         | solutions with it, what i got was goto stuffed mess. I know I
         | can't expect nice C code, but i certainly didn't expect gotos.
         | 
         | As for the performance gain - 13.5 s with Python, 9 s compiled.
         | It was a naive implementation of AoC 2020/23, so a lot of array
         | cutting, concatenation etc. So this isn't really math, rather
         | lot of RAM I/O
        
           | czardoz wrote:
           | There's nothing wrong with gotos in compiled code. At the end
           | of the day, machine code is really just a bunch of gotos with
           | other instructions in between.
           | 
           | The reason goto is considered bad is that it can make code
           | hard to follow for humans. Since this is an intermediate step
           | in compilation, that's not an issue here.
        
       | zedr wrote:
       | Cython has a similar feature:
       | https://cython.readthedocs.io/en/latest/src/tutorial/pure.ht...
        
         | tomthe wrote:
         | Yes and it works.
         | 
         | What is the difference between cython and mypyc? I think they
         | should answer the question why anyone would want this over
         | cython on the readme.
        
           | makeworld wrote:
           | > Note the use of cython.int rather than int - Cython does
           | not translate an int annotation to a C integer by default
           | since the behaviour can be quite different with respect to
           | overflow and division.
           | 
           | This seems like an important difference to me. Your regular
           | type annotations can be used.
        
           | pletnes wrote:
           | Cython is great, but it (used to?) introduce its own language
           | with its own type syntax.
        
             | hangonhn wrote:
             | But that's because Python didn't have type annotations. Now
             | that it has them, cython can just use those instead of its
             | own and developers will get the benefit of being able to
             | compile to C using pure Python.
        
           | intrepidhero wrote:
           | Cython was around long before Python got type annotations so
           | they kind of had to come up with their own thing. Cython will
           | also happily compile Python WITHOUT type annotations, you
           | just won't see much of a performance boost.
           | 
           | Even without types cython provides a neat way to embed your
           | code and the interpreter into a native executable and has
           | applications for distributing python programs on systems that
           | are tricky for python like Android and WASM.
        
           | syastrov wrote:
           | Not having worked with cython, the difference seems to be
           | that cython requires using special types in its annotations
           | as well as not supporting specializing the standard types
           | like 'list'.
           | 
           | Mypy aims to be compatible with the standard Python type
           | annotations and still be able to optimize them. So in theory,
           | you don't need to modify your existing type-annotated
           | program. In practice I believe there are limitations
           | currently.
        
             | JesseMeyer wrote:
             | Cython has first class treatment for Numpy arrays. Can
             | Mypyc generate machine optimized code for chomping Numpy
             | arrays element-wise?
        
               | throwaway894345 wrote:
               | I don't think I want my toolchain to have first class
               | knowledge of specific libraries...
        
               | JesseMeyer wrote:
               | Python is married to Numpy for scientific computing.
        
               | throwaway894345 wrote:
               | In my opinion it's this sort of short-sighted thinking
               | that has cursed the Python project. "Everyone uses
               | CPython" leads to "let's just let third party packages
               | depend on any part of CPython" which leads to "Can't
               | optimize CPython because it might break a dependency"
               | which leads to "CPython is too slow, the ecosystem needs
               | to invest heavily in c-extensions [including numpy]"
               | which leads to "Can't create alternate Python
               | implementations because the ecosystem depends concretely
               | on CPython"[^1] and probably also the mess that is Python
               | package management.
               | 
               | I'm not sure that the Numpy/Pandas hegemony over Python
               | scientific computing will last. Eventually the ecosystem
               | might move toward Arrow or something else. In this case
               | it's probably not such a big deal because Arrow's
               | mainstream debut will probably predate any serious
               | adoption of Cython, but if it didn't then the latter
               | would effectively preclude the former--Arrow becomes
               | infeasible because everyone is using Cython/Numpy and
               | Cython/Arrow performance is too poor to make the move,
               | and since no one is making the move it's not worth
               | investing in an Arrow special case in Cython and now no
               | one gets the benefits that Arrow confers over
               | Numpy/Pandas.
               | 
               | [^1]: Yes, Pypy exists and its maintainers have done
               | yeoman's work in striving for compatibility with the
               | ecosystem, and still (last I checked) you couldn't do
               | such exotic things as "talking to a Postgres database via
               | a production-ready (read: 'maintained, performant,
               | secure, tested, stable, etc') package".
        
               | nerdponx wrote:
               | You are mixing up "how things are implemented" with
               | "stuff that data scientists interact with."
               | 
               | Arrow is a low-level implementation detail, like BLAS.
               | "Using" Arrow in data science in Python would mean
               | implementing an Arrow-backed Pandas (or Pandas-like)
               | DataFrame.
               | 
               | Your rank-and-file data scientist doesn't even know that
               | Arrow _exists_ , let alone that you can theoretically
               | implement arrays, matrices, and data frames backed by it.
               | 
               | If you want to break the hegemony of Numpy, you will have
               | to reimplement Numpy using CFFI instead of the CPython C
               | API. There is no other way, unless you get everyone to
               | switch to Julia.
        
               | JesseMeyer wrote:
               | Scientists are typically not trained computer scientists.
               | They do not care, nor appreciate these technical
               | arguments. They have two datasets A, and B, and want
               | their sum, expressed in a neat tidy form.
               | 
               | C = A + B
               | 
               | Python with Numpy perfectly service just that need. We
               | all have our grief with the status quo, but Python needs
               | data processing acceleration from somewhere. In my view,
               | Python needs to implement a JIT to alleviate 95% of the
               | need for Numpy.
        
               | throwaway894345 wrote:
               | Scientists aren't the only players at the scientific
               | computing table these days. There's increasing demand to
               | bring data science applications to market, which implies
               | engineering requirements in addition to the science
               | requirements.
               | 
               | > In my view, Python needs to implement a JIT to
               | alleviate 95% of the need for Numpy.
               | 
               | Numpy is just statically typed arrays. This seems like
               | best case for AOT compilers, no? I'm all for JIT as well,
               | but I don't have faith in the Python community to get us
               | there.
        
               | jessermeyer wrote:
               | JIT works great here too. It would see iteration and the
               | associated mathematical calculations as a hotspot, and
               | optimize only those parts, which is easy since the arrays
               | are statically typed and sized.
               | 
               | I say this as a Computer Scientist at NASA that tends to
               | re-write the scientific code in straight C. But for many
               | workloads, a JIT would make my team more productive,
               | basically for free as a user.
        
           | averageuser wrote:
           | Cython lets you use C structs to speed up memory access, and
           | generally gives you lower-level access.
           | 
           | Note that GraalPython has the C structs memory layout too.
        
           | ojnabieoot wrote:
           | I am not qualified to make any technical arguments. There's a
           | strong security and tech-managerial argument for using the
           | software that's aligned to the reference implementation.
           | Obviously cython is _currently_ the better choice for risk-
           | adverse organizations that need compiled Python. But I think
           | C-ish level people have a good reason to trust the stability,
           | longevity, and security of a product built by the "most
           | official" Python folks. There would need to be a _deeply
           | compelling_ technological reason to choose cython, not merely
           | chasing a few wasted cycles or nifty features.
           | 
           | Obviously organizations that don't manage human lives or
           | large amounts of money can use 'riskier' tools without as
           | much worry. This isn't an argument against cython generally.
           | But I worked at a hospital and wrote a lot of Python, and
           | would not have been able to get the security team to support
           | cython on their SELinux servers without a really good
           | argument. Cython is just an unnecessary liability when your
           | job manages identifiers and medical details on servers
           | accessible on a fairly wide (albeit private) network.
        
       | [deleted]
        
       | polyrand wrote:
       | I am always keeping and eye on mypyc, typed_python (llvm Python
       | compiler)[0] and nuitka[1]
       | 
       | I guess that because Python is extremely dynamic, we may never
       | have a full everything-works compiler, but I'm excited about the
       | possibility of this becoming some kind of intermediate step where
       | different parts of the program get compiled when possible.
       | 
       | [0] https://github.com/APrioriInvestments/typed_python [1]
       | https://github.com/Nuitka/Nuitka
        
         | CyberRabbi wrote:
         | I feel like it should be the agenda of the typed python syntax
         | to allow writing annotated python code that can be compiled
         | into a form that is as fast as equivalent c code.
        
         | intrepidhero wrote:
         | Cython is also mentioned downthread.
         | 
         | typed_python is new to me. I'll check it out. I'm too am
         | keeping an eye on this space. I think that compiling or
         | transpiling python may be the solution to both the major
         | problems I have with python: performance and distribution.
         | Exciting times.
        
         | cycomanic wrote:
         | I'd add Pythran to that list. It's a python to cpp compiler for
         | numerical python. It achieves impressive speed ups often with
         | very little adjustment of the numerical code. It's highly
         | undervalued IMO, you get speed similar or better than highly
         | optimized cython or c code with very little or no adjustments.
         | 
         | I compared it to cython, numba and Julia for DSO, which I wrote
         | about here:
         | https://jochenschroeder.com/blog/articles/DSP_with_Python2/
        
           | nerpderp82 wrote:
           | If you have a Python2 codebase, Shedskin also gives excellent
           | speedups for numerical codes, the only thing that didn't see
           | as good of a speed boost was string operations. Although that
           | might be fixed.
           | 
           | https://shedskin.github.io/
        
         | crazypython wrote:
         | Nuitka only removes interpreter overhead. (just 30%) It's still
         | quite slow. To get real performance improvements, we'd need
         | memory optimizations such as a modern JIT's hidden classes and
         | shapes, which store data directly on the object, instead of
         | inside a dictionary. https://mathiasbynens.be/notes/shapes-ics
        
         | anaphor wrote:
         | We can have a compiler that does everything. It's just a matter
         | of whether you have to stick the python interpreter in the
         | compiled binary or not, or how much of it you have to use and
         | whether you can only use the parts required. This is how a lot
         | of Scheme compilers work, even though you still have `eval` and
         | similar things.
        
         | dehrmann wrote:
         | Have you considered Kotlin and Graal? It's obviously not
         | Python, but Kotlin feels syntactically like Python meets Java,
         | and since it compiles to byte code, you can do AoT compilation
         | in Graal.
         | 
         | Edit: apparently GraalPython is a thing.
        
           | crazypython wrote:
           | Syntactically, sure. But D is semantically a better
           | combination of Python and Java. With `mixin`, you can `eval`
           | arbitrary strings at compile time. You can call methods and
           | use variables that are dynamically generated, like Python's
           | `__getitem__` with D's `opDispatch`. You can generate code
           | based on names and member variables using D's traits. You can
           | use Python-like refcounting with `RefCounted!`. You can use
           | Python's generators/iterators using D's lazy ranges, which
           | are just as fast as manual for loops.[0] You can bind to
           | Python with little effort using PyD. Just like Python, D has
           | great C interop.
           | 
           | D compiles quickly and has much nicer syntax than C or C++.
           | 
           | [0]: https://forum.dlang.org/post/xbejngbluilrulstmmhu@forum.
           | dlan...
        
           | heavyset_go wrote:
           | Kotlin Native also compiles to platform binaries.
        
             | pjmlp wrote:
             | Kotlin Native is going through a reboot after they realised
             | making a memory model incompatible with JVM semantics
             | wasn't that great idea after all.
             | 
             | Who would have guessed....
        
               | heavyset_go wrote:
               | Have any links so that I can read up on this? I found
               | this from last July[1].
               | 
               | [1] https://blog.jetbrains.com/kotlin/2020/07/kotlin-
               | native-memo...
        
           | amelius wrote:
           | The main benefit of Python is the ecosystem.
        
       ___________________________________________________________________
       (page generated 2021-02-23 23:00 UTC)