C++でもnumpyっぽく使えるxtensorを試してみます。
インストール
まずは、インストール。次のスクリプトを実行します。
#!/bin/bash
git clone https://github.com/xtensor-stack/xtensor.git
git clone https://github.com/xtensor-stack/xtl.git
git clone https://github.com/xtensor-stack/xsimd.git
git clone https://github.com/xtensor-stack/xtensor-blas.git
INSTALL_PATH=$(pwd)/root
for x in xtl xsimd xtensor xtensor-blas
do
echo "-------- $x ----------"
cd $x
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_PATH ..
make install
cd ../..
done
行列積の計算時間
行列積の計算時間を測定してみます。測定する処理を書いたC++のコードは次の通り。10000x10000の2つの行列の積を計算し、その後、平均値を計算します。これを5回繰り返します。なお、平均値は10000になります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | #include <iostream>
#include "xtensor.hpp"
#include "xtensor-blas/xlinalg.hpp"
void dot(void){
xt::xarray<float> a = xt::ones<float>({10000, 10000});
xt::xarray<float> b = xt::ones<float>({10000, 10000});
for(int i=0; i<5; ++i){
auto d = xt::linalg::dot(a, b);
auto c = xt::average(d);
std::cout << c << std::endl;
}
}
int main(void){
dot();
return 0;
}
|
このファイルを
dot.cppとして保存し、次のような
CMakeLists.txtを作成します。
xtensor_pathの値は適切なものに書き換えてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | cmake_minimum_required(VERSION 3.10)
project(xtexample VERSION 1.0)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_definitions(-DHAVE_CBLAS=1)
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)
add_executable(xtexample dot.cpp)
set(XTENSOR_USE_XSIMD ON)
set(xtensor_path "/path/to/xtensor/root")
set(xtensor_DIR "${xtensor_path}/lib/cmake/xtensor")
set(xtl_DIR "${xtensor_path}/lib/cmake/xtl")
set(xsimd_DIR "${xtensor_path}/lib/cmake/xsimd")
find_package(xtensor REQUIRED)
target_include_directories(xtexample PUBLIC ${xtensor_INCLUDE_DIRS})
target_link_libraries(xtexample PUBLIC xtensor xtensor xtensor::optimize xtensor::use_xsimd
${BLAS_LIBRARIES} ${LAPACK_LIBRARIES})
|
ビルドします。
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ make
処理時間を測定します。
$ for _ in {1..4}; do time ./xtexample; done
結果は次の通りです。4コア8スレッドのCPUで測定しています。
Trial # | real | user | sys |
1 | 0m43.805s | 5m17.723s | 0m23.991s |
2 | 0m46.952s | 5m44.626s | 0m21.066s |
3 | 0m49.060s | 5m59.719s | 0m22.163s |
4 | 0m50.819s | 6m9.617s | 0m25.812s |
Python numpyとの比較
比較のため、Pythonのnumpyで同じことをしてみます。測定コードは以下の通り。
| import numpy as np
def dot():
a = np.ones((10000, 10000), dtype=np.float32)
b = np.ones((10000, 10000), dtype=np.float32)
for i in range(5):
d = np.dot(a, b)
c = np.average(d)
print(c)
dot()
|
C++の場合と同様に実行します。
for _ in {1..4}; do time python3 comp.py; done
結果は次の通り。
Trial # | real | user | sys |
1 | 0m44.816s | 5m29.361s | 0m17.726s |
2 | 0m49.529s | 6m5.012s | 0m21.217s |
3 | 0m49.188s | 5m58.570s | 0m24.681s |
4 | 0m50.497s | 6m11.620s | 0m22.015s |
C++と同じくらいの結果となりました。
まとめ
C++でnumpyのように行列計算を使いたい場合は、xtensorを使えばよさそうです。
処理速度に関しても、少なくとも行列積に関してはnumpyに比べて遅いわけではないようです。