python性能优化和源码保护-编译整个项目

本文介绍了如何使用Cython将Python代码编译为C/C++扩展模块,提升运行效率。详细步骤包括在Linux和Windows环境下单个文件及整个Python项目的编译方法,并解析了相关编译参数。Cython编译后生成的动态链接库在多线程场景下仍受GIL限制,但在循环和列表遍历等方面有显著优化。同时,文章还提醒了在编译过程中需要注意的代码安全性和动态链接库的依赖性。
部署运行你感兴趣的模型镜像

Python是一门动态解释型语言,由于GIL、GC机制等特性,python运算效率很低。

同时由于python程序没有静态编译的过程,项目代码以py源码的方式交付,任何人都可以获取和修改源代码,无法做到相应的安全保护。

针对这两个问题,可以将Python源代码编译生成C/C++扩展模块的形式(Windows平台生成.pyd文件,Linux生成.so文件),以动态链接库的方式调用。

python调用C/C++扩展函数的时候,GIL会被锁定,直到这个函数结束。由于这期间没有python的字节码被运行,所以线程不会切换,如果在扩展模块中调用阻塞式地I/O函数,python中的多线程机制就形同虚设。

Cython

使用cython编译加速python代码,cython是一门编程语言,属于python的超集,也是一个编译器,负责将cython源码(或python源码)翻译成高效的C/C++源码,最终编译成python的扩展模块,经过cython翻译编译后,程序执行效率会有明显提升。

安装cython:

pip install cython

在使用Cython编译Python代码时,需要安装C/C++编译器,linux系统默认安装有gcc编译器,windows系统可使用MSVC(Microsoft Visual C/C++)或者clang编译器。

cython先将python转为对应的C代码,然后(用linux的gcc或者windows的vc编译器)编译成so(pyd),但这个so有一些特殊:

  • 这个so由cython生成的C代码编译而成,可能添加了cython标注的一些静态性能优化的代码。
  • 使用这个so需要依赖python vm环境(不管是程序本身就以python vm启动,还是从C或其他环境调用)
    在这里插入图片描述
    在这里插入图片描述

Note: 纯python源码被cython编译后,因为没有使用类型标注等cython语法,故编译后的动态链接库和可执行文件,主要依赖python的运行时,并不依赖C/C++运行时,主要由python解释器执行,多线程GIL的问题同样存在,性能提升有限,但对for循环、大的列表对象遍历性能有明显优化效果。

使用方法

先用cython将python语言代码转换为c语言代码,然后用c编译器(gcc)生成可执行文件或动态链接库。

通过shell或python脚本的方式,将项目启动的入口py编译成可执行文件,将项目的其他.py文件编译成.so(__init__.py除外)

Note: __init__.py文件定义了python的包结构,为了使cython编译后的.so能按照正常路径import,__init__.py不能被编译,故为了保护代码,整个项目的所有__init__.py文件不建议放业务相关代码。

单个文件的编译示例-linux

将启动main.py编译成二进制可执行文件main

# 可通过文件头的 #!/usr/bin/env python 标记是否程序启动文件
# step1: 将python代码翻译成c代码(main.py -> main.c)
cython -D -2 --directive always_allow_keywords=true --embed main.py
# step2: 将c代码编译为目标文件(main.c -> main.o)
gcc -c -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o main.o main.c
# step3: 将目标文件编译为二进制可执行文件(main.o -> main)
gcc -I/usr/include/python2.7 -o main main.o -lpython2.7

将test.py编译为动态链接库test.so

# step1: 将python代码翻译成c代码(test.py -> test.c)
cython -D -2 --directive always_allow_keywords=true test.py
# step2: 将c代码编译为linux动态链接库文件(test.c -> test.so)
gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o test.so test.c

cython参数说明;

cython参数说明:
-D, --no-docstrings, Strip docstrings from the compiled module.

-o, --output-file <filename>   Specify name of generated C file
-2                             Compile based on Python-2 syntax and code semantics.
-3                             Compile based on Python-3 syntax and code semantics.

gcc参数说明

-shared:
编译动态库时要用到

-pthread:
在Linux中要用到多线程时,需要链接pthread库

-fPIC:
作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),
则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意
位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。

-fwrapv:
它定义了溢出时候编译器的行为——采用二补码的方式进行操作

-O参数
这是一个程序优化参数,一般用-O2就是,用来优化程序用的
-O2:
会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。 
-O3: 在O2的基础上进行更多的优化

-Wall:
编译时 显示Warning警告,但只会显示编译器认为会出现错误的警告

-fno-strict-aliasing:
“-fstrict-aliasing”表示启用严格别名规则,“-fno-strict-aliasing”表示禁用严格别名规则,当gcc的编译优化参数为“-O2”、“-O3”和“-Os”时,默认会打开“-fstrict-aliasing”。 

