初めてのMojo🔥(体験版)

投稿日 2023-05-21
最終更新日 2023-11-18

「Pythonより35000倍速い」というフレーズが話題になっているMojo🔥を使ってみたのでその使用感を書きたいと思います。

Mojo🔥とは

Mojo🔥はスタートアップ企業であるModular Inc.が考案したプログラミング言語です。ただし、まだ完成していないようです。
「Mojo🔥」までが正式名称です。(絵文字の入力がめんどくさいので以後Mojoと書きます。)
Mojoの最大の特徴はCPythonに似た文法でいくつか最適化がされていると言うことです。
製作者によると、Pythonは非常に優れたエコシステムである一方で以下の2つのデメリットがあると記載しています。

  1. パフォーマンスが低い : Pythonでよく言われている問題です。これに加えて、製作者はこの問題の既存の解決方法についても言及しています。その例として、PyPyのJITコンパイラによる最適化・TorchScript等のサブセットの利用・@tf.function等のDSLの利用を挙げていますが、これらはいずれもMojoが目指すものと異なっていたり、非常に多くの制限を持っていると指摘しています。
  2. デプロイが難しい : モバイルやサーバーへのデプロイが難しいとのことです。具体的には「a.out」のようなもののデプロイの方法と、依存関係の解決方法を課題として挙げていました。


この2つの問題を解決したのがMojoということです。

現在、MojoはPythonモジュール呼び出すサポートをしており、numpyなどのPythonライブラリを簡単に呼び出すことができます。
一方で、Mojo自体にはPythonの完全互換性はなく、Pythonコードをコピペしただけでは動かない場合があるそうです。そのため、PythonからMojoへの変換ツールの提供も検討しているそうです。

ちなみに「Pythonより35000倍早い」というのはマンデルブロ集合の演算でシングルスレッドのPythonとマルチスレッドのMojoを比較した結果なので、実際には35000倍以上早い場面はかなり限られると思います。ただし、シングルスレッドでほぼ同じコードでも数倍程度は早くなるようです。

Mojo🔥(プレイグラウンド)の実施方法

先述の通りMojoはまだ正式版をリリースしておらず、OSSも公開されていません。
そのため、現在は簡単なプレイグラウンドのみ実施できます。
このプレイグラウンドを利用するには、以下のリンクからwaitlistに登録する必要があります。
https://www.modular.com/get-started
私の場合、waitlist登録後5日後にアクセス可能状態になりました。(登録する時期によって変わると思います)
プレイグラウンドはJupyterHub環境下でnotebook形式で実施できます。この中にすでにサンプルコードが記載されている、これを各自でアレンジしながら体験することができます。
なお、JupyterHubの利用可能時間は1人2時間のみです。
時間を超えた場合はドキュメントに同様のサンプルコードが記載されているため、そちらを見ながら調査することになると思います。

Mojo🔥を使って分かったこと

ここから実際にプレイグラウンドを利用してみたメモを共有します。(低レイヤーについてあまり詳しくないので途中までになります)

基本的な使い方

Pythonに非常に似た書き方でコーディングすることができます。
なお、実行はmojo {ファイル名} でできます。
拡張子は「.mojo」か「.🔥」のどちらかを使うことができます。

$ cat hello.🔥
def main():
    print("hello world")
    for x in range(9, 0, -3):
        print(x)
$ mojo hello.🔥
hello world
9
6
3
$


letとvar

Mojoでは変数宣言時にlet及びvarを利用することができます。ただし、JavaScriptとは少し違うので注意が必要です。
letで宣言すると変数の値の変更が不可になります。(JavaScriptのconstがこれに当たります。)
varで宣言した場合は値の変更が可能です。(JavaScriptのletに該当します。)
なお、以下のコードのようにvarで宣言していなくても、型の変更は不可となります。すなわちJavaScriptのvar宣言はMojoには存在しないことになります。

x = 10
x = "nutria"
> error: Expression [13]:31:9: 'StringLiteral' value cannot be converted to 'Int' in assignment


構造体

Mojoはまだクラスが未実装であり、代わりに構造体を使うことになります。
クラスが動的であるのに対して構造体は静的であるという違いがあります。
構造体はstruct で宣言します。構造体内の全てのプロパティはvarかletで宣言する必要があります。

struct MyPair:
    var first: Int
    var second: Int

    # We use 'fn' instead of 'def' here - we'll explain that soon
    fn __init__(inout self, first: Int, second: Int):
        self.first = first
        self.second = second

    fn __lt__(self, rhs: MyPair) -> Bool:
        return self.first < rhs.first or
              (self.first == rhs.first and
               self.second < rhs.second)


fnとdef

Mojoでは関数宣言の際、def以外にfnでも宣言することができます。
fnはいわゆるstrictモードとなっており、いくつかの制約があります。
具体的には以下の違いがあるそうです。

  • 引数が全てletのようにイミュータブルである
  • 引数に型指定が必須である。戻り値に型指定がない場合はNoneであるとみなされる
  • ローカル変数の暗黙的な宣言が無効ある
  • raisesを明示的に宣言する必要がある


Pythonライブラリの呼び出し

MojoではPythonInterface を利用することによって、Pythonライブラリを使用することができます。
以下が、numpy を利用する例になります。これによってPythonとの互換性を実現しています。

from PythonInterface import Python

# This is equivalent to Python's `import numpy as np`
let np = Python.import_module("numpy")

# Now use numpy as if writing in Python
array = np.array([1, 2, 3])
print(array)

なお、Mojoが用意しているプリミティブ型はPythonでそのまま使うことができます。
MojoではDictionaryが未実装ですが、PythonのDictionaryを操作することはできるようです。
Mojoで現在実装済みの組み込みクラスと標準ライブラリは公式ドキュメントの「Mojo library」の項目に網羅されています。
ここに記載されていないものはMojoでは未実装なのでPythonInterface を使う必要があるでしょう。

今回は以上になります。まだ、完成すらしていないですが今後の動向はチェックしたいですね。
実際に使ってみて、確かに学習コストは低いと感じました。Pythonコードをコピペしてちょっと変えれば動きそうです。
一方で、実用的なコードがどのくらい早くなるのかが気になりました。おそらくPythonInterface で呼び出されたライブラリがMojo用に最適化されることはないと思うので、Mojo特有の記述をどれだけするかによって、速度が変わってくるような気がします。
また、ディープラーニングの学習フェーズでは大部分がGPUによる処理のため、さほど速度が変わらないように感じます。このあたりは2つ目のメリットであるデプロイ容易性によって評価が変わるかもしれません。(まだ情報を追いきれてないです。)

最新言語をいち早く理解するためにコンパイラなどの低レイヤーの話も重要になってくることもわかりました。このあたりもいつか勉強してみたいですね。


参考

公式ドキュメント : https://docs.modular.com/mojo/