目次
前置き
PythonからRustを呼んで高速化する方法です
Pyo3は上手くいかなかったので今回は使わない方法を紹介します
そのままのPythonでは呼べないのでCythonを使います
またCythonとRustの仲介役としてC++を使います(文字列の扱いが簡単になる)
Rust
まずは作成
cargo new rust_to_py --lib
libをsrcに名前を変えておいてください
[package]
edition = "2018"
name = "rust_to_py"
version = "0.1.0"
- [lib]
+ [src]
crate-type = ["cdylib"]
name = "rs_library"
use std::{ffi::CStr, os::raw::c_char};
fn _print(code: &str) -> i32 {
println!("{}",&code);
return 0;
}
#[no_mangle]
pub extern "C" fn rs_fact(x: i32) -> i32 {
if x <= 0 {
return 1;
}
return rs_fact(x - 1) * x;
}
#[no_mangle]
// *const c_charはC言語の文字列を受け取る型
pub extern "C" fn rs_print(
code: *const c_char
) -> i32 {
return _print(unsafe { CStr::from_ptr(url) }.to_str().unwrap());
}
C++用にヘッダーファイルを作っておきましょう
#pragma once
namespace rs_to_py_lib {
extern "C" {
int rs_fact(int x);
int rs_print(const char *code);
}
}
C++
今回C++は二つのサンプルを用意 文字列を渡す方法 C++経由で呼ぶ方法
//lib/cpp/main.cpp
#include "main.hpp"
#include <iostream>
namespace cpp_main_lib {
int c_rs_print(std::string code) {
//c_str()でC言語の文字列に変換
return rs_to_py_lib::rs_print(code.c_str());
}
int c_fact(int x) {
if (x <= 0) {
return 1;
}
return c_fact(x - 1) * x;
}
int c_rs_fact(int x) { return rs_to_py_lib::rs_fact(x); }
}
しっかり宣言しておきましょう
//lib/include/main.hpp
#pragma once
#include <string>
#include "rs.hpp"
namespace cpp_main_lib {
int c_rs_print(std::string code);
int c_fact(int x);
int c_rs_fact(int x);
}
Cython
直で呼ぶ方法もあるのでサンプルに載せています
# lib/cython/modules.pyx
from libcpp.string cimport string
cdef extern from "../include/main.hpp" namespace "cpp_main_lib":
cdef int c_rs_print(string code)
cdef extern from "../include/main.hpp" namespace "cpp_main_lib":
cdef int c_fact(int x)
cdef extern from "../include/main.hpp" namespace "cpp_main_lib":
cdef int c_rs_fact(int x)
cdef extern from "../include/rs.hpp" namespace "rs_to_py_lib":
cdef int rs_fact(int x)
# lib/cython/main.pyx
include "modules.pyx"
from libcpp.string cimport string
cpdef int py_c_rs_print(string code):
return c_rs_print(code)
#一番早い
cpdef int py_c_fact(int x):
return c_fact(x)
#一番遅い
cpdef int py_c_rs_fact(int x):
return c_rs_fact(x)
#中間
cpdef int py_rs_fact(int x):
return rs_fact(x)
Cythonの設定
# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
sources = ["./lib/cython/main.pyx","./lib/cpp/main.cpp"]
setup(
cmdclass=dict(build_ext=build_ext),
ext_modules=[
Extension(
name='calc',
sources=sources,
language='c++',
include_dirs=['./lib/include'],
extra_compile_args=["-stdlib=libc++", "-Wdeprecated"],
library_dirs=["./target/release"],
libraries=["rs_library"],
)
]
)
Build
STEP
cargo build --release
STEP
sudo CFLAGS=-stdlib=libc++ python setup.py build_ext -i
STEP
するとsetup.pyのnameで指定した値(今回の例だとcalc)でimportできるようになります 文字列はPython3はバイト文字に変換する必要があります
文字列の前にb
またはencode('文字コード')
import calc
x = 2
print(calc.py_c_fact(x))
print(calc.py_c_rs_fact(x))
print(calc.py_rs_fact(x))
print(calc.py_c_rs_print(code.encode('utf-8')))
まとめ
いかがだったでしょうか?
以上PythonからRustを呼んで高速化する方法でした