ロゴ ロゴ

Pythonのsub-interpretersを使いこなしたかった…

まえがき

最近は学業が忙しく割と死んでました.ウグイスです.

Python3.12からの機能としてsub-interpretersというものがあります.

わざわざこんなブログを見ている人には説明自体不要かもしれませんが,一応説明いたしますと

PythonにはGlobalInterpreterLockというものがあります.これは端的に言いますと,Pythonでは一つのプロセスにつき同時に一つのCPUしか利用できないというものです.今までPythonではプロセスが一つのCPUしか利用できない以上,スレッドを作ったところで同時に複数のCPUを利用できませんでした.(ちなみにCpythonのみの仕様)

今までのPythonではCPU負荷の高い並列処理を行わせたい場合,プロセスを用いる方法はありました.ただプロセスを用いるとデータの転送コストがかかってしまいます.何らかのプロセス間通信,またはファイル書き込みなどを行う必要があり,転送コストが並列化による計算時間短縮効果を減じてしまいます.

そこでこのsub-interpretersを使うとあら不思議,同時に複数のスレッドを実行できるようになるというものです.

まあ論より証拠ということで早速使っていきましょう

環境

以下が実行環境です.まあPythonのバージョンさえあってれば動く気はしますが.

CPU: Core i7-12700KF
OS: Ubuntu 22.04
Memory: 16GB x2
Python: 3.12.0

Hello sub-interpreters

最初から難しいのをやってもいいのですが,チュートリアルからやるのがセオリーというものです.

というわけで複数のサブインタプリタを使ったprintから初めて行きましょう.これは何が楽ってスレッドに渡したい情報も,スレッドから返す情報もないので実装が簡単です.

というわけでまずは以下のプログラムを実行します

import _xxsubinterpreters as interpreters

exe_script = """print("Hello, sub-interpreters")"""
interp_id = interpreters.create()
interpreters.run_string(interp_id, exe_script)
interpreters.destroy(interp_id)

実行してHello, sub-interpretersと出れば成功です.

見りゃわかりますが,create()でサブインタプリタを作り,run_string()にインタプリタのidと実行してほしいPythonScriptを文字列で渡してあげると実行してくれます.

destroy()でサブインタプリタを破棄できます.プログラムの終了時に回収されると思いますが,一応使ったものは片づけましょう

データをサブインタプリタに渡したい

まず基本的な動かし方はわかりました.

次は当然,関数に引数を渡すような感じでデータを渡したいと思いますよね?

というか渡せないと数値計算で使いにくいのが明らかです.

データを渡すためにはsharedというものを使います.コードは以下

import _xxsubinterpreters as interpreters

exe_script = """print(message)"""
interp_id = interpreters.create()
interpreters.run_string(interp_id, exe_script, shared={'message': 'Hello, sub-interpreters'})
interpreters.run_string(interp_id, exe_script, shared={'message': 'Hello, Den3'})
interpreters.destroy(interp_id)

こうすると2つのメッセージが表示されますね.

run_string()のキーワード引数sharedに辞書型でデータを渡すことができます.

ちな,int, float, bool, bytes, str, None, tuple以外のデータを共有することはできません

データをメインに戻したい

数値計算で並列処理をする場合はメインのインタプリタに戻せないと困りますよね?

もちろん方法が存在しています.チャンネルというものを使えば行けます.

以下がサンプルプログラムです.

import _xxsubinterpreters as interpreters
import _xxinterpchannels as channels

exe_script = """
import _xxinterpchannels as channels

print(f"input:{input}")
channels.send(channel_id, input**2)
"""
channel_id = channels.create()
interp_id = interpreters.create()
interpreters.run_string(interp_id, exe_script, shared={'input': 2, 'channel_id': channel_id})
interpreters.run_string(interp_id, exe_script, shared={'input': 3, 'channel_id': channel_id})
print(channels.recv(channel_id))
print(channels.recv(channel_id))
interpreters.destroy(interp_id)

channels.createでチャンネルを作成します.channels.sendでデータを送ってchannels.recvで受け取ります.

これを実行すると,

input:2
input:3
4
9

こうなると思います.こういう風にできれば何かに使えそうな気がしてきませんか?

実際のアプリケーションを動かそうとした…

並列処理といえばマンデルブロ集合ですよね.確かmojoのデモにも使われていたと思います.

というわけでchatGPTにマンデルブロ集合を描くプログラムを作ってもらいました.

import numpy as np
import matplotlib.pyplot as plt

def mandelbrot(c, max_iter):
    z = 0
    n = 0
    while abs(z) <= 2 and n < max_iter:
        z = z**2 + c
        n += 1
    if n == max_iter:
        return max_iter
    return n + 1 - np.log(np.log2(abs(z)))

def mandelbrot_set(width, height, x_min, x_max, y_min, y_max, max_iter):
    x, y = np.meshgrid(np.linspace(x_min, x_max, width), np.linspace(y_min, y_max, height))
    c = x + 1j * y
    mandelbrot_image = np.vectorize(lambda c: mandelbrot(c, max_iter))(c)
    return mandelbrot_image

def plot_mandelbrot(width, height, x_min, x_max, y_min, y_max, max_iter):
    mandelbrot_image = mandelbrot_set(width, height, x_min, x_max, y_min, y_max, max_iter)
    plt.imshow(mandelbrot_image, cmap='hot', extent=(x_min, x_max, y_min, y_max))
    plt.colorbar()
    plt.title('Mandelbrot Set')
    plt.show()

# パラメータの設定
width, height = 800, 800
x_min, x_max = -2, 2
y_min, y_max = -2, 2
max_iter = 100

# マンデルブロ集合の描画
plot_mandelbrot(width, height, x_min, x_max, y_min, y_max, max_iter)

で,動かそうとしてソースコードをいじっていたんですが大問題が発生しました.

どうにも一部のライブラリ(numpyとかmatplotとかetc..)と相性が悪く,importした時点でうまく動きません.

なのでsub-interpreterを使って数値計算を行いたい場合は,numpyを抜いて使う必要があります???????

まとめ

以上,オブラートに包んで言うと,使いにくいです.現状では所詮実験的機能ですねという感じです.

まあ,私程度では使いこなせませんでしたということで結論とさせていただきます.

まあ今後に期待ですかね.Pythonで並列処理自体は需要があると思うのでより洗練されて使いやすい機能になることを期待しています.

最近の報告

最近あったこととしては,キーボードを少し改造しました.

前のブログで使っていた軸から,outemuのLemon軸に変えてみました,静音という意味ではそこまでは変わらないかなという感じですが,少し重いので個人的には使いやすいです.

ただ,キーキャップを変えて遊んだ感じだと,キャップとの相性によっては擦過音みたいなのが聞こえるのでそこはいまいちでした.

あと,キートップを緑色のに変えました.なんか抹茶チョコレートみたいな感じの色合いです.

形が前とは変わって,かなりフラットな感じになりました.どっちが使いやすいかはまだわからないですが…

コメント入力

関連サイト