CodonをJupyterLabで使う

# 背景
なにやらCodonとかいうポケモンみたいなPythonコンパイラがC++並みに速いらしい。
[ベンチマーク](https://exaloop.io/benchmarks)を覗いた感じ、2~200倍くらい？

調べてもJupyter上で使う方法が出てこなかったので、Docker上のUbuntuのJupyterで使えるようにするための備忘録を残しておく。
なお、進捗としてはcodonの形式で書かれたアッカーマン関数のプログラムを動かすまでとする。

参考:
1.[Build errors on ubuntu 20.04 LTS](https://github.com/exaloop/codon/issues/299)
2.[あなたのPythonを100倍高速にする技術 / Codon入門](https://zenn.dev/turing_motors/articles/e23973714c3ecf)


# 環境
環境としては以下の通り:
PC:Macbook pro 2018 i5
Docker for Desktop: 4.17.0
Image:Jupyter/base-notebookをいじったもの
(Ubuntu22.04,Python3.10(Conda))
前提として、Jupyter環境はすでに整っているものとします。

# Codonをビルドする。
めんどくさいことに、Jupyterを使うにはソースからビルドする必要があるらしい。
コンテナ内に入って作業をする。
ビルドにはninjaというものを使っているらしい。C++で書かれているらしいので、c++関連のものと追加で必要そうなライブラリをインストールした。
```bash
sudo apt update
sudo apt install -y clang-12 cmake ninja-build libxml2-dev libsodium-dev uuid-dev
````
```bash
git clone --depth 1 -b codon https://github.com/exaloop/llvm-project
cmake -S llvm-project/llvm -B llvm-project/build -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DLLVM_INCLUDE_TESTS=OFF \
    -DLLVM_ENABLE_RTTI=ON \
    -DLLVM_ENABLE_ZLIB=OFF \
    -DLLVM_ENABLE_TERMINFO=OFF \
    -DLLVM_TARGETS_TO_BUILD=all
cmake --build llvm-project/build
cmake --install llvm-project/build
```
結構時間がかかるので、寝る前とかにやることを推奨。
ただし、<b>Ninja</b>などがインストールされていないと失敗するので先にインストールした。
もしエラーが出た際はエラーを調べれば何をインストールすればいいのか大体でてくるので大丈夫だと思われる。
その後、件のプロジェクトをcloneしてくる。
今回はllvm-project下にcodonをcloneしてくる(どこでもいいと思う..)
```bash
cd llvm-project
git clone https://github.com/exaloop/codon.git
```
Jupyterで使うために手直しが必要らしいので、codon/build/_deps/xeus-src/CMakeLists.txtを手直し。参考にしたissueの一部を修正したらbuildが通るようになった。


```CMake
--- build/_deps/xeus-src/CMakeLists.txt.orig	2022-10-12 18:38:44.000000000 -0700
+++ build/_deps/xeus-src/CMakeLists.txt	2022-10-12 19:55:16.000000000 -0700
.....
.....
 # Dependencies
 # ============
@@ -310,13 +312,15 @@ macro(xeus_create_target target_name linkage output_name)
         if (APPLE)
             target_link_libraries(${target_name} PUBLIC "-framework CoreFoundation")
         else ()
-            if (XEUS_STATIC_DEPENDENCIES)
+            if ((NOT XEUS_USE_DYNAMIC_UUID) AND (XEUS_STATIC_DEPENDENCIES))
+                message(STATUS "static libuuid linking")
                 find_path(LIBUUID_INCLUDE_DIR uuid.h PATH_SUFFIXES uuid)
                 find_library(LIBUUID_LIBRARY libuuid.a)
                 target_include_directories(${target_name} PRIVATE ${LIBUUID_INCLUDE_DIR})
                 target_link_libraries(${target_name} PUBLIC ${LIBUUID_LIBRARY}) 
                 target_link_libraries(${target_name} PUBLIC ${sodium_LIBRARY_RELEASE})
             else ()
+                message(STATUS "dynamic libuuid linking")
                 find_package(LibUUID REQUIRED)
                 target_link_libraries(${target_name} PUBLIC LibUUID::LibUUID)
.....
.....
```
あとは以下のコマンドでbuildをする。
```bash

cmake -S . -B build -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DLLVM_DIR=$(llvm-config --cmakedir) \
    -DCMAKE_C_COMPILER=clang-12 \
    -DCMAKE_CXX_COMPILER=clang++-12 \
    -DCODON_JUPYTER=ON \
    -DXEUS_USE_DYNAMIC_UUID=ON
cmake --build build --config Release 
```


# Jupyterのcodonカーネルを用意する
jupyterは起動できるものとします。
もし準備できていない人は準備してください。

```bash
jupyter kernelspec list

```
で自身のjupyterのカーネルの設定ファイルのPathを確認する。
```
Available kernels:
  python3    /opt/conda/share/jupyter/kernels/python3
```
のように表示される場合は、/opt/conda/share/jupyter/kernels/にcodonのカーネル設定を入れたcodonフォルダを作成する。
他の環境の場合は、多分<環境による>/share/jupyter/kernels/になっていると思われる?

```bash
cd /opt/conda/share/jupyter/kernels/
mkdir codon
cd codon
touch kernel.json
```
vimなりnanoなり、好きなテキストエディタでkernel.jsonを作成する。
```kernel.jsonのテンプレ
{
    "display_name": "Codon",
    "argv": [
        "/path/to/codon/build/codon",*****ここをビルドしたcodonへのpathに変更する必要がある。
        "jupyter",
        "{connection_file}"
    ],
    "language": "python"
}
```
もし/optにllvm-projectをclone/buildし、そのllvm-project下にcodonをclone/buildした場合は以下になる。
```kernel.jsonの例
{
    "display_name": "Codon",
    "argv": [
        "/opt/llvm-project/codon/build/codon",
        "jupyter",
        "{connection_file}"
    ],
    "language": "python"
}
```

これで、jupyter環境を再起動することで、codonカーネルを選択することができるようになる。

![スクリーンショット 2023-04-04 21.07.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321651/8084488c-0fbd-b241-be6f-0941a96be3ff.png)


# サンプルプログラムを動かす
サンプルプログラムを参考サイトから拝借してきた。
```Python
def calc_ackermann():
    import time
    #import sys#Pythonでは必要
    #sys.setrecursionlimit(100000)#Pythonでは必要

    def ackermann(m: int, n: int) -> int:
        if m == 0:
            return n + 1
        elif n == 0:
            return ackermann(m - 1, 1)
        else:
            return ackermann(m - 1, ackermann(m, n - 1))

    m = 3
    n = 11

    t0 = time.time()
    result = ackermann(m, n)
    t1 = time.time()

    print(f"ackermann({m}, {n}) = {result}")
    print(f"elapsed time: {int((t1 - t0) * 1000)} [ms]")
calc_ackermann()
```
Pythonカーネルの場合:42897ms
![スクリーンショット 2023-04-04 21.09.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321651/2bac1b60-c844-74b4-f2af-e46348f5fd47.png)


Codonカーネルの場合:197ms
![スクリーンショット 2023-04-04 21.09.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321651/a0a2662a-40d3-5164-e380-389c603bab82.png)

うーん、速い。

# 最後に
CodonがJupyterをサポートしてるのに、初期はビルドしてないのは多分ライブラリのライセンス関係っぽいので、今後も必要になってくるかもしれない。

また、Pythonライブラリだが、
```
export CODON_PYTHON=/path/to/libpython.X.Y.so
```
で環境せっていしたあとにごにょごにょする必要があるっぽい？

```#
from python import ほにゃらら
or
#pip install codon-jitが必要
import codon
@codon.jit(pyvars=['ほにゃらら'])
def .....

```
で呼べるらしいがエラーがでてできなかった。
```python
Runtime error: libpython.so: cannot open shared object file: No such file or directory
```

設定してもうまくいかないし、ビルドし直す必要があるのかもしれない....
もし知っている方がいれば教えてほしいです...

# 追記
jupyter notebook --generate-configで生成される設定ファイルにos.environ["CODON_PYTHON"]=/path/to/libpython.X.Y.soみたいな感じで環境変数を設定するようにしたらfrom python import \<\<python library>>が動いた！