-I (大写的i):
是用来指定头文件目录
-I /home/hello/include表示将/home/hello/include目录作为第一个寻找头文件的目录,寻找的顺序是:/home/hello/include-->/usr/include-->/usr/local/include 

-l:
-l(小写的 L)参数就是用来指定程序要链接的库,-l参数紧接着就是库名,把库文件名的头lib和尾.so去掉就是库名了,例如我们要用libtest.so库库,编译时加上-ltest参数就能用上了
整个python项目编译示例-linux

github: 使用Cython编译整个python项目

Usage

$ python build.py -h
usage: build.py [-h] [-c] [-f CONFIG] [-s SRC_DIR] [-o OUTPUT_DIR]
                [-e EXE_FILES] [-x EXCLUDE_FILES]

optional arguments:
  -h, --help            show this help message and exit
  -c, --using-config    whether to use a configuration file
  -f CONFIG, --config-file CONFIG
                        config file path for compile
  -s SRC_DIR, --src-dir SRC_DIR
                        src file dir
  -o OUTPUT_DIR, --output-dir OUTPUT_DIR
                        output file dir
  -e EXE_FILES, --exe-files EXE_FILES
                        the executable file list, separate by comma.
  -x EXCLUDE_FILES, --exclude-files EXCLUDE_FILES
                        the exclude file list, separate by comma.

Example

# build.yaml
compile:
  src_dir: '/home/test/src'
  output_dir: '/home/test/output'
  exe_files:
    - '{src_dir}/runserver.py'
  exclude_files:
    - '{src_dir}/uwsgi.py'

使用配置文件
python build.py -c -f ./build.yaml

或者
python build.py -s /home/test/src -o /home/test/output -e {src_dir}/runserver.py -x {src_dir}/uwsgi.py
单个文件的编译示例-windows

windows系统使用cython需要确保已安装C/C++编译器且环境变量正确配置,cython能找到编译器。windows系统可使用MSVC(Microsoft Visual C/C++)或者clang编译器。

安装VS: https://visualstudio.microsoft.com/zh-hans/vs/
安装Windows SDK: https://developer.microsoft.com/zh-cn/windows/downloads/windows-sdk/

参考:https://github.com/cython/cython/wiki/CythonExtensionsOnWindows

将test.py编译为动态链接库test.pyd

# step1: 将python代码翻译成c代码(test.py -> test.c)
cython -D -3 --directive always_allow_keywords=true test.py
# step2: 将c代码编译为windows动态链接库文件(test.c -> test.pyd)
cythonize -i test.c

最后得到windows下的动态链接库文件test.cp39-win_amd64.pyd,还需要将文件名重命名为test.pyd。

注意:编译连接后的模块(module)名默认取的源文件名,示例中即test,即使对test.pyd进行了重命名,模块名也不会变,import的时候要注意,源文件名修改后需要重新编译链接。

output:

D:\demo>cython -D -3 --directive always_allow_keywords=true test.py

D:\demo>cythonize -i test.c
running build_ext
building 'demo.test' extension
creating D:\tmpz2e3_zzu\Release
creating D:\tmpz2e3_zzu\Release\demo
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\bin\HostX86\x64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -Ic:\python
39\include -Ic:\python39\include -IC:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include -IC:\Program Files\Microsof
t Visual Studio\2022\Community\VC\Auxiliary\VS\include -IC:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt -IC:\Program Files (x86)\Windo
ws Kits\10\\include\10.0.22621.0\\um -IC:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\shared -IC:\Program Files (x86)\Windows Kits\10\\in
clude\10.0.22621.0\\winrt -IC:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\cppwinrt /TcD:\demo\test.c /FoD:\tmpz2e3_zzu\Release\demo\test
.obj
test.c
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\bin\HostX86\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:
EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:c:\python39\libs /LIBPATH:c:\python39\PCbuild\amd64 /LIBPATH:C:\Program Files\Microsoft Visual Studio\2022\Community
\VC\Tools\MSVC\14.33.31629\lib\x64 /LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.22621.0\ucrt\x64 /LIBPATH:C:\Program Files (x86)\Windows Kit
s\10\\lib\10.0.22621.0\\um\x64 /EXPORT:PyInit_test D:\tmpz2e3_zzu\Release\demo\test.obj /OUT:D:\demo\test.cp39-win_amd64.pyd /IMPLIB:D:\tmpz2e3_zzu\Rele
ase\demo\test.cp39-win_amd64.lib
  正在创建库 D:\tmpz2e3_zzu\Release\demo\test.cp39-win_amd64.lib 和对象 D:\tmpz2e3_zzu\Release\demo\test.cp39-win_amd64.exp
正在生成代码
已完成代码的生成

cythonize参数说明;

cythonize参数说明:
-b, --build           build extension modules using distutils
-i, --inplace         build extension modules in place using distutils  (implies -b),即 将编译后的扩展模块直接放在与test.py同级的目录中。

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pushiqiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值