如何用C代码给Python写扩展库(Cython)
作者:jazz_bin 发布时间:2023-06-08 17:06:32
之前一篇文章里提到了利用Cython来编译Python,这次来讲一下如何用Cython给Python写扩展库。
两种语言混合编程,其中最重要的是类型的传递。
我们用一个简单的例子进行入门:这次的目标是用C语言写一个Numpy的加法和元素相乘模块。在本例中,Numpy的array被传入到C语言模块内,变成了二维数组。
1. 头文件main.h:
#ifndef _MAIN_H
#define _MAIN_H
void plus(double *a, double *b, double *r, int n, int m); // 矩阵加法
void mul(double *a, double *b, double *r, int n, int m); // 矩阵按元素相乘
void main(double *a, double *b, double *r, int n, int m, int times); // 用于测试的main函数
#endif
2. 把主要代码写在main.c中:
#include "main.h"
/***********************************
* 矩阵的加法
* 利用数组是顺序存储的特性, *
* 通过降维来访问二维数组! *
* r
***********************************/
void plus(double *a, double *b, double *r, int n, int m)
{
int i, j;
for(i = 0; i < n; i++)
{
for(j = 0; j < m; j++)
*(r + i*m + j) = *(a + i*m + j) + *(b + i*m + j);
}
}
/***********************************
* 矩阵的按元素乘法
* 利用数组是顺序存储的特性, *
* 通过降维来访问二维数组! *
* r
***********************************/
void mul(double *a, double *b, double *r, int n, int m)
{
int i, j;
for(i = 0; i < n; i++)
{
for(j = 0; j < m; j++)
*(r + i*m + j) = *(a + i*m + j) * *(b + i*m + j);
}
}
/***********************************
* main函数
* 利用数组是顺序存储的特性, *
* 通过降维来访问二维数组! *
* r
***********************************/
void main(double *a, double *b, double *r, int n, int m, int times)
{
int i;
// 循环times次
#pragma omp parallel for
for (i = 0; i < times; i++)
{
// 矩阵的加法
plus(a, b, r, n, m);
// 矩阵按元素相乘
mul(a, b, r, n, m);
}
}
这个main.c中实现了矩阵的加法、矩阵按元素相乘的功能,用到的数据结构是二维数组,但是因为C语言中给函数传递二维数组比较麻烦,这里用降维的方法实现。另外在main()函数中,采用一个循环来进行测试,以测试性能。
3. 下面编写test.pyx文件来调用上述C函数(注意,后缀是.pyx噢):详细的知识点在注释中写出来了~
# 既要import numpy, 也要用cimport numpy
import time
import numpy as np
cimport numpy as np
# 使用Numpy-C-API
np.import_array()
# cdefine C 函数
cdef extern from "main.h":
void plus(double *a, double *b, double *r, int n, int m)
void mul(double *a, double *b, double *r, int n, int m)
void main(double *a, double *b, double *r, int n, int m, int times)
"""
# 定义一个"包装函数", 用于调用C语言的main函数,调用范例:plus_fun(a, b, r)
# 在这里要注意函数传入的参数的类型声明,double表示数组的元素是double类型的,
# ndim = 2表示数组的维度是2
# 在调用main函数时,要把python的变量强制转化成相应的类型(以确保无误),比如<int>
# 当然,基本类型如int,可以不显式地写出来,如下面的a.shape[0]、a.shape[1]
"""
def main_func(np.ndarray[double, ndim=2, mode="c"] a not None,
np.ndarray[double, ndim=2, mode="c"] b not None,
np.ndarray[double, ndim=2, mode="c"] r not None,
times not None):
main(<double*> np.PyArray_DATA(a),
<double*> np.PyArray_DATA(b),
<double*> np.PyArray_DATA(r),
a.shape[0],
a.shape[1],
<int> times)
4. 为了用Cython编译上述代码,我们创建一个setup.py文件:
import numpy
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
filename = 'test' # 源文件名
full_filename = 'test.pyx' # 包含后缀的源文件名
setup(
name = 'test',
cmdclass = {'build_ext': build_ext},
ext_modules=[Extension(filename,sources=[full_filename, "main.c"],
include_dirs=[numpy.get_include()])],
)
5. 上述的main.h、main.c、test.pyx一定要放在同一个文件夹下。此时在该文件夹下按住shift键,然后右击鼠标,打开cmd或PowerShell控制台,在控制台中运行以下命令进行Cython编译:
python setup.py build_ext --i
或者:
python setup.py build_ext --inplace
编译成功的图例:
此时在同目录下会生成“test.cp36-win_amd64.pyd”的二进制码文件,它是闭源的,但是可以直接用python来import。下面编写测试代码main.py来进行测试:
import test
import time
import numpy as np
start_time = time.time()
a = np.random.rand(100, 100) * 2 - 1 # 生成300*300的随即矩阵
b = np.random.rand(100, 100) * 2 - 1
r = np.empty_like(a) # 创建一个空矩阵,用来存储计算结果
test.main_func(a, b, r, 500000) # 调用main_func进行测试
end_time = time.time()
print(end_time - start_time) # 输出时间
print(r) # 输出运行结果
执行结果:
通过本例我们可以看到:将循环放在C语言模块中,而不是原生的Python中,可以提高执行效率。
来源:https://blog.csdn.net/qq_33353186/article/details/80298239
猜你喜欢
- odeJs 微信公众号功能开发,移动端 H5页面调用微信的支付功能。这几天根据公司的需要使用 node 和 h5页面调用微信的支付功能完成支
- 1.数据和日志文件分开存放在不同磁盘上数据文件和日志文件的操作会产生大量的I/O。在可能的条件下,日志文件应该存放在一个与数据和索引所在的数
- 背景对接多个外部接口,需要保存请求参数以及返回参数,方便消息的补偿,因为多个外部接口,多个接口字段都不统一,整体使用一个大字段(longte
- 本文介绍了Python小波分析库Pywavelets,分享给大家,具体如下:# -*- coding: utf-8 -*- import n
- 如果你在文件夹里有很多视频,并且文件夹里还有文件夹,文件夹里的文件夹也有视频,怎么能逐个读取并且保存。。所以我写了个代码用了os,walk,
- 我就废话不多说了,大家还是直接看代码吧~#编写程序将列表中的偶数变成他的平方def word_len(s): # s = [i
- 按照本文操作和体会,会对sql优化有个基本最简单的了解,其他深入还需要更多资料和实践的学习: 1. 建表: 代码如下:creat
- mysql建表test;安装logstash(跟es版本一致)# 下载wget https://repo.huaweicloud.com/l
- W3C终于发布了第一个HTML5草案,大家还沉溺在HTML2XHTML转换的快乐和痛苦中时,却又突然发现,HTML5和XHTML2,到底谁是
- 前言学会向程序中添加必要的注释,也是很重要的。注释不仅可以用来解释程序某些部分的作用和功能(用自然语言描述代码的功能),在必要时,还可以将代
- 字符编码我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题。因为计算机只能处理数字,如果要处理文本,就必须先把
- MySql批量插入优化Sql执行效率实例详解itemcontractprice数量1万左右,每条itemcontractprice 插入5条
- 首先说一个小技巧,True可看作1,False可看作0 ,并且可以参与运算!正文开始!!! 一、map()map(func,ite
- Mac 安装Mysql有许多开发的小伙伴,使用的是mac,那么在mac上如何安装Mysql呢?这篇文章就给大家说说。1、首先,登陆Mysql
- sql语句查询数据库中的表名/列名/主键/自动增长值 ----查询数据库中用户创建的表 ----jsj01 为数据库名 select nam
- 本文实例讲述了Python3.5 Pandas模块之Series用法。分享给大家供大家参考,具体如下:1、Pandas模块引入与基本数据结构
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&
- 阅读目录一般而言,当我们需要某些功能的模块时(无论是内置模块或自定义功能的模块),可以通过import module 或者 from * i
- 本文介绍的是python中pandas.DataFrame对行与列求和及添加新行与列的相关资料,下面话不多说,来看看详细的介绍吧。方法如下:
- 闭包函数是什么?在开始学习的闭包的时候,大家很能都比较难理解.就从他的官方解释来说,都是比较概念化的.不过我们也还是从闭包的含义出发。闭包是