You've heard me talk previously about scaling Python and Python performance on the show,
but on this episode, I'm bringing you a very interesting project pushing the upper bound
Python performance for a certain class of applications. You'll meet Braxton McKee from
Euphoria. They are developing an entirely new Python runtime that is focused on horizontally
scaling Python applications across thousands of CPU cores and even across GPUs. They describe it
as compiled automatically parallel Python for data science. Let's dig into it on Talk Python to Me,
episode 60, recorded May 2nd, 2016.
Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the
ecosystem, and the personalities. This is your host, Michael Kennedy. Follow me on Twitter,
where I'm @mkennedy. Keep up with the show and listen to past episodes at talkpython.fm
and follow the show on Twitter via at Talk Python. This episode is brought to you by SnapCI and
OpBeat. Thank them for supporting the show on Twitter via snap underscore CI and OpBeat.
Braxton, welcome to the show.
Oh, thanks, Michael. I'm excited to be here.
Yeah, we're going to take Python to a whole other level of scalability here with sort of
computational Python, which is where Python has actually struggled the most in parallelism. So
I'm really excited to talk about that.
Well, it's a complicated topic. It's something I spent a lot of years on and it's actually really
fun. So we'll have a lot of good stuff to talk about.
Yeah, I don't know how many people have heard of your project, but I'm really excited to share
with them because it sounds super promising. But before we get into all of this, let's start at the
your story. How did you get into Python and programming?
Well, I've been programming for pretty much my entire life. I think I started when I was eight
years old and I'm like 36 now. So it's kind of the way I think. I studied math in school, but
writing code has always been the way I think about approaching problems. So like when I was doing math
assignments, if I wanted to understand what was going on, I'd usually go write some kind of program to
try to understand what was happening, do the numeric integral by hand or whatever. And then when I
left college, I went and I started working in the hedge fund industry, which was a great experience
as a young person. I got to log code. I had to solve some really interesting problems. And it was really
there that my sort of desire to work on tools came around because I was just consistently frustrated with
how much effort it took me to actually implement these solutions. I would talk with my colleagues about
some computation that we wanted to do and we could describe in principle what it was in five minutes.
And then they would ask me, all right, how long is this going to take? And I said, all right, come back to me in three months.
Because, you know, getting things to actually scale up is really challenging.
I started writing Python code actually after I left the hedge fund industry, which was in 2008. So, you know, Python really got big in, you know, 05, 06, 07. And it got into my attention.
And then when I was doing, I was actually doing some speech recognition work. And I spent a lot of time doing the same thing that, you know, most people who are frustrated with Python's performance end up doing, which is you, you try and figure out the little bit of it that's slow and stick that in C++.
And then write, write wrappers for that, and then try and do all the rest of your work in Python.
And a lot of the things that I've been interested in recently, and I think a lot of the pretty awesome projects that are out there for speeding Python up, are essentially trying to address that same problem.
Saying, how can we get more and more of what you're doing to live in Python, because you pick Python for a reason, and be able to just use the tool the way you want and not have to pay a performance penalty?
Yeah, you know, I've noticed in the industry, there seems to be more and more attention given to the speed of Python.
I know the NumPy guys have been around for a while, the PyPy guys have been around for a while, really working on this issue.
But it seems like more and more people are trying different angles of approach.
So in Python 3.6, they have some really interesting stuff to speed up method invocation in Python, which is one of the slower bits.
There's the PyJohn stuff coming from Microsoft.
There's a lot of really interesting work.
And so yours kind of lands in that space as well, right?
Yeah, absolutely.
And I think that what's going on is, you know, Python is a really lovely language to program as an implementer.
It's got a lot of weird corner cases, and it's a very hard thing for computers to reason about.
And, you know, the core of what makes it possible for systems to speed up code is their ability to reason about the code that you've written.
So if you look at, like, a C++ compiler, it does all of these fancy tricks inside to transform your code into something that's faster.
And it does that because it can make really strong assumptions about what you're doing that it can apply to the machine language that it's generating.
And that's really hard to do in Python because Python gives you so much flexibility.
And so I think that the reason you're seeing so many different approaches is because there's a million different ways to look at the problem and say, okay, we can look at what people have written and start to identify particular things that we want to speed up or make, I think this is the more common case, start to put some constraints around what we're going to tell people is going to be fast.
And as soon as you put constraints, now you give these optimizers something to work with.
And I bet you if you looked in the internals of all of these different things, each one of them would have a different way of looking at Python and picking a subset of it saying, this is the stuff that we're going to make fast.
Like NumPy, if you look at it originally, the whole point of NumPy, why it's fast is because you can really, if you look at its roots, really just stick floats and ints in there.
It's not trying to be fast for arbitrary Python.
It's trying to make Python fast for matrix algebra, linear algebra kind of stuff.
And adding that constraint is what allows it to be fast.
Yeah, that's interesting.
I think a lot of those things I mentioned do add constraints.
PyPy sort of gave up the C module integration in order to get its speed.
Whereas the PyJon stuff from Microsoft, they're trying to make sure they're 100% compatible, but that puts a different kinds of pressure and makes it harder on them.
The NumPy stuff, like you said, is focused basically on matrix algebra and matrix multiplication and stuff.
Yeah, absolutely.
And I think that guys like the PyPy guys have a big amount of respect for what they're doing because they're trying to solve a really, really brutal problem.
Which is, let's make anything written in Python orders of magnitude faster.
For most of my work, I've focused on a slightly restricted problem, which is to say, let's try and make Python programs that are written in this sort of purely functional style faster.
Our original goal was not just to get a compiler to be faster, but also to get code to be able to scale, right?
To be able to write idiomatic simple Python and get something that can use thousands of cores effectively without the programmer having to really think about how that's happening.
One of the crucial ways that you can do that is you say, okay, I'm going to make it so that if you make a list of integers, you can't change it again.
That seems like a pretty trivial change, but it's the underpinning for an enormous amount of different optimizations that we can do inside the program.
And if you look at systems like Hadoop and Spark, they have as a core tenant this idea that the datasets themselves are immutable and you make transformations on those datasets.
And that immutability is sort of the key to them being able to do things in a distributed context.
We said we can take that same idea and apply it not just at the outermost level of the operations we're allowing, but we can let that filter all the way through the program.
And you can take that same constraint and apply it not just to make things distributed, but also to make the compiled machine code faster.
Because if I make a list of integers, I can now assume that it's going to be a list of integers forever because you can't change it.
That makes me able to generate much faster machine code if I'm from the compiler because I can start to make really strong assumptions about what's going on inside of it.
Okay, that's awesome.
And you said a few things that I'm sure have piqued people's interest.
Python, thousands of cores, and machine instructions and compiled code.
So maybe we can take a step back just for a minute and tell us about your project, Euphora, and what you guys are up to there.
And then we can dig into the details.
Yeah, absolutely.
So Euphora is a platform for executing Python code at massive scale.
And the basic idea is that you should be able to write simple idiomatic Python that expresses what you want and get the same performance that you get,
not just if you had rewritten the code in C, but if you had rewritten the code in C and used threads and message passing to generate a parallel implementation.
You should be able to do this directly from the Python source code without making any modifications to it and having the compiler really see everything going on inside of your code and use that to generate a fast form.
So we've been working on this for about five years.
We have a totally separate runtime and compiler framework that we've developed to do this.
And we actually think of the languages almost like front end.
So we built a VM.
And I mean VM in the sense of like the Java virtual machine or Microsoft CLR.
It sort of has enough structure for it to be able to reason about programs in this context and make them fast.
And then what we've basically built is a cross compiler from Python down into that virtual machine.
And we're planning on doing other languages at some point in the future.
But there's so much goodness in Python that we really wanted to start there because that was what our roots were.
The project is 100% open source.
So you can just go get it on GitHub.
And the Python front end for this thing has really only been around for about nine months.
So it's still a work in progress.
But it's evolving pretty rapidly.
And, you know, we've been able to get cases where you can take relatively naive looking Python programs, dump this into this thing,
and get them to work actually pretty efficiently on thousands of cores.
Our biggest installations have gone up to six or 7,000 cores running on Amazon Web Services.
And actually being able to use those things efficiently without really having to think about it is pretty fun.
Yeah, that's really amazing.
Distributed sort of grid computing type frameworks are not totally new.
But I think two of the things that are super interesting about what you've told me,
maybe the first and most interesting thing is that you don't have to write special distributed code, right?
You just write regular code and the system reasons about how to parallelize it.
Is that correct?
Right, absolutely.
And that comes, as I mentioned, from this idea that we put some constraints on the language.
So the idea is it's immutable Python.
So you make a list and you don't modify the list.
You make a new list by writing a list comprehension that scans over the old one and makes a transformation of it.
Because of that constraint, the system can now see, okay, like you're doing all of these operations on these data structures.
And I can see you're doing this function now, but you're going to call this other function next.
And it can reason about the flow of data inside of that and say, okay, you know, these things are independent of each other.
I can schedule them separately if I want to.
That freedom then gives the runtime the ability to say, okay, now it's trying to solve this problem of what's the most efficient way to lay things out.
But the end result is that if you say, okay, here's a list comprehension or I've got a divide and conquer algorithm where I take something, I scan over it, I do some computation, cut it in half, and then recurse.
It can see that that's what you're doing and parallelize that.
And it turns out that if you look at regular people's code, there's all kinds of opportunities to parallelize it that they probably weren't thinking about.
They wouldn't only have thought about it if they really hit a performance bottleneck, but it's in there.
Like, if you're computing the covariance of two time series, you end up having three different dot products that you need to do, and they can all be done independently of each other.
And, you know, if you write it out the naive way, the Py4 compiler can actually see that parallelism.
And if those tasks are big enough, it will split them into three and actually do those on separate threads for you, and you don't have to actually explicitly say that.
And, you know, in my thinking, there's been a continual movement in computing to raise the level of abstraction at which we work.
So, like, we used to write an assembler, I guess, 40 years ago, right around when I was being born.
And then they came up with C and Fortran, and these really made it possible to write more complicated programs because more and more of the details were abstracted away.
And if you look at Python, Python is like the natural extension of that idea on single-threaded computing as far as it can go.
You can be so concise and clear in Python because so many low-level details have been extracted away from you.
But we're still not there on parallel computing.
Like, people still spend a huge amount of time writing custom code to do parallel compute.
I think the reason that technologies like Hadoop and Spark have been so successful is that within certain problem domains, they can make a lot of that complexity go away.
And I wanted to take it a step further and say, let's not just do it for specific patterns of computing, MapReduce jobs or whatever.
But, like, let's do this for all of your code, everything that you write.
There should be enough structure in it for us to see and make it fast.
That's great.
Maybe you could just tell folks who are not familiar with Spark and Hadoop how they work and then, you know, like, what you mean by taking a little farther to general code with yours because I'm sure a lot of people know about Hadoop, but not everyone.
Sure. So, Hadoop began out of Yahoo as the technology they developed to index the web.
And, you know, the core component of it is you take your data set and you split it across a bunch of machines.
And you're given two basic primitives.
You've given math and reduce.
And the basic idea is that if you think hard enough, you can fit any big parallel compute job into that pattern.
Mappers basically allow you to take every element in your data set and run a little function on it to get a new element.
And reduce allows you to take collections of elements and jam them together.
And for a lot of big data parallel applications, this turns out to be a very natural way to express things.
And the reason these technologies have been successful is because if you can figure out how to fit what you're doing into MapReduce, it's actually a very easy way to program.
And the infrastructure, the Hadoop ecosystem, can give you all these benefits.
So, Hadoop is very fault tolerant.
You can turn the power off on any one of the machines or a rack of machines running Hadoop.
And because of the programming model, the central scheduler that's making decisions about what to do can react to that and still ensure that your calculation completes because it can schedule the map and reduce jobs that were running on that machine on another machine.
As an example.
So, if you work within this framework, you suddenly make a bunch of problems that are sort of endemic to parallel computing go away.
But the problem is that map and reduce and the other primitives that the community has since added to those systems are still relatively clumsy ways of describing computations.
There's a bunch of things that fit really naturally into them.
So, page rank and a lot of the things that people do with web scale data fit very naturally.
But scientific computing, numerical computing, things where the calculations don't just fall naturally into this very embarrassingly parallel structure don't fit that well.
And it can take some real thinking to jury rig your application into that model.
So, when I talk about wanting to be able to do this from the arbitrary code, what I really mean is that you should be able to write code the way you're thinking about solving your problem.
And the infrastructure should be able to generate that parallel implementation from your code as opposed to you saying, look, here's the place in my code where there's a million little tasks, each one of which can be done in parallel.
Go do them in parallel, which is what you're essentially doing when you break things down to fit them into the MapReduce model, which I think pretty much what 90% of people doing large-scale computing right now are doing, at least the ones that I'm talking to.
Yeah, you really have to change the way you approach the problem to fit it into MapReduce, right?
Yeah, and a lot of people are not comfortable with that.
Like, my best, my favorite example of that is, is like nested parallelism.
So, like, imagine you have a data set that has some tree structure to it.
So, you say, well, I've got a bunch of companies, and then for each company I have a bunch of transactions.
And now you've kind of got two layers to your hierarchy.
And if you say, I want to do something in parallel first over the companies, but then within each company in parallel again over all of the transactions, that's like a very unnatural thing to say in MapReduce because which thing are you mapping over?
Are you mapping over the companies?
Are you mapping over the records?
You need to kind of jury-rig it in order to make it work.
But if I told you to do that in Python, you would just have two loops.
It would be totally obvious to you how to do it.
And the only reason you would ever go to the MapReduce land is because you really needed that performance.
And at the end of the day, this gets back to this original frustration I had, like, I think it was almost 10 years ago when I left the hedge fund industry thinking about this stuff.
Like, thinking to myself, why is it so hard for the system to see that I have two loops and that there's parallelism in both of them and just make it parallel?
Why am I having to, like, rejigger everything in order to fit the model that's been given to me?
And that turns out to be an easy thing to say and a hard thing to implement.
That's what we've been doing.
Yeah, that's really amazing.
So maybe it's worth digging into that a little bit.
You said in the beginning you thought that you would have to use a different language other than Python, right?
Yeah, and this has been a really interesting evolution for us.
So that mutability idea, the idea that, like, if I make a data structure, I can't change it, is at the heart of pretty much any system that's scaled.
And the reason is because it gets rid of all of these concurrency issues.
It's like if the data element changes on one machine and you allow that, then if you have a distributed system, you have to make sure that that right operation propagates to everybody else who needs it.
And that can create real serious problems.
And if you don't handle it correctly, you end up with programs that are unstable or give you the wrong answer or really slow because there's a huge amount of locking.
But if you look at Python, Python as a language is a very immutable thing.
When you make a class, you start with an empty class and you start by pushing methods into it, right?
And the way people build up lists of operations is they make lists that are empty and they keep appending things to them.
So when I was first coming to this problem, thinking to myself, okay, I want everything that's good in Python, namely this ability to just pass functions around and not think about typing.
You know, all of the good things about Python's object model.
But I don't want that mutability.
I, my first thought was, okay, this is going to be completely impossible to do with vanilla Python.
I also thought to myself, well, actually there's a bunch of features in other languages that are purely functional.
Things like, like OCaml that are really nice features that Python doesn't have.
And we might actually need those things when we take away the immutability.
Yeah, that's interesting because, you know, Python is super mutable.
It's not just the data itself is mutable, but the structures that make up the execution of your program, like the classes and functions themselves are mutable, right?
So this is a really hard problem to solve with Python.
If you actually look at good Python programs, like they go through two phases.
They go through one phase where they set everything up and then they go through another phase where you don't change things.
Like the worst bugs I've ever seen in live Python programs are the ones where people are changing the methods on classes, like as they're running their program.
And so now, you know, you've got some class and it's got some function on it and the meaning of that function changes as the program is running.
You know, it's impossible for a human being to reason about those programs.
So like large scale stuff, people don't do that.
People actually tend, the bigger their program gets, the more closely it adheres to the actual, to the immutable style, because that actually makes it possible for human beings to reason about the code.
And let me move it this way.
If a human being can't reason about your program, the chances that a computer can reason about it are pretty low, at least right now, maybe in two or three more years.
That won't be true.
But they haven't figured that one out yet.
That's right.
Continuous delivery isn't just a buzzword.
It's a shift in productivity that will help your whole team become more efficient.
With SnapCI's continuous delivery tool, you can test, debug, and deploy your code quickly and reliably.
Get your product in the hands of your users faster and deploy from just about anywhere at any time.
And did you know that ThoughtWorks literally wrote the book on continuous integration and continuous delivery?
Connect Snap to your GitHub repo and they'll build and run your first pipeline automagically.
Thanks SnapCI for sponsoring this episode by trying them for free at snap.ci slash talkpython.
So yeah, that immutability key, it felt, my initial reaction was it was going to be impossible and we needed new language constructs.
But after we built the language out, I realized, wow, this is basically Python.
Like there's no, it didn't actually change that much.
And what we ended up with is, is basically our language is now like bit code.
Like actually mapping Python down into our language, which is called Fora.
And saying Fora is now bit code for all of this other stuff.
It's like, it's like a language for describing scalable parallel scripting.
That frees it from needing to be a language that you would program in every day because we have that.
We have Python.
And so it was actually a fairly nice transition for us.
The reason I chose to do this, it came out of a conversation I had with someone who's done a lot of modified Python.
So my original, if you talked to me five years ago, I would have said, look, no one wants a language that has weird, messed up semantics.
They want Python to be Python and it's going to give them something different.
It should be its own very pure thing.
But I talked to this guy named Mike Dubno.
He was the CTO of Goldman Sachs for many years.
He's the godfather of new language stuff on Wall Street.
The team that worked for him at Goldman in the 80s, they've all gone to all the other financial institutions in New York and all re-implemented really interesting large-scale computation systems.
Usually they have a language component to them.
Yeah, there's a lot of interesting language work happening on Wall Street.
A friend of mine just got hired at a startup to build a new language to express ideas just slightly differently.
You know, it's so, yeah, that's cool.
Yeah, no.
And I bet you if you trace it back, it traces back to Mike Dubno at some level.
He was the one that convinced me that actually Python with slightly modified semantics is actually something people are totally comfortable with.
And his evidence was that, you know, he built a system at Bank of America that's got hundreds of millions of lines of Python code.
And it's a big graph calculation thing.
Like you make one node that represents the price of oil and another node that's some trade that some trader needs to have repriced.
And you describe these things in Python.
And they're updated.
They can run on different machines from each other, but they kind of look like they're on one machine.
And there are some rules about how that needs to work.
It's not vanilla Python.
And his point was that Python programmers are totally happy having some constraints around their work as long as it's mostly Python.
And his evidence was there's a hundred million lines of code written against this thing.
And I thought that was pretty good evidence.
And that convinced me that...
That's good evidence, yeah.
Right.
That I could make a go of this idea of restricted, you know, of immutable Python, that that was something that people could work in.
Yeah.
And you were able to basically take those ideas and then map them to your custom runtime, right?
Yeah.
And it actually didn't even take that long.
Like, there's been a...
Most of the work has been around libraries and tooling.
But we had the core mapping from, like, the Python object model and runtime down into...
Down into Fora done in just a few weeks.
Mostly because the object models are really not that different.
Most of the pain is around all of the little idiosyncrasies of Python.
Like, if you divide two integers in Python 2, you get an int.
But if you divide them in Python 3, you get a float.
Like, these little details, making sure that all that stuff works perfectly tends to be a lot of work.
But that's not the meat of the intellectual problem, right?
That's the details of getting a working system that can run existing Python code and get the same answer as what you get in a regular Python interpreter.
That totally makes sense.
So there's a bunch of questions I have.
The first one is, you know, what kind of problems are you guys solving?
And what kind of problems do you see other people solving with this system?
Like, where are you focused on applying it?
Sure. So for the most part, we're interested in data science and machine learning.
So because of the immutable nature of this version of Python, it's really not a good fit for systems programming tasks.
Like, I wouldn't write a real-time transaction processing thing in it because it's designed to take really big data sets, do really big computations on them efficiently.
You could think of it almost like we optimized for throughput of calculations, not for latency.
Like, anything you stick into this thing is going to take at least a half second.
But the goal of it is to be able to use 10,000 cores to take something that might take five hours down to five seconds or something like that.
Right.
We're obviously good at solving all of the standard data parallel things.
So, like, what you're saying is I've got a bunch of data on Amazon S3, and I want to pull it into memory and parse it and, you know, slice it the way you would in Pandas.
All that kind of stuff works pretty well.
And, you know, the goal of the platform is to actually make it so you can write regular idiomatic Pandas code and just have that scale.
We haven't done all of the functions, but we're working towards that.
I think the place where you really see differentiation is when you have a more complicated algorithm where you've got, as I said before, some kind of structure that doesn't fit nicely into MapReduce.
Like, I'll give you an example of one project that we did.
One of our customers does Bayesian modeling of retail transactions.
So they have all of these transactions from different human beings at different vendors, and they build these models to try and predict whether they're going to be, you know, good customers for these vendors in the future.
And they're trying to ask questions like, well, you know, what does it mean about you for your purchases at Home Depot?
So the fact that I can tell that you go to Dunkin' Donuts every single day in the morning for coffee.
So they're interested in relationships across vendors.
And so you end up with this structure of observations where you can group the data by person or you can group the data by vendor.
And you have variables that have to do with, like, how much people care about that particular vendor because, you know, the way people interact with Amazon.com is very different than the way they interact with Lowe's.
And then you also have this information about the individual person.
And so this ends up wanting to suck about 100 gigabytes of data, keep it in memory, and then pass over that data in different orders depending on whether you're trying to update the variables about the individuals or whether you're updating the variables about the vendors.
It turns out that it's very easy to describe this in Python.
You just have two different loops, one in one direction, one in the other.
And you kind of alternate back and forth between them as you're updating the model.
This would have been very hard to express as pure map produced, but it's just, you know, it's the whole project is like a couple hundred lines of Python code, which is pretty nice.
And this is all distributed and whatnot, right, across the different cores and machines?
Exactly.
The reason it's only a couple hundred lines of code is because all of the painful stuff, which is making it fast, getting the numeric likelihood calculation to be fast, doing the gradient descent minimization, like, all of that is handled by the infrastructure as opposed to being explicitly
described by the user.
So the workflow is literally you run a command line thing that boots machines in AWS.
We're a big fan of using spot instances in Amazon's infrastructure.
If you're not familiar with that, it allows you to basically bid for the market against the market price for compute.
If the market price goes above your level, then you don't get the machine, but if it goes below, then you do.
And it is usually around 10% of the regular cost of buying a machine, which means that I can get something like like a thousand cores for something like 10 bucks, which is a pretty insane price for that level of hardware.
That's great.
And spot instances are really good for when you want to spin up some stuff, do some work for 30 minutes and throw them away.
It's less good for like running them indefinitely as a web server, right?
Yeah, exactly.
Although there are people who are figuring out that if you spread your spot instance request over enough different zones in Amazon, that the chances that the price spikes in more than two or three of those zones is relatively low.
So like I know there are people running real time ad bidding networks entirely on spot and the pricing is so cheap that that's feasible, but it takes a lot of careful design to make it work.
But for the kind of analytics workloads we're talking about, it's pretty perfect because usually as a programmer, what you're hoping to do is, you know, tune your algorithm, get it so that you like it on a small scale, and then fire it off and just have as many machines as is required to get the calculation done, you know, in an amount of time that allows you to like look at the answer and solve whatever business objective you have.
And so spot is perfect for that because you can literally just say, all right, how many machines do I need to get this done in a half an hour?
You know, you look at the workload, you look at the throughput, you divide, and it will just happen.
And then you're not committed to holding onto that hardware for any longer than you need it.
And at least in our case, because the backend infrastructure is fault tolerant, if the market price happens to change and you lose the machines, you can just go and raise your bid price or wait for the price to drop again and continue where you left off.
You don't really lose anything from having the volatility of the pricing shutting your machines down.
Yeah, that's really interesting from a distributed computing perspective.
It sounds to me with the restriction on sort of read-only type of operations that not every package out there is going to be suitable.
So can I go to your system and say pip install something and start working that way?
And are there packages that will run or does it have to kind of all be from scratch?
It's certainly the case that if you do it from scratch, you can control everything and that you have the highest likelihood that it works.
We adopted this hybrid approach where we said we're going to have two kinds of code.
There's going to be code that we understand how to translate, and then there's going to be code where we know there's no hope of translating it, mostly because it's written in C.
Like if you look at the core of NumPy and Pandas, they're all written in C.
There's nothing to even analyze there.
So you don't need to do anything to make them fast, but you would need to do something in order to make them parallel.
So what we have is a library translation approach.
So we've rewritten the core of NumPy and Pandas in pure Python.
And then whenever we see that you're using the libraries, we replace your calls to the C versions of those libraries with calls to the pure Python versions.
And that allows our infrastructure to see the library and make it scale out.
The downside is that we obviously have to translate any libraries that are written in C that way back into Python.
It's not as much work as you'd think because part of the reason those libraries are so complicated is, in fact, because they're written in C.
Like NumPy has to have separate implementations of every routine for both floats and doubles and ints and everything else.
But if you just write it as Python, it's obviously a lot simpler.
And the compiler is responsible for generating all of those specializations.
As a user, this means that, you know, you're better off if your algorithm either uses really vanilla stuff that we've already translated, or if you're willing to write the algorithm in regular Python yourself.
So most of the work where we've had really good success scaling this up is cases where people wrote their own algorithm anyways, because it didn't work with something off the shelf.
But our vision over time is that we could get most of the major algorithms like scikit-learn-stop, pandas, NumPy, all of the core stuff that people use, translated back into pure Python in a way that works with the compiler.
And it's pretty easy to do these translations.
So, you know, one of the things I'd say to the community is if you're using the system and you run into a function that's not there, you know, go and do the translation.
In most cases, it's pretty easy, and you can contribute it back, and it's a nice way of extending the system.
We're working on that kind of thing all the time, and we just tend to do the functions as we run into them, as we need them for our other work.
This episode is brought to you by OpBeat.
OpBeat is application monitoring for developers.
It's performance monitoring, error logging, release tracking, and workflow in one simple product.
OpBeat is integrated with your code base and makes monitoring and debugging of your production apps much faster and your code better.
OpBeat is free for an unlimited number of users.
And starting today, December 1st, OpBeat is announcing that their Flask support is graduating from beta to a full commercial product.
Visit opbeat.com slash Flask to get started today.
We also have plans that should be released later this summer of letting you run stuff out of process in regular Python so that if you don't want to translate it and you're willing to say, look, here's some crazy model that somebody wrote.
It's in C. I'm never going to translate it.
I don't want to run this model in parallel.
I want to run thousands of copies of it.
The distinction is I don't need this model itself to scale.
I just need to have lots and lots of different versions of it running on different machines.
In that case, we'll be able to let you run that out of process basically using the same kind of thing that you would do if you were solving this problem by hand, which is multiprocessing.
And this is basically the same approach as what PyPy has taken, which is to say you then chunk the problem up into smaller chunks yourself.
We'll ship each slice of the problem to a different Python interpreter on a different machine, and then it'll just run in vanilla Python the way it runs.
The upside of that approach is that it makes it possible to access all of this content that may never fit nicely into one of these models.
The downside of that approach is that you end up with all of the headaches that are usually associated with things like PySpark, which is like if that process runs out of memory and crashes, you don't have any idea what happened.
PySpark can't know.
You know, we won't really be able to know why that happened.
And then the responsibility is now back on the user of the system to figure out, well, why did it run out of memory?
What do I need to do to get it to fit?
Which is all stuff that we can make go away when we can actually see the source code in itself and translate it.
So I think it's kind of a necessary step to getting everything working for a lot of use cases.
Yeah, absolutely.
Because you don't want to try to translate the long tail of all these libraries and stuff.
So talk to me about how you share memory.
So, for example, if I've got a list in memory and it's, as far as my program is concerned, has like a billion objects in it, what does that look like to the system, really?
Sure.
This is a super interesting question.
So the idea is we put it in chunks, each chunk being a reasonably small amount of memory, like 50 or 100 megabytes.
And then the idea is that these chunks are scattered throughout memory.
So, like, imagine that you had a billion strings and each string is like, on average, a kilobyte.
So you've got a terabyte of stuff in that list.
But, like, the strings are different lengths, right?
So, you know, you might have some chunks might have 50,000 records and some other chunks might have 5,000 records and some other might have 500,000 records, depending on how you and how your data actually looks.
So the system chunks it up.
And then what it does is, as your program is running, it thinks about this sort of at the page level.
It says, okay, I can see your program is running.
And depending on what it's doing, it needs different blocks of that data to be located together on the same machine.
So if you do a really simple thing, like, imagine you say, okay, let's make a list comprehension.
I'll scan over my list of strings and take the length of each one.
Well, that's a really simple operation.
You know, I don't need anything more than each individual string at once to do that.
So it doesn't have to move any data around.
It just literally goes to every chunk and says, okay, take that chunk and apply the length function to it, and now you get back a bunch of integers.
But you could do something more complicated.
You could say, all right, imagine that these strings are log messages and, you know, the first tenth of them are from one machine and the second tenth are from another machine.
You might say, okay, I'm actually going to go figure out the indices of the different machines,
and I want to do something where I'm scanning through them and looking at them at different timestamps across all the different machines.
So now you actually have a more complicated relationship.
You need different chunks.
You might need the first hundred thousand strings and string 500 million to 503 million.
You might need those two blocks together because you're actually doing something with the two of them.
One partition might be really evenly distributed across the machine.
Then you ask the question the right way, and it just parallels it perfectly.
But you might cross-cut it really badly, right?
There's two problems here, right?
One of them is, did you write something where I can get enough data in memory to solve your problem without having to load a lot of stuff?
And then the other problem is, like, which things need to be together.
So, like, what I was just describing is a case where if you just naively put the chunks on different machines,
it's very easy to ask a query where the chunks are in the wrong place.
And our system can handle that, right?
So it'll see, like, oh, you're accessing these two chunks together because you're using both of them.
That problem it solves well because you can actually see that co-location and break your problem down.
It's basically active working sets of pages.
And so it'll just shuffle everything around in memory to meet the requirements of your problem.
There's a second issue, which is, like, imagine you said, look, I've got a terabyte of strings.
I'm going to start indexing randomly into the set of terabytes of strings,
and you're just not going to be able to predict anything about what I'm going to do.
I'm just going to grab them randomly in sequence.
You know, there's kind of no way to make that fast.
What you'll end up doing is repeatedly waiting on the network while the system goes and fetches, you know,
string 1 million and then string 99 million and then, you know, whatever.
It's going around and grabbing.
And this is just because you've written an algorithm that doesn't have any cache locality to it whatsoever.
And so there isn't really a good solution around that other than trying to minimize the amount of network latency.
But the way I try and articulate it to people is to say, try and think about it so that your program in your mind,
you could break it down into sets of Python function calls that use, you know, a couple of gigs of data at a time.
And it will figure out how to get those gigs of data to the right place.
You know, it'll see what that structure is by basically through actually running your program and seeing where you have cache misses.
Yeah, that's pretty interesting because one of the things you can do to make your code much more high performance,
especially around parallelism, but even in general, is to think about caching cache misses on the CPU cache, right?
Like locality of data, things like that.
And that's, you know, running on my own machine.
But it's interesting that that also applies on the distributed sense for you guys.
Yeah, well, in many senses, it's exactly the same problem.
It's just that it's a much, much, much worse problem.
Like when you cache miss on L1 cache in your CPU, you're doing something and the data is not present.
Like it's like 100 CPU cycles to go and fetch that data from long-term memory, which is like much slower than what your CPU is used to.
But that's still incredibly, incredibly fast.
Like if you have a job running on one computer on a network and it says, hey, I need this 50 meg of data on another machine.
Like go get that for me.
Like if you have 10 gig Ethernet, that means you can move like a gigabyte around per second on that network between two machines.
That's like a 20th of a second that you're going to have to wait to get that 50 meg.
You know, four orders of magnitude, five, I don't even know.
It's some stupidly larger number of time to wait for that data to be present.
So it's the same basic problem.
It's just that you're way more sensitive to the cache locality issue when you're operating in a distributed context.
And it's one of the reasons why the MapReduce Spark model has been so successful is like you're giving the framework a very clear idea of what the cache locality is going to be.
When you say take this function and run it on every element of this data set, what you're implicitly telling it is like copy the data for this function to every single machine before you do the job.
And now you'll never be waiting for any data.
And like we're saying, look, you can take that even a step further.
You can infer which data actually needs to go there for arbitrarily complicated patterns and make sure that you're not waiting on cache misses.
But it's all boiling down to like trying to fit the program into a model where threads are not waiting on data, where you're getting the most out of the CPUs as you possibly can.
Okay.
Yeah, that makes a lot of sense.
So let me ask you a little bit about your business model.
Euphoria is open source.
You can go to GitHub.com slash Euphoria and check it out, right?
But obviously you guys are doing this as a job.
So what's the story there?
Sure.
So we're basically a data science and engineering consultancy at this point.
We deploy Euphoria as part of our work for our clients.
We open sourced it because we wanted technology to get to as many people's hands as possible.
You know, I didn't leave the hedge fund industry in order to purely just make a pile of money, right?
I wanted to build stuff that people could use that would enable the world to do interesting things.
So we put it out there and we're hoping that we're hoping other people will pick it up.
But our business model mostly revolves around us actually doing data science and engineering work for our clients.
And so in some cases, the Euphoria platform is really only 25% or 30% of the solution because you have algorithmic work that needs to be done regardless of what platform you're doing it in.
And then you have data integration work that needs to be done, you know, making all these various databases talk to each other.
And so the infrastructure ends up being sort of a part of it.
I think over time we're anticipating building out additional products and services on top of the Euphoria platform and selling as standalone applications.
But infrastructure like this really wants to be open source.
Like it's very hard to charge for it successfully because you're asking people to take infrastructure and then build a lot of things on top of it.
And nobody likes to do that if they don't actually control the infrastructure.
And it's also one of the things that really benefits from community involvement.
Again, like this idea of the library work.
We as a small company are not going to be able to port every library.
But if I put this infrastructure out there and it's useful for people, then each marginal developer can look at it and say, hey, there's some piece of content in NumPy or some crazy scikit-learn algorithm that I'm missing.
It's not that much work for me to go port that one thing and contribute it back.
And that spirit of like everybody working together is the kind of thing that makes these big platform systems actually run, which is, I think, again, why you've seen so much access in open source for this kind of platform infrastructure.
That sounds really nice.
I think the one thing I could see that you guys could make sort of directly charge for is if you actually managed the servers and you kind of sold it as computation as a service or something like this.
I thought about that, but it's a little like there was a company called PyCloud that raised a bunch of venture funding.
Yeah, I met some of those guys.
Yeah, and their stuff was super awesome.
And I think they ultimately shut that down because they didn't feel like they could generate the kind of return that they wanted.
And, you know, the Databricks guys are doing Spark host as a service.
But then Google came out with Spark host as a service and Google's cost to be able to provide that infrastructure is super low.
So, you know, I looked at it and it's a nice idea.
But I think that you just you run into this problem where if you're successful, Amazon or Google will just run the same thing and their cost advantage will eat your lunch.
You know, I think in enterprise land, there's a lot more value to solving the LDAP problem, which is, you know, most of the people listening to this podcast are probably big users of open source software.
And a lot of them, you know, will pick this kind of technology up to use personally.
When you bring this kind of technology into the enterprise, you need all these additional support services that regular people don't need.
It needs to integrate with some crazy Microsoft legacy product from 10 years ago.
It needs to be able to talk to all kinds of funny databases that that startups don't have, as an example.
I think that a lot of the monetization and open source happens around that kind of that kind of problems.
Yeah, I think you're right.
Enterprise definitely has a lot of its own special challenges, let's say.
And that's great.
Right.
And those challenges, they don't affect adoption.
They affect how big organizations like charade and lock this kind of technology down.
And so it makes sense to basically build products and services around that and to try and make the core infrastructure as widely used as possible in the community so that it's as good as it can be.
Yeah.
Okay, great.
So you said you have some interesting things going on and sort of what's coming as well.
And one of them is you had recently added iPython notebook integration, right?
Yeah, so this is something that had been on our to-do list for a while.
It doesn't turn out that it's actually that much work.
You just need to make sure that you understand where to get the source code.
Because instead of living on disk, like most Python modules, it's in memory in these specialized Python notebook cells.
We also did some work around getting feedback from the cluster.
So you can fire something off now and it will tell you while the thing is running, you're using 500 cores.
You can see that number go up and down as it paralyzes your computation because you can write something that is naturally single-threaded.
And then you'll get feedback saying, hey, like this thing that you wrote, it's really only using one core because you've got some loop that can't be parallelized, something like that.
And this is, I think, a place we're planning on doing more, giving more feedback, more introspection, like some profiling tools and stuff like that.
So that as you're running your calculation, you can see, okay, it's really spending a lot of time inside of these functions.
This place is like thrashing because it's using too much data or whatever.
So I think there's a lot that we can do there to sort of help give more feedback to people about what's going on in their calculations.
This is a common problem in any distributed programming context, which is like when you're working on a regular Python interpreter, you can just put print statements and everything is good.
But when you're working on across hundreds of machines, even if you could get all the print statements, you'd be drowning in print statements.
You wouldn't be able to get anything productive out of it.
And it can be really hard for people to understand, like, you know, okay, in a distributed context, why is this slow or fast?
So I think there's a lot to do to give people more clarity.
I think the other things that we're working on right now that I think are super interesting is we just finished doing something where we're actually making an estimate of how long your computation is going to run for.
So what we do is as we're running your computation, we're constantly breaking it up into little pieces.
And we actually look at what functions you're calling and what values are on the stack.
So, you know, I can notice that if you, you know, if you call F with the number 10, it takes one second.
If you call it with the number 1,000, it takes 100 seconds.
I can build a little model for all your function calls about how long they take.
And this means that on subsequent runs, I can actually make a projection for how long the different pieces of the computation are going to take.
And this is useful in two ways.
This is useful for the scheduler because now I can say, okay, well, this super long computation, I'd better schedule that first because that will improve the overall runtime of the computation, gives it a sense of how to spread the calculations across the system better.
But it's also useful in that I think over the long run, we'll actually be able to give you accurate estimates of how long your calculation is going to take from the beginning and actually make recommendations to you about how much hardware you should use, which would move us towards a more automated way of using the cloud.
Where instead of booting machines and thinking of them as your cluster, you just literally fire the thing off and think purely in terms of cost.
That's great.
So you have like some machine that would just be sort of in charge of orchestrating this and you say, go run this job.
And it would go, all right, that's 100 reserve or 100 spot instances at this price of this many cores and go.
Something like that?
Yeah, exactly.
Like it could actually, it could come back to you and say, I think this is going to take, you know, 100,000 compute seconds, you know, and that it's parallelizable up to 1,000 cores.
So this is how much it's going to cost.
And you could think of it totally that way and then really completely abstract away the actual machines.
But you can't really do until you have a good estimate because the problem with computer programs is like you can keep adding zeros and quickly get to runtimes that are astronomical.
So you don't want to do that naively because then you'll end up with some system that just says, hey, like I'm just going to run on a million cores and send you an astronomical Amazon bill, which you really don't want.
So actually getting a good estimate is pretty important if you're going to make that feature work.
But like that model also has incredibly useful features because it's essentially a profile of your code.
It can tell you, hey, by the way, the reason why your program is taking so long is that like when you're calling this function, it's taking 3,000 compute seconds.
And then that tells you, oh, I should go look there and maybe see why it is that it's so slow.
Yeah.
That's really cool.
And so far what we've been talking about is running this on CPUs, on dedicated VMs in the cloud.
But if you're going to do computational stuff like pure mathy type stuff, some of the fastest hardware you can get a hold of are actually graphics units, GPUs, right?
Yeah, absolutely.
I think that that's the thing that's driving the current AI renaissance right now is the fact that like these graphics processors aren't just good for playing video games.
They're good for doing general math.
And in many cases, they're several hundred times more math operations than your regular CPU can do.
The biggest issue with them is that they're really, really challenging to program.
Unlike your CPU, where each of the cores on your machine can do a completely different thing at once, and you can just program as if they're totally independent of each other.
The threads on a GPU all have to do exactly the same thing simultaneously.
They can kind of try and hide that from you a little bit.
But as soon as you do something that the GPU doesn't like, it suddenly gets hundreds of times slower and there's no benefit to using it.
It's really hard to write code.
It's efficient on a GPU.
And one of the things that we're spending a lot of time on, Euphoria, is figuring out how to get the Python code to run natively on GPU and to solve some of these programming problems.
And so the idea is, in the same way that we're able to make fast programs scaling out on the cluster by actually running them and learning from the way they're behaving, like which functions are slow and how they're accessing data, we can apply those same techniques to solve some of the problems that people have with GPUs.
So as an example, we can identify places where threads are in fact all doing something together in lockstep and say, hey, we've noticed that this is a good piece of code to run on a GPU and schedule that automatically.
And then there are more aggressive program transformations you can do where you can detect that if you've modified the program slightly, you would end up with something that actually would run efficiently on GPU.
I was reading some blog post by a guy at NVIDIA where he pointed out that if you move an array from local memory to shared memory in a GPU, suddenly it speeds up 25 times, which is like an enormous performance difference, right?
And I didn't even remember exactly when I was reading it, like what's the difference between local and shared memory?
And my point is that this is the kind of thing that like an optimizer that actually had a statistical model of your program would be perfectly capable of doing in an automated way, which would free you from the burden of trying to figure that out.
And in some cases might do optimizations that no programmer had ever thought of because it's such a hard thing to optimize and they didn't realize, wow, if I move this one little thing over, it's going to be faster.
So we're just getting started with this, but I think there's an enormous amount that we can do and you should expect to see more commits related to that coming out over the summer.
Yeah, I think that's really amazing.
And just for those of you guys who don't know out there, you can go to Amazon, AWS EC2 and say, I would like to get a GPU cluster with NVIDIA or ATI Radeon or whatever type of graphics cards, right?
Yeah.
So what's even better is you can do this with Spot.
So you can go and get machines that have four Teslas on them and you can pay like 25 cents an hour for that.
And so you can put a hundred machines with four Teslas on them for 25 bucks, which is just an incredible, that's like just a stupid amount of computing hardware.
The GPU instance prices on Amazon fluctuate a little bit more wildly because I think there are some researchers doing a whole lot of deep learning research on there.
So it's actually kind of funny.
Sometimes the smaller GPU instances actually cost a lot more than the larger ones because there's pressure for those on pricing.
But it is an amazing ecosystem.
And a lot of our work is focused on this idea that like not only is it easy to boot those machines, but it should be easy to use 100 GPUs without having to think about it, right?
There's actually some great software out there right now for using a single GPU effectively.
But what happens when you now say, okay, well, I have data on one GPU and it wants to talk to data on a completely different GPU on a completely different machine, right?
That's the thing that I'm trying to make totally transparent.
That's a really awesome problem.
And so obviously the GPUs are fast and this would make it possibly, maybe possible for you to get an answer to your question sooner.
But would it actually make it cheaper as well to answer your question in general?
No question that there are a whole host of problems that if you move them from CPU to GPU, your total cost of computation goes down by a couple orders of magnitude.
So actually as an example, this maximum likelihood calculation I was doing on the retail stuff for one of our customers, one of the reasons we're pushing into the GPU space is that we currently use about 40 Amazon machines,
the really big instances to get their calculation done about an hour, but they have to do this fairly frequently to update the model when new data comes in.
And, you know, I mean, that costs them 10 bucks every time they run it.
And if they, you know, do that every day, four times a day for a long enough time, that starts to become real money again.
You know, we've estimated that it would be about 5% of the cost if we can get that same calculation to run as efficiently as we think it could using GPU.
Now the problem with that is like, as soon as you do that, it's not clear that people will take the cost savings and not just reinvest it into a solving a bigger problem.
But like, look, that's great for them if they want to do it.
Yeah, that's interesting.
So 20 times cheaper when you say 5%.
Yeah, something like that.
Yeah, that's awesome.
I mean, it's extremely dependent on the problem.
Sometimes it's only like two times faster.
Sometimes it's slower.
And sometimes it's like, look, for the neural network stuff that people are doing, it's at least 100 times faster slash cheaper.
I mean, the neural network people all think in terms of electricity costs, right?
They literally say how many like training cycles they can do per kilowatt hour.
Wow.
Okay, that's really amazing.
So is there a way with your system to sort of extrapolate and estimate how much something would cost?
Is there a way to say, I have these three problems?
If I were to give it this much data, how much would each one of these cost?
Because I can only afford to answer one.
Well, so that's one of the things that we're hoping to answer with some of this extrapolatory runtime stuff, this ability to predict what runtime is actually going to be and how much compute power you need.
So the idea would be that you would run all three problems at several smaller scales.
The system could see how the runtime of all the little functions was changing as you're changing the problem size.
And then it would be able to extrapolate from there.
At the end of the day, though, it all depends on how accurate you want your estimate to be.
If you really want a perfect estimate, you should actually do that experiment yourself and make a decision.
And like, honestly, the way I usually do this problem is doing that process by hand.
I'll run it with some smaller input and I'll just keep adding zeros until it becomes appreciably slow.
And then I'll try and understand why it's changing.
But yeah, in principle, that's totally the direction that we're moving in where you can literally just get a price estimate for each of the three things.
And there might be, you know, plus minus two times or whatever, depending on how accurate the estimates are.
Yeah.
Okay.
That sounds great.
So I guess the last thing we have time to cover is how do you get started?
Like, I know it's on GitHub, but there's a lot of moving pieces with distributed computing.
And so can you just walk me through going from nothing until I have an answer, maybe, what that looks like?
Yeah, absolutely.
So we publish Docker images with the software backend every time we do a release.
So if you want to run this on your local machine, you pip install the front end, which is called Pyfora.
And that's just vanilla Python code that, like, has the thing that takes your Python code and sends it to the server.
And then you need to get some nodes that can actually run work.
And you've got two options.
One of them is to run it just on your local machine.
And doing this gets you the benefit of using the cores on your local machine.
So it's not trivial.
So to do that, you just do Docker run the euphora slash service colon latest.
That, you know, pulls the latest version of the euphora service and runs it.
And then if you want to run this on Amazon, which is the way I do everything, we have a little command line utility called Pyfora AWS.
And that thing can just boot instances.
You have to have your AWS credentials exposed in the environment the same way you would if you were using Voto to interact with AWS.
But if you do that, it knows how to start machines and stop them.
And it will make sure everything is configured on there correctly.
You do need to make some decisions about the security model.
Personally, I prefer to boot machines and then use SSH tunneling.
So then I'm connecting to a server that looks like it's on local host and SSH is taking care of all of the security.
But there are other ways to do it that are documented in Pyfora AWS.
But the basic idea is you use AWS or you use Docker locally to get something going, you know, one of these machines.
And that gives you an IP address that you can talk to.
In your Python program, you connect to that IP address.
You get a little connection object.
And then any code that you want to execute in Euphoria, you just put inside of a with block that references that connection.
So you say with my connection colon.
And then anything inside of there when the Python, when your Python interpreter gets to it, instead of executing it, it will take that code and all the resulting objects, ship them over to the server.
The server will execute them and then it will ship it back.
You know, I'm going to be now that we have Python notebook integration, I'm going to be publishing a bunch of example, Python notebooks.
So you should be able to go to our website and just pull down a few examples of those things operating.
But, you know, the basic point is that if you if you are a user of Amazon already, you can get up and running with the amount of time it takes to boot an AWS instance.
And how long it takes you to pip install Pyfora.
Wow, that sounds really cool.
Nice work on that.
And props for using the context manager, the width block.
It turns out it's a really it's a really nice way of doing it there.
You have to you have to do some clever trickery under the hood to make that work, especially when it comes time to propagate exceptions.
Because if you produce an exception on the server, you've got to take that state move back over and then kind of rebuild the appropriate stack trace objects on the, you know, on the client, which is a kind of an interesting thing to do.
But the end result is a is a really nice integration.
And it gives you this nice ability to pick and choose where you want to do the Pyfora, you know, to use the technology.
So it means that if there are parts of your code that are really never going to be paralyzed and you don't want to move them or if you're touching things on your file or you're reading things off of the Internet or whatever, that stuff can all happen on your local box.
It's just a heavy compute stuff can happen remotely.
It also gives you a nice way of deciding, like, which objects are going to live remotely and which objects are going to live locally.
So you can do a calculation that ends up producing this, you know, like think about the example we were talking about earlier.
We have a list of a billion strings like in the width block.
That list of a billion strings, you can do whatever you want in your local Python interpreter.
You'll end up with like a reference to that list.
It's like like a proxy object.
You can't do anything with it locally because you obviously can't bring a terabyte of strings back into your local Python process without crashing it or waiting for hours and hours.
But you can then pass that back into a subsequent width block and do something there.
Cut it down to something smaller if you want to pull back a slice of it or whatever.
And so it makes a really nice workflow for kind of describing which things are going to be in your local process and which things are going to be remote.
That sounds awesome.
This is really cool.
If you've got big data processing, it sounds like people should check it out.
So I think we're going to have to leave it there.
We're just about out of time.
Let me ask you just a couple questions to wrap things up.
I always ask my guests what their favorite PyPI package is.
There's 75,000, 80,000 of them out there, and we all get experience with different ones that we'd want to tell people about.
What's yours?
Well, I hate to not be super interesting on this, but I have to say I love Pandas.
I think Wes did a great job.
I would argue that the resurgence of Python in the financial services community is basically due to the existence of Pandas pulling people out of the R mindset and into Python.
So if you're not familiar with it, check it out.
Probably everybody listening to this knows about it.
Yeah, Pandas is very cool.
And how about an editor?
What do you open up if you're going to write some code?
I am a strict adherent to Sublime.
I even paid for it, although I didn't have the energy to actually paste the key so that it still keeps asking me for the key.
But I did pay for it.
I think he did a great job with that editor.
Yeah, Sublime is great.
All right, so final call to action.
What should people do to get started with you for it?
So go and check us out on GitHub.
Try running some code.
We've tried to make it as easy as possible to get started.
As I said before, we're going to be posting a bunch of my Python notebooks with examples.
And then, you know, please give us feedback.
Tell us what libraries you want ported, what problems you're running into using it.
And, you know, like, honestly, if you run into some NumPy function that we didn't get to yet, take a crack at implementing it yourself.
It's a pretty straightforward model.
And send us a pull request.
We'd love to include it.
All right.
Excellent.
I think this is a great project you're on, and I'm happy to share with everyone.
So thanks for being on the show, Braxton.
Thank you so much for having me.
It's a pleasure to talk about this stuff, as always.
This has been another episode of Talk Python to Me.
Today's guest was Braxton McKee, and this episode has been sponsored by SnapCI and OpBeat.
Thank you guys for supporting the show.
SnapCI is modern, continuous integration and delivery.
Build, test, and deploy your code directly from GitHub, all in your browser with debugging, Docker, and parallels included.
Try them for free at snap.ci slash talkpython.
OpBeat is mission control for your Python web applications.
Keep an eye on errors, performance, profiling, and more in your Django, Flask, and Pyramid web apps.
Tell them thanks for supporting the show on Twitter, where they're at, OpBeat.
Are you or a colleague trying to learn Python?
Have you tried books and videos that left you bored by just covering topics point by point?
Well, check out my online course, Python Jumpstart by Building 10 Apps, at talkpython.fm/course,
to experience a more engaging way to learn Python.
You can find the links from this show at talkpython.fm/episodes slash show slash 60.
Be sure to subscribe to the show.
Open your favorite podcatcher and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, Google Play feed at /play,
and direct RSS feed at /rss on talkpython.fm.
Our theme music is Developers, Developers, Developers by Corey Smith, who goes by Smix.
You can hear the entire song at talkpython.fm/music.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Smix, let's get out of here.
Mike Beckton, who rocked his best.
I'm first developers.
.
Don't forget.