TadaoYamaokaの日記

山岡忠夫Homeで公開しているプログラムの開発ネタを中心に書いていきます。

PythonからC#へポインタ渡しする

C#からPythonへポインタ渡しするとは逆に、Python for .NETを使ってPythonからC#へポインタ渡しする方法について紹介する。

Python for .NET

Python for .NETを使用すると、PythonからC#のコードを呼び出すことができる。
Pythonと同じプロセスで、.NETのランタイムを動かすことができ、C#で記述したコードをPythonのモジュールと同じように扱うことができる。

import clr
from System import String
from System.Collections import *

Python for .NETのインストール

pipコマンドで、インストールする。

pip install pythonnet

C#側のコード作成

.NET Core 3.1を使用して、C#のコードを作成する。

クラスライブラリのプロジェクトを作成する。

dotnet new classlib --name torch_sharp

以下の通りC#のコードを記述する。

using System;

namespace torch_sharp
{
    public class Class1
    {
        public unsafe void func(IntPtr data, int size)
        {
            float* array = (float*)data.ToPointer();
            for (int i = 0; i < size; ++i)
            {
                array[i]  = i;
            }
        }
    }
}
解説

Pythonからは、IntPtr型でポインタを受け取り、それをToPointer()でfloat*に型変換して、float型の配列として扱っている。
Python側でもfloat型になっていることを前提にしている。

Python側のコード作成

以下の通りPythonのコードを記述する。

import clr
clr.AddReference('torch_sharp')
from torch_sharp import Class1
from System import IntPtr

import numpy as np

obj = Class1()

data = np.empty(5, dtype=np.float32)
obj.func(IntPtr.op_Explicit(data.ctypes.data), len(data))

print(data)
解説
clr.AddReference('torch_sharp')
from torch_sharp import Class1

この部分で、C#のクラスライブラリをPythonモジュールとしてimportしている。

ビルドしたC#の.dllファイルは、Pythonモジュールと同じディレクトリに配置しておく必要がある。
C#のプロジェクトファイル(.csproj)で、

  <Target Name="CopyDLL" AfterTargets="AfterBuild">
    <Copy SourceFiles="$(OutDir)/torch_sharp.dll" DestinationFolder="../../torch_sharp/" />
  </Target>

のように記述しておくとよい。

Python側からC#にポインタ渡しするデータは、Numpyで作成する。

IntPtr.op_Explicit(data.ctypes.data)

の部分で、Numpyのデータをポインタとして取り出して、C#のIntPtr型に変換している。
ポインタを数値型のままC#に渡して、C#側でIntPtrにしてもよいが、IntPtrで渡した方が32bit、64bitかを気にしなくてよい。

まとめ

PythonからC#へポインタ渡しする方法について紹介した。

C#のアプリケーションで作成したデータを使って、Pythonでモデルを学習したいというようなケースで、C#のデータをシリアライズして、Python側で読み込みたいような場合に、PythonからC#のコードが呼べると便利である。
モデルに入力するためのデータの前処理を行うとデータ量が増えるため、シリアライズデータは加工前の状態にしたいが、Pythonで加工を行うと遅いため、速度を考慮するとC#で加工を行いたい。
しかし、PythonC#の間で、データのマーシャリングが発生すると呼び出しのオーバーヘッドが発生するので、ここで紹介した方法でポインタ渡しをするとオーバーヘッドを抑えられる。