Update on 9th May 2024:
Mojo is developing so rapidly that a couple of things I wrote last year are already obsolete.
let
variables are not supported anymore- you can define three types of function arguments :
borrowed
,inout
andowned
- to execute your mojo code you need a
main
method
Among all the tech stack I have worked with in my life I enjoy working with Python the most. Even with its weaknesses, efficacy and speed. They may come to an end, though. Around two months ago I heard about Mojo, spreading rapidly with its huge flames to burn down the obstacles of having a “fast Python”. Mojo is said to be thousands of times faster than Python. I was really interested and checked its documentation right after. While Mojo is still a work in progress, developers are provided with a JupyterHub-based playground to try it out. That’s exactly what I did.
About Mojo
Mojo is intended to be a new programming language for AI developers. It was created by the intention of unifying the world’s ML/AI infrastructure. Later they realized that programming across the entire stack was too complicated, so they chose to embrace the Python ecosystem and the goal was to provide kind of a superset of Python. Meaning to make existing Python code compatible with Mojo. Python is widely used and really popular, so they got it right I think.
Some Basic Facts About Mojo
- Mojo is not interpreted at runtime as Python, but compiled ahead-of-time to machine-native code, using the LLVM toolchain.
- Existing Python code is fully compatible with Mojo. You can import your own Python libraries as well.
- Migrating existing Python code to Mojo is not fully compatible yet, but it’s a work in progress.
- You can define
var
orlet
variables,var
is mutable whilelet
is immutable. Uselet
when possible for better performance. struct
vsclass
: Mojo’sstruct
is similar to Python’sclass
, however the former is static, while the latter is dynamic. Mojostructs
are bound at compile-time meaning you cannot add methods at runtime. In return you get performance while being safe and easy to use.- In Mojo you can define a
def
or anfn
. Similar tostructs
, anfn
is stricter but provides you with greater performance. - You can define both immutable arguments and mutable arguments. An immutable argument, borrowed object, is just an immutable reference to the object the function receives. This is the default behavior.
- Using the
__copyinit__
method we can implement copyable types such as reference semantic, immutable or deep value semantic. In Python it is reference semantic. - The
@register_passable("trivial")
struct decorator tells Mojo that the type should be copyable and movable but that it has no user-defined logic for doing so. - Mojo destroys values using an “As Soon As Possible” (ASAP) policy, behaving like a hyper-active garbage collector.
- Mojo’s file extension can be
.mojo
or.🔥
. Yes, you see it well. :)
You can and I recommend that you check Mojo’s programming manual and modules.
Also, here is the launch video from Mojo’s website.
Let’s Test It!
To start with, this is how Mojo’s playground looks like:
Having been provided with access to the playground, I created two examples to test Mojo’s performance against Python.
My first test was inspired by a HackerRank task I just solved a few days ago. The implementation of complex numbers. The result is breath-taking.
Python version:
import time
class MojoComplex:
def __init__(self, real, imaginary):
self.real = real
self.imaginary = imaginary
def __add__(self, no):
return MojoComplex(self.real + no.real, self.imaginary + no.imaginary)
def __sub__(self, no):
return MojoComplex(self.real - no.real, self.imaginary - no.imaginary)
def __mul__(self, no):
r = (self.real * no.real) + (self.imaginary * no.imaginary * -1)
i = (self.real * no.imaginary) + (self.imaginary * no.real)
return MojoComplex(r, i)
start = time.time_ns()
result = 0
for i in range(10000000):
x = MojoComplex(i + 91, i)
y = MojoComplex(i, i - 2)
sum = x + y
sub = x - y
mul = x * y
result += (sum.real + sub.real + mul.real)
end = time.time_ns()
print(result)
print(end - start)
Output:
4750001345000000
22078695473
If you want to run pure Python code in the playground notebook, use %%python
in the cell.
Mojo version:
let time = Python.import_module("time")
struct MojoComplex:
var real: Int
var imaginary: Int
fn __init__(inout self, real: Int, imaginary: Int):
self.real = real
self.imaginary = imaginary
fn __add__(self, no: MojoComplex) -> MojoComplex:
return MojoComplex(self.real + no.real, self.imaginary + no.imaginary)
fn __sub__(self, no: MojoComplex) -> MojoComplex:
return MojoComplex(self.real - no.real, self.imaginary - no.imaginary)
fn __mul__(self, no: MojoComplex) -> MojoComplex:
let r: Int
let i: Int
r = (self.real * no.real) + (self.imaginary * no.imaginary * -1)
i = (self.real * no.imaginary) + (self.imaginary * no.real)
return MojoComplex(r, i)
start = time.time_ns()
var result: Int = 0
for i in range(10000000):
let x: MojoComplex
let y: MojoComplex
let sum: MojoComplex
let sub: MojoComplex
let mul: MojoComplex
x = MojoComplex(i + 91, i)
y = MojoComplex(i, i - 2)
sum = x + y
sub = x - y
mul = x * y
result += (sum.real + sub.real + mul.real)
end = time.time_ns()
print(result)
print(end - start)
Output:
4750001345000000
3006
The result was so breath-taking for me that I decided to create and print the result
variable to make sure it is right. Truly incredible, around 7 million times faster. In the Mojo version we take advantage of the performant static struct
type, fn
methods and let
variables when possible. If you declare a variable as var
that could be let
, Mojo warns you anyway.
My second test was just a random function with some mathematical operations.
Python version:
import time
def do_math_operations(a, b):
sum = 0
sum += (a + b)
sum += (a - b)
sum += (a * b)
return sum
start = time.time_ns()
result = 0
for i in range(1000000):
result += do_math_operations(i*2, i)
end = time.time_ns()
print(result)
print(end - start)
Output:
666667666665000000
504218147
Mojo version:
let time = Python.import_module("time")
def do_math_operations(a: Int, b: Int) -> Int:
var sum: Int = 0
sum += (a + b)
sum += (a - b)
sum += (a * b)
return sum
start = time.time_ns()
var result: Int = 0
for i in range(1000000):
result += do_math_operations(i*2, i)
end = time.time_ns()
print(result)
print(end - start)
Output:
666667666665000000
2638
Again, the result is just mind-blowing. Simply no words. Around 190,000 times faster.
To test whether Mojo is really fully compatible with Python, I added a library from my previous project to predict whether it’s going to rain tomorrow. I didn’t change anything in the code, I just imported the library in Mojo and it ran successfully. It’s really fantastic to have this compatibility.
from PythonInterface import Python
Python.add_to_path(".")
let mypython = Python.import_module("my_python.predict_rain")
Only by importing the library the code is executed, because of the way the library is written.
Check this YouTube video that walks you through the Mojo playground and some examples to show you Mojo’s incredible performance. Also, I recommend you to go through, read and execute all of the notebooks in the playground, there are great and stunning examples with explanations.
🔥 Burn Mojo, Burn 🔥
I am really excited to have Mojo out and I see no reason why it wouldn’t be an immediate success. AI is present more and more in our lives (let’s put aside the question whether it is good or not, personally I think there should be limits) and I’m sure Mojo will be a great part of it. As things stand at the moment and the tests having been done so far, we have every reason to believe that Mojo’s flames will keep on burning, not to destroy, but to build and create …
Comments
Post a Comment