pybind11でclassを<vector>にしてC++側でマルチスレッド処理する

[pybind11](https://github.com/pybind/pybind11)ではintやdoubleといった基本型だけでなく、
自作のclassも引数や返り値で使えるようだ。
intはstd::vector<int>とかstd::valarray<int>で渡すことができた。
classもstd::vector<My_Class>とすれば渡せるようだ。

#C++のマルチスレッドサンプル

まずはC++のコードから。
main関数があるので普通のexeプログラムだ。
CTestクラスは1つ当たり3個のdouble変数を持ち、
main()で3600万回ループを回しているので、
およそ1億個の乱数を生成していることになる。
手元のマシンでは3200ミリ秒ほど。
タスクマネージャーで見ても全てのコアが使われている。

```cpp:main.cpp
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;

#include <array>
#include <string>
#include <chrono>
#include <iostream>
#include <random>
#include <thread>
#include <future>


class CTest {
public:
	std::array<double, 3> data;
	void set(const std::array<double, 3> &x) {
		data = x;
	}
	CTest() {};
	~CTest() {};
}

// 乱数生成
// output 確保済み領域のポインタ
// number 発生させる乱数の個数
// rand_min, rand_max 乱数の範囲
void MT_RAND_vec(double* output, const int &number, const double &rand_min, const double &rand_max) {
	std::random_device rd;
	std::mt19937 mt(rd());
	std::uniform_real_distribution<double> MT_RAND(rand_min, rand_max);
	for (auto i = 0; i < number; ++i) {
		output[i] = MT_RAND(mt);
	}
}

// 汎用マルチスレッドルーチン
void MT(void(*func)(std::vector<CTest>*, int, int), std::vector<CTest>* input, const int length) {
	const int num_thread = 12; // 最大スレッド数
	const int unit = length / num_thread; // 1スレッド当たりの処理量
	int step = 0;
	std::vector<std::thread> t(num_thread); // スレッドオブジェクト生成
	for (auto i = 0; i < num_thread; ++i) { // スレッドを順次生成
		t[i] = std::thread(func, input, step, unit);
		step += unit;
	}
	// 全てのスレッドが終了するまで待機
	for (auto& threads : t) {
		threads.join();
	}
}

// 子スレッドにする関数
// startの位置からnumber個の乱数を生成して書き込む
void TH_set_RAND(std::vector<CTest>* input, const int start, const int number) {
	double *temp = new double[number * 3];
	MT_RAND_vec(temp, number * 3, 0.0, 256.0);
	for (auto i = 0; i < number; ++i) {
		(*input)[start + i].set({ temp[3 * i] , temp[3 * i + 1] , temp[3 * i + 2] });
	}
	delete[]temp;
}

// <vector>を与えるとマルチスレッドルーチンを起動させる
std::vector<CTest> vecMT_set_RAND(std::vector<CTest> input) {
	int length = static_cast<int>(input.size());
	std::vector<CTest>* ptr = &input;
	MT(TH_set_RAND, ptr, length);
	return input;
}

int main() {
	std::chrono::system_clock::time_point  start, end; // 型は auto で可
	start = std::chrono::system_clock::now(); // 計測開始時間

	int loop_number = 36000000;
	std::vector<CTest>X(loop_number);
	vecMT_set_RAND(X);

	end = std::chrono::system_clock::now();  // 計測終了時間
	double elapsed = static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()); //処理に要した時間をミリ秒に変換
	std::cout << "処理時間は" << elapsed << "ミリ秒です。" << '\n';

    return nRetCode;
}
```

#Pythonから呼んでみる

多少メソッドを追加しつつ……
dllのプロジェクトにコードをコピーして書いていく。

```cpp:dll.cpp
PYBIND11_PLUGIN(CTestLib) {
	py::module m("CTestLib", "pybind11 example plugin");
	py::class_<CTest>(m, "CTest")
		.def(py::init())
		.def("set", &CTest::set)
		.def("set_RAND", &CTest::set_RAND);
	m.def("vec_create", &vec_create, "C++");
	m.def("vecMT_set_RAND", &vecMT_set_RAND, "C++");
	return m.ptr();
}
```

```py3:dllTest.py
import CTestLib
Temp_vector = CTestLib.vec_create(1200000)
Rand_vector  = CTestLib.vecMT_set_RAND(Temp_vector)
print (Rand_vector[1199999].data)
```

これで動くんだ。
ちゃんと120万個のクラス配列に乱数が3つずつ格納されてる。
ただしこれで同じく3000ミリ秒ぐらい。
ざっと30倍差はついてる。
タスクマネージャーを見ると1コア分しか使ってないようで、
それを加味しても3倍以上の速度差がある。
これはC++のクラス配列をPythonのクラス配列に変換するときのオーバーヘッドなのだろうか。
どっちにしろ普通にマルチスレッドにしても意味がないな。