diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 3529d31..b5cafb8 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -4,39 +4,23 @@ "type": "split", "children": [ { - "id": "b22be8ecf266ef8e", + "id": "b4a0049808dcf451", "type": "tabs", "children": [ { - "id": "5368d045abb8511f", + "id": "22a9ab72237d5df4", "type": "leaf", "state": { - "type": "markdown", + "type": "diff-view", "state": { - "file": "_posts/pool/2024-11-14-深入理解Python异步编程(上).md", - "mode": "source", - "source": false + "file": "Clippings/使用line_profiler对python代码性能进行评估优化 - DECHIN - 博客园.md", + "staged": false }, - "icon": "lucide-file", - "title": "2024-11-14-深入理解Python异步编程(上)" - } - }, - { - "id": "342665bd1d8204d1", - "type": "leaf", - "state": { - "type": "markdown", - "state": { - "file": "_posts/pool/2024-11-27-多线程信号量.md", - "mode": "source", - "source": false - }, - "icon": "lucide-file", - "title": "2024-11-27-多线程信号量" + "icon": "git-pull-request", + "title": "Diff View (使用line_profiler对python代码性能进行评估优化 - DECHIN - 博客园)" } } - ], - "currentTab": 1 + ] } ], "direction": "vertical" @@ -67,7 +51,7 @@ "state": { "type": "search", "state": { - "query": "字符串", + "query": "git", "matchingCase": false, "explainSearch": false, "collapseAll": false, @@ -88,11 +72,13 @@ "title": "书签" } } - ] + ], + "currentTab": 1 } ], "direction": "horizontal", - "width": 321.5 + "width": 321.5, + "collapsed": true }, "right": { "id": "55bf47326259e19a", @@ -200,17 +186,26 @@ "obsidian-excalidraw-plugin:新建绘图文件": false } }, - "active": "342665bd1d8204d1", + "active": "22a9ab72237d5df4", "lastOpenFiles": [ - "Clippings/Fatescript 当我谈RAG时我谈些什么.md", + "_posts/engineering/2024-09-11-git版本管理常见技巧.md", + "_posts/pool/2024-12-13.md", + "_posts/pool/2024-11-14-深入理解Python异步编程(上).md", + "assets/Excalidraw/Drawing 2024-12-05 13.25.09.excalidraw.md", + "assets/Excalidraw/Drawing 2024-12-05 13.24.41.excalidraw.md", + "_posts/pool/2024-12-05.md", + "Clippings/使用line_profiler对python代码性能进行评估优化 - DECHIN - 博客园.md", + "_posts/pool/2024-11-28.md", + "_posts/pool/2024-12-04.md", + "Clippings/华为“天才少年”:现在的AI技术要么无趣,要么无用|钛媒体AGI_澎湃号·湃客_澎湃新闻-The Paper.md", "_posts/pool/2024-11-27-多线程信号量.md", + "Clippings/Fatescript 当我谈RAG时我谈些什么.md", "_posts/coding/2024-11-20-Python-asyncio异步转同步.md", "_posts/pool/2024-11-26.md", "_posts/pool/2024-11-22.md", "_posts/coding/2024-08-13-python开多进程.md", "_posts/coding/2024-08-13-Python获取类的属性.md", "_posts/debug/2024-09-12-记一次离奇的linux-ssh登录bug.md", - "_posts/pool/2024-11-14-深入理解Python异步编程(上).md", "_posts/pool/2024-11-14.md", "_posts/pool/2024-11-15.md", "pool", @@ -218,18 +213,9 @@ "_posts/engineering/2024-10-16-Elasticsearch之一百种你不得不记的RESTful api.md", "_posts/engineering/2024-10-24-ssh-keygen自定义密钥名称.md", "_posts/engineering/2024-09-13-linux安装全局可用的conda+创建有root权限的新账号.md", - "_posts/engineering/2024-09-11-git版本管理常见技巧.md", "_posts/coding/2024-08-13-json.dumps输出美化版json.md", "_posts/coding/2024-08-13-chatglm-PPO训练路径探索.md", "_posts/coding/2024-08-13-generator.md", - "_posts/coding/2024-08-13-数据结构知识池.md", - "_posts/brain/2024-09-20-obsidian ios多平台同步超完美方案.md", - "_posts/brain/2024-09-06-github_page+obsidian实现个人笔记云管理.md", - "_posts/debug/2024-10-18-不太安全但管用的docker非root用户权限问题.md", - "_posts/math/2024-08-13-泰勒公式.md", - "_posts/game/2024-06-27-猫国建设者原版开启小猫科学家.md", - "_posts/language/2024-10-24-A2-English-for-Developers-one.md", - "_posts/engineering/2024-08-13-脚本内指定GPU部署.md", "Clippings", "_posts/language", "assets/img/mrj9tyfxgpwc4ohkdhkq3uu3azxww8g.png", diff --git "a/Clippings/\344\275\277\347\224\250line_profiler\345\257\271python\344\273\243\347\240\201\346\200\247\350\203\275\350\277\233\350\241\214\350\257\204\344\274\260\344\274\230\345\214\226 - DECHIN - \345\215\232\345\256\242\345\233\255.md" "b/Clippings/\344\275\277\347\224\250line_profiler\345\257\271python\344\273\243\347\240\201\346\200\247\350\203\275\350\277\233\350\241\214\350\257\204\344\274\260\344\274\230\345\214\226 - DECHIN - \345\215\232\345\256\242\345\233\255.md" new file mode 100644 index 0000000..090474e --- /dev/null +++ "b/Clippings/\344\275\277\347\224\250line_profiler\345\257\271python\344\273\243\347\240\201\346\200\247\350\203\275\350\277\233\350\241\214\350\257\204\344\274\260\344\274\230\345\214\226 - DECHIN - \345\215\232\345\256\242\345\233\255.md" @@ -0,0 +1,373 @@ +--- +title: "使用line_profiler对python代码性能进行评估优化 - DECHIN - 博客园" +source: "https://www.cnblogs.com/dechinphy/p/line-profiler.html" +author: +published: +created: 2024-12-04 +description: "介绍python的逐行性能分析工具line_profiler的安装与使用,对给定的两个案例用line_profiler进行分析并给出性能分析的结论,其中通过正弦函数的不同实现方式的性能排名也给了大家一些库的使用的启发。" +tags: + - "clippings" +--- +介绍python的逐行性能分析工具line\_profiler的安装与使用,对给定的两个案例用line\_profiler进行分析并给出性能分析的结论,其中通过正弦函数的不同实现方式的性能排名也给了大家一些库的使用的启发。 + +## 性能测试的意义 + +在做完一个python项目之后,我们经常要考虑对软件的性能进行优化。那么我们需要一个软件优化的思路,首先我们需要明确软件本身代码以及函数的瓶颈,最理想的情况就是有这样一个工具,能够将一个目标函数的代码每一行的性能都评估出来,这样我们可以针对所有代码中性能最差的那一部分,来进行针对性的优化。开源库`line_profiler`就做了一个这样的工作,开源地址:[github.com/rkern/line\_profiler](https://www.cnblogs.com/dechinphy/p/github.com/rkern/line_profiler)。下面让我们一起看下该工具的安装和使用详情。 + +## line\_profiler的安装 + +`line_profiler`的安装支持源码安装和pip的安装,这里我们仅介绍pip形式的安装,也比较容易,源码安装方式请参考官方开源地址。 + +```bash +[dechin@dechin-manjaro line_profiler]$ python3 -m pip install line_profiler +Collecting line_profiler + Downloading line_profiler-3.1.0-cp38-cp38-manylinux2010_x86_64.whl (65 kB) + |████████████████████████████████| 65 kB 221 kB/s +Requirement already satisfied: IPython in /home/dechin/anaconda3/lib/python3.8/site-packages (from line_profiler) (7.19.0) +Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (3.0.8) +Requirement already satisfied: backcall in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (0.2.0) +Requirement already satisfied: pexpect>4.3; sys_platform != "win32" in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (4.8.0) +Requirement already satisfied: setuptools>=18.5 in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (50.3.1.post20201107) +Requirement already satisfied: jedi>=0.10 in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (0.17.1) +Requirement already satisfied: decorator in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (4.4.2) +Requirement already satisfied: traitlets>=4.2 in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (5.0.5) +Requirement already satisfied: pygments in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (2.7.2) +Requirement already satisfied: pickleshare in /home/dechin/anaconda3/lib/python3.8/site-packages (from IPython->line_profiler) (0.7.5) +Requirement already satisfied: wcwidth in /home/dechin/anaconda3/lib/python3.8/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->IPython->line_profiler) (0.2.5) +Requirement already satisfied: ptyprocess>=0.5 in /home/dechin/anaconda3/lib/python3.8/site-packages (from pexpect>4.3; sys_platform != "win32"->IPython->line_profiler) (0.6.0) +Requirement already satisfied: parso<0.8.0,>=0.7.0 in /home/dechin/anaconda3/lib/python3.8/site-packages (from jedi>=0.10->IPython->line_profiler) (0.7.0) +Requirement already satisfied: ipython-genutils in /home/dechin/anaconda3/lib/python3.8/site-packages (from traitlets>=4.2->IPython->line_profiler) (0.2.0) +Installing collected packages: line-profiler +Successfully installed line-profiler-3.1.0 +``` + +这里额外介绍一种临时使用pip的源进行安装的方案,这里用到的是腾讯所提供的pypi源: + +```bash +python3 -m pip install -i https://mirrors.cloud.tencent.com/pypi/simple line_profiler +``` + +如果需要永久保存源可以修改`~/.pip/pip.conf`文件,一个参考示例如下(采用华为云的镜像源): + +```bash +[global] +index-url = https://mirrors.huaweicloud.com/repository/pypi/simple +trusted-host = mirrors.huaweicloud.com +timeout = 120 +``` + +## 在需要调试优化的代码中引用line\_profiler + +让我们直接来看一个案例: + +```python +# line_profiler_test.py +from line_profiler import LineProfiler +import numpy as np + +@profile +def test_profiler(): + for i in range(100): + a = np.random.randn(100) + b = np.random.randn(1000) + c = np.random.randn(10000) + return None + +if __name__ == '__main__': + test_profiler() +``` + +在这个案例中,我们定义了一个需要测试的函数`test_profiler`,在这个函数中有几行待分析性能的模块`numpy.random.randn`。使用的方式就是先import进来`LineProfiler`函数,然后在需要逐行进行性能分析的函数上方引用名为`profile`的装饰器,就完成了line\_profiler性能分析的配置。关于python装饰器的使用和原理,可以参考这篇[博客](https://www.cnblogs.com/dechinphy/p/decoretor.html)的内容介绍。还有一点需要注意的是,line\_profiler所能够分析的范围仅限于加了装饰器的函数内容,如果函数内有其他的调用之类的,不会再进入其他的函数进行分析,除了内嵌的嵌套函数。 + +## 使用line\_profiler进行简单性能分析 + +line\_profiler的使用方法也较为简单,主要就是两步:先用`kernprof`解析,再采用python执行得到分析结果。 + +1. 在定义好需要分析的函数模块之后,用`kernprof`解析成二进制`lprof`文件: + +```bash +[dechin-manjaro line_profiler]# kernprof -l line_profiler_test.py +Wrote profile results to line_profiler_test.py.lprof +``` + +该命令执行结束后,会在当前目录下产生一个`lprof`文件: + +```bash +[dechin-manjaro line_profiler]# ll +总用量 8 +-rw-r--r-- 1 dechin dechin 304 1月 20 16:00 line_profiler_test.py +-rw-r--r-- 1 root root 185 1月 20 16:00 line_profiler_test.py.lprof +``` + +2. 使用`python3`运行`lprof`二进制文件: + +```bash +[dechin-manjaro line_profiler]# python3 -m line_profiler line_profiler_test.py.lprof +Timer unit: 1e-06 s + +Total time: 0.022633 s +File: line_profiler_test.py +Function: test_profiler at line 5 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 5 @profile + 6 def test_profiler(): + 7 101 40.0 0.4 0.2 for i in range(100): + 8 100 332.0 3.3 1.5 a = np.random.randn(100) + 9 100 2092.0 20.9 9.2 b = np.random.randn(1000) + 10 100 20169.0 201.7 89.1 c = np.random.randn(10000) + 11 1 0.0 0.0 0.0 return None +``` + +这里我们就直接得到了逐行的性能分析结论。简单介绍一下每一列的含义:代码在代码文件中对应的行号、被调用的次数、该行的总共执行时间、单次执行所消耗的时间、执行时间在该函数下的占比,最后一列是具体的代码内容。其实,关于`line_profiler`的使用介绍到这里就可以结束了,但是我们希望通过另外一个实际案例来分析line\_profiler的功能,感兴趣的读者可以继续往下阅读。 + +## 使用line\_profiler分析不同函数库计算正弦函数sin的效率 + +我们这里需要测试多个库中所实现的`正弦函数`,其中包含我们自己使用的fortran内置的`SIN`函数。 + +在演示line\_profiler的性能测试之前,让我们先看看如何将一个fortran的`f90`文件转换成python可调用的动态链接库文件。 + +1. 首先在Manjaro Linux平台上安装gfotran + +```bash +[dechin-manjaro line_profiler]# pacman -S gcc-fortran +正在解析依赖关系... +正在查找软件包冲突... + +软件包 (1) gcc-fortran-10.2.0-4 + +下载大小: 9.44 MiB +全部安装大小: 31.01 MiB + +:: 进行安装吗? [Y/n] Y +:: 正在获取软件包...... + gcc-fortran-10.2.0-4-x86_64 9.4 MiB 6.70 MiB/s 00:01 [#######################################################################################] 100% +(1/1) 正在检查密钥环里的密钥 [#######################################################################################] 100% +(1/1) 正在检查软件包完整性 [#######################################################################################] 100% +(1/1) 正在加载软件包文件 [#######################################################################################] 100% +(1/1) 正在检查文件冲突 [#######################################################################################] 100% +(1/1) 正在检查可用存储空间 [#######################################################################################] 100% +:: 正在处理软件包的变化... +(1/1) 正在安装 gcc-fortran [#######################################################################################] 100% +:: 正在运行事务后钩子函数... +(1/2) Arming ConditionNeedsUpdate... +(2/2) Updating the info directory file... +``` + +2. 创建一个简单的fortran文件`fmath.f90`,功能为返回正弦函数的值: + +```fortran +subroutine fsin(theta,result) + implicit none + real*8::theta + real*8,intent(out)::result + result=SIN(theta) +end subroutine +``` + +3. 用f2py将该fortran文件编译成名为`fmath`的动态链接库: + +```bash +[dechin-manjaro line_profiler]# f2py -c -m fmath fmath.f90 +running build +running config_cc +unifing config_cc, config, build_clib, build_ext, build commands --compiler options +running config_fc +unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options +running build_src +build_src +building extension "fmath" sources +f2py options: [] +f2py:> /tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fmathmodule.c +creating /tmp/tmpup5ia9lf/src.linux-x86_64-3.8 +Reading fortran codes... + Reading file 'fmath.f90' (format:free) +Post-processing... + Block: fmath + Block: fsin +Post-processing (stage 2)... +Building modules... + Building module "fmath"... + Constructing wrapper function "fsin"... + result = fsin(theta) + Wrote C/API module "fmath" to file "/tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fmathmodule.c" + adding '/tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fortranobject.c' to sources. + adding '/tmp/tmpup5ia9lf/src.linux-x86_64-3.8' to include_dirs. +copying /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/f2py/src/fortranobject.c -> /tmp/tmpup5ia9lf/src.linux-x86_64-3.8 +copying /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/f2py/src/fortranobject.h -> /tmp/tmpup5ia9lf/src.linux-x86_64-3.8 +build_src: building npy-pkg config files +running build_ext +customize UnixCCompiler +customize UnixCCompiler using build_ext +get_default_fcompiler: matching types: '['gnu95', 'intel', 'lahey', 'pg', 'absoft', 'nag', 'vast', 'compaq', 'intele', 'intelem', 'gnu', 'g95', 'pathf95', 'nagfor']' +customize Gnu95FCompiler +Found executable /usr/bin/gfortran +customize Gnu95FCompiler +customize Gnu95FCompiler using build_ext +building 'fmath' extension +compiling C sources +C compiler: gcc -pthread -B /home/dechin/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC + +creating /tmp/tmpup5ia9lf/tmp +creating /tmp/tmpup5ia9lf/tmp/tmpup5ia9lf +creating /tmp/tmpup5ia9lf/tmp/tmpup5ia9lf/src.linux-x86_64-3.8 +compile options: '-I/tmp/tmpup5ia9lf/src.linux-x86_64-3.8 -I/home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include -I/home/dechin/anaconda3/include/python3.8 -c' +gcc: /tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fmathmodule.c +gcc: /tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fortranobject.c +In file included from /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/ndarraytypes.h:1822, + from /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/ndarrayobject.h:12, + from /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/arrayobject.h:4, + from /tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fortranobject.h:13, + from /tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fmathmodule.c:15: +/home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: 警告:#warning "Using deprecated NumPy API, disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp] + 17 | #warning "Using deprecated NumPy API, disable it with " \ + | ^~~~~~~ +In file included from /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/ndarraytypes.h:1822, + from /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/ndarrayobject.h:12, + from /home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/arrayobject.h:4, + from /tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fortranobject.h:13, + from /tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fortranobject.c:2: +/home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: 警告:#warning "Using deprecated NumPy API, disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp] + 17 | #warning "Using deprecated NumPy API, disable it with " \ + | ^~~~~~~ +compiling Fortran sources +Fortran f77 compiler: /usr/bin/gfortran -Wall -g -ffixed-form -fno-second-underscore -fPIC -O3 -funroll-loops +Fortran f90 compiler: /usr/bin/gfortran -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops +Fortran fix compiler: /usr/bin/gfortran -Wall -g -ffixed-form -fno-second-underscore -Wall -g -fno-second-underscore -fPIC -O3 -funroll-loops +compile options: '-I/tmp/tmpup5ia9lf/src.linux-x86_64-3.8 -I/home/dechin/anaconda3/lib/python3.8/site-packages/numpy/core/include -I/home/dechin/anaconda3/include/python3.8 -c' +gfortran:f90: fmath.f90 +/usr/bin/gfortran -Wall -g -Wall -g -shared /tmp/tmpup5ia9lf/tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fmathmodule.o /tmp/tmpup5ia9lf/tmp/tmpup5ia9lf/src.linux-x86_64-3.8/fortranobject.o /tmp/tmpup5ia9lf/fmath.o -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib -L/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../lib -lgfortran -o ./fmath.cpython-38-x86_64-linux-gnu.so +Removing build directory /tmp/tmpup5ia9lf +``` + +这中间会有一些告警,但是并不影响我们的正常使用,编译好之后,可以在当前目录下看到一个so文件(如果是windows平台可能是其他类型的动态链接库文件): + +```bash +[dechin-manjaro line_profiler]# ll +总用量 120 +-rwxr-xr-x 1 root root 107256 1月 20 16:40 fmath.cpython-38-x86_64-linux-gnu.so +-rw-r--r-- 1 root root 150 1月 20 16:40 fmath.f90 +-rw-r--r-- 1 dechin dechin 304 1月 20 16:00 line_profiler_test.py +-rw-r--r-- 1 root root 185 1月 20 16:00 line_profiler_test.py.lprof +``` + +3. 用ipython测试该动态链接库的功能是否正常: + +```bash +[dechin-manjaro line_profiler]# ipython +Python 3.8.5 (default, Sep 4 2020, 07:30:14) +Type 'copyright', 'credits' or 'license' for more information +IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help. + +In [1]: from fmath import fsin + +In [2]: print (fsin(3.14)) +0.0015926529164868282 + +In [3]: print (fsin(3.1415926)) +5.3589793170057245e-08 +``` + +这里我们可以看到基于fortran的正弦函数的功能已经完成实现了,接下来让我们正式对比几种正弦函数实现的性能(底层的实现有可能重复,这里作为黑盒来进行性能测试)。 + +首先,我们还是需要创建好待测试的python文件`sin_profiler_test.py`: + +```python +# sin_profiler_test.py +from line_profiler import LineProfiler +import random +from numpy import sin as numpy_sin +from math import sin as math_sin +# from cupy import sin as cupy_sin +from cmath import sin as cmath_sin +from fmath import fsin as fortran_sin + +@profile +def test_profiler(): + for i in range(100000): + r = random.random() + a = numpy_sin(r) + b = math_sin(r) + # c = cupy_sin(r) + d = cmath_sin(r) + e = fortran_sin(r) + return None + +if __name__ == '__main__': + test_profiler() +``` + +这里`line_profiler`的定义跟前面定义的例子一致,我们主要测试的对象为`numpy,math,cmath`四个开源库的正弦函数实现以及自己实现的一个fortran的正弦函数,通过上面介绍的`f2py`构造的动态链接库跟python实现无缝对接。由于这里的`cupy`库没有安装成功,所以这里暂时没办法测试而注释掉了。接下来还是一样的,通过`kernprof`进行编译构建: + +```bash +[dechin-manjaro line_profiler]# kernprof -l sin_profiler_test.py +Wrote profile results to sin_profiler_test.py.lprof +``` + +最后通过`python3`来执行: + +```bash +[dechin-manjaro line_profiler]# python3 -m line_profiler sin_profiler_test.py.lprof +Timer unit: 1e-06 s + +Total time: 0.261304 s +File: sin_profiler_test.py +Function: test_profiler at line 10 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 10 @profile + 11 def test_profiler(): + 12 100001 28032.0 0.3 10.7 for i in range(100000): + 13 100000 33995.0 0.3 13.0 r = random.random() + 14 100000 86870.0 0.9 33.2 a = numpy_sin(r) + 15 100000 33374.0 0.3 12.8 b = math_sin(r) + 16 # c = cupy_sin(r) + 17 100000 40179.0 0.4 15.4 d = cmath_sin(r) + 18 100000 38854.0 0.4 14.9 e = fortran_sin(r) + 19 1 0.0 0.0 0.0 return None +``` + +从这个结果上我们可以看出,在这测试的四个库中,`math`的计算效率是最高的,`numpy`的计算效率是最低的,而我们自己编写的`fortran接口函数`甚至都比`numpy`的实现快了一倍,仅次于`math`的实现。其实,这里值涉及到了单个函数的性能测试,我们还可以通过`ipython`中自带的`timeit`来进行测试: + +```bash +[dechin-manjaro line_profiler]# ipython +Python 3.8.5 (default, Sep 4 2020, 07:30:14) +Type 'copyright', 'credits' or 'license' for more information +IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help. + +In [1]: from fmath import fsin + +In [2]: import random + +In [3]: %timeit fsin(random.random()) +145 ns ± 2.38 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) + +In [4]: from math import sin as math_sin + +In [5]: %timeit math_sin(random.random()) +107 ns ± 0.116 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) + +In [6]: from numpy import sin as numpy_sin + +In [7]: %timeit numpy_sin(random.random()) +611 ns ± 4.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) + +In [8]: from cmath import sin as cmath_sin + +In [9]: %timeit cmath_sin(random.random()) +151 ns ± 1.01 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) +``` + +在这个结果中我们看到排名的趋势依然跟之前的保持一致,但是由于将`random`模块和计算模块放在一起,在给出的时间数值上有些差异。 + +## 总结概要 + +本文重点介绍了python的一款逐行性能分析的工具`line_profiler`,通过简单的装饰器的调用就可以分析出程序的性能瓶颈,从而进行针对性的优化。另外,在测试的过程中我们还可以发现,不同形式的正弦三角函数实现,性能是存在差异的,只是在日常使用频率较低的情况下是不感知的。需要了解的是,即使是正弦函数也有很多不同的实现方案,比如各种级数展开,而目前最流行、性能最高的计算方式,其实还是通过查表法。因此,不同的算法实现、不同的语言实现,都会导致完全不一样的结果。就测试情况而言,已知的性能排名为:`math`<`fortran`<`cmath`<`numpy`从左到右运行时长逐步增加。 + +## 版权声明 + +本文首发链接为:[https://www.cnblogs.com/dechinphy/p/line-profiler.html](https://www.cnblogs.com/dechinphy/p/line-profiler.html) +作者ID:DechinPhy +更多原著文章请参考:[https://www.cnblogs.com/dechinphy/](https://www.cnblogs.com/dechinphy/) \ No newline at end of file diff --git "a/Clippings/\345\215\216\344\270\272\342\200\234\345\244\251\346\211\215\345\260\221\345\271\264\342\200\235\357\274\232\347\216\260\345\234\250\347\232\204AI\346\212\200\346\234\257\350\246\201\344\271\210\346\227\240\350\266\243\357\274\214\350\246\201\344\271\210\346\227\240\347\224\250\357\275\234\351\222\233\345\252\222\344\275\223AGI_\346\276\216\346\271\203\345\217\267\302\267\346\271\203\345\256\242_\346\276\216\346\271\203\346\226\260\351\227\273-The Paper.md" "b/Clippings/\345\215\216\344\270\272\342\200\234\345\244\251\346\211\215\345\260\221\345\271\264\342\200\235\357\274\232\347\216\260\345\234\250\347\232\204AI\346\212\200\346\234\257\350\246\201\344\271\210\346\227\240\350\266\243\357\274\214\350\246\201\344\271\210\346\227\240\347\224\250\357\275\234\351\222\233\345\252\222\344\275\223AGI_\346\276\216\346\271\203\345\217\267\302\267\346\271\203\345\256\242_\346\276\216\346\271\203\346\226\260\351\227\273-The Paper.md" new file mode 100644 index 0000000..09834af --- /dev/null +++ "b/Clippings/\345\215\216\344\270\272\342\200\234\345\244\251\346\211\215\345\260\221\345\271\264\342\200\235\357\274\232\347\216\260\345\234\250\347\232\204AI\346\212\200\346\234\257\350\246\201\344\271\210\346\227\240\350\266\243\357\274\214\350\246\201\344\271\210\346\227\240\347\224\250\357\275\234\351\222\233\345\252\222\344\275\223AGI_\346\276\216\346\271\203\345\217\267\302\267\346\271\203\345\256\242_\346\276\216\346\271\203\346\226\260\351\227\273-The Paper.md" @@ -0,0 +1,11 @@ +--- +title: "华为“天才少年”:现在的AI技术要么无趣,要么无用|钛媒体AGI_澎湃号·湃客_澎湃新闻-The Paper" +source: "https://www.thepaper.cn/newsDetail_forward_26937826" +author: +published: +created: 2024-11-28 +description: "图片来源:unsplash ▎“我们相信,在人类世界的数字延伸中,有趣的灵魂终会相遇。” 作者|AI科技组 本文首发钛媒体APP 近期,一篇4万字的演讲风靡于国" +tags: + - "clippings" +--- +GPT-soVITS 的路线比 VITS 更好,可以实现 zero-shot 语音克隆,不再需要收集大量高质量语音做训练。开源模型可以合成的语音质量终于逼近 ElevenLabs 的水平了。) \ No newline at end of file diff --git "a/_posts/pool/2024-11-27-\345\244\232\347\272\277\347\250\213\344\277\241\345\217\267\351\207\217.md" "b/_posts/pool/2024-11-27-\345\244\232\347\272\277\347\250\213\344\277\241\345\217\267\351\207\217.md" index f6a195a..aa0f640 100644 --- "a/_posts/pool/2024-11-27-\345\244\232\347\272\277\347\250\213\344\277\241\345\217\267\351\207\217.md" +++ "b/_posts/pool/2024-11-27-\345\244\232\347\272\277\347\250\213\344\277\241\345\217\267\351\207\217.md" @@ -46,4 +46,254 @@ print("All workers have finished.") - 其他线程如果尝试获取信号量,必须等待,直到有线程释放信号量。 - `with semaphore`语句自动处理信号量的获取和释放,确保即使发生异常也能正确释放信号量。 -使用Semaphore可以帮助管理并发,避免数据竞争和资源冲突。 \ No newline at end of file +使用Semaphore可以帮助管理并发,避免数据竞争和资源冲突。 + +`Lock` 和 `Semaphore` 都是 Python 中用于线程同步的工具,但它们在功能和用途上有一些关键的区别。以下是这两者的比较: + +### 1. 锁的概念 + +- **Lock(锁)**: + - `Lock` 是一个基本的同步原语。它用来保护共享资源,确保在某一时刻只有一个线程能够访问资源。 + - `Lock` 只有两种状态:可用和不可用。 + - 当一个线程持有锁时,其他线程请求锁将被阻塞,直到锁被释放。 + +- **Semaphore(信号量)**: + - `Semaphore` 是一个更复杂的同步原语,可以用来控制同时访问某个特定资源的线程数量。 + - `Semaphore` 使用一个内部计数器来跟踪当前可用的资源数量,计数器可以大于1。 + - 当线程调用 `acquire()` 时,信号量的计数器会减少。线程可以在计数器为正时继续执行;当计数器为0时,线程会阻塞,直到有线程释放信号量。 + +### 2. 使用场景 + +- **Lock**: + - 通常用于保护共享数据,确保同一时刻只有一个线程对数据进行读/write 操作。适合于需要严格互斥的场景。 + +- **Semaphore**: + - 适用于限制对某个资源的访问,例如限制同一个数据库连接,还可以控制并发线程的数量。适合于不需要严格互斥,但需要限制访问量的场景。 + +### 3. 代码示例 + +以下是使用 `Lock` 和 `Semaphore` 的简单示例: + +```python +import threading +import time + +# 使用Lock进行线程同步 +lock = threading.Lock() + +def lock_worker(identifier): + print(f"Worker {identifier} is trying to acquire the lock.") + lock.acquire() # 请求获取锁 + try: + print(f"Worker {identifier} has acquired the lock.") + time.sleep(2) + finally: + print(f"Worker {identifier} is releasing the lock.") + lock.release() + +# 使用Semaphore进行线程同步 +semaphore = threading.Semaphore(2) # 最多允许2个线程同时访问 + +def semaphore_worker(identifier): + print(f"Worker {identifier} is trying to acquire the semaphore.") + semaphore.acquire() # 请求获取信号量 + try: + print(f"Worker {identifier} has acquired the semaphore.") + time.sleep(2) + finally: + print(f"Worker {identifier} is releasing the semaphore.") + semaphore.release() + +# 创建线程 +lock_threads = [] +for i in range(3): + thread = threading.Thread(target=lock_worker, args=(i,)) + lock_threads.append(thread) + thread.start() + +# 等待所有Lock线程完成 +for thread in lock_threads: + thread.join() + +print("All Lock workers have finished.") + +# 创建Semaphore线程 +semaphore_threads = [] +for i in range(5): + thread = threading.Thread(target=semaphore_worker, args=(i,)) + semaphore_threads.append(thread) + thread.start() + +# 等待所有Semaphore线程完成 +for thread in semaphore_threads: + thread.join() + +print("All Semaphore workers have finished.") +``` + +### 总结 + +- 使用 `Lock` 和 `Semaphore` 可以有效地管理线程之间的协作与资源访问。 +- `Lock` 适用于需要严格互斥的场景,保证一次只有一个线程在访问共享资源。 +- `Semaphore` 适用于需要控制同时访问的线程数量的场景,可以允许多个线程同时访问,但数量受到限制。 + +根据具体的需求和场景选择合适的同步原语可以提高程序的效率和安全性。 + +是的,如果你的资源管理不涉及一段连续的代码块使用,例如需要在多个不连续的逻辑中释放某个资源,使用 `with` 语句就不太适合。在这种情况下,手动调用 `acquire()` 和 `release()` 将更为适用。 + +### 何时使用 `with` 语句 + +`with` 语句非常适合用于以下情况: + +1. **简单的、连续的资源使用**: + - 当你在一个逻辑块中获取资源,并在逻辑块结束时释放它。比如访问某个共享资源或者进行文件读写。 + +2. **异常安全**: + - 如果你的代码包含可能引发异常的操作,使用 `with` 语句可以确保即使出现异常,资源也会被正确释放。 + +### 不适合使用 `with` 的情况 + +如果你需要在多个不同的逻辑步骤中使用信号量或其他资源,可能更适合手动管理: + +1. **分散的操作**: + - 如果你的逻辑分散在多个地方,你可能希望在不同条件下释放信号量,或者在获取信号量后执行某些操作而没有立即完成。 + +2. **多个条件分支**: + - 例如,如果你有多个条件下的逻辑,其中不是每个条件都应该释放信号量,手动管理会更灵活。 + +### 示例 + +以下是一个不适合使用 `with` 的代码示例: + +```python +import threading +import time + +semaphore = threading.Semaphore(2) + +def worker(identifier): + print(f"Worker {identifier} is trying to acquire the semaphore.") + semaphore.acquire() # 手动获取信号量 + try: + print(f"Worker {identifier} has acquired the semaphore.") + # 可能在这里做一些操作 + time.sleep(1) + if identifier == 1: + print(f"Worker {identifier} needs to do something and will release the semaphore.") + # 在此释放信号量,但并不做完整的操作 + semaphore.release() + return # 提前返回 + # 完成其他操作 + print(f"Worker {identifier} has completed its task.") + finally: + # 这里,以保障最后总会释放信号量 + semaphore.release() # 确保最后释放信号量 + +# 启动多个线程 +threads = [] +for i in range(3): + thread = threading.Thread(target=worker, args=(i,)) + threads.append(thread) + thread.start() + +# 等待所有线程完成 +for thread in threads: + thread.join() + +print("All workers have finished.") +``` + +在这个示例中,信号量的释放并不是总是在一个统一的代码块中完成的,因此没有使用 `with` 语句。而是用 `try`...`finally` 确保资源在不论何种情况下都能被释放。 + +### 总结 + +- 当你的资源获取和释放是局部的、分开的,或者在不同情况下需做不同处理时,手动调用 `acquire()` 和 `release()` 是更好的选择。 +- 而当资源的使用在一段逻辑中是连续的且不易引发异常,使用 `with` 语句会更简洁和安全。选择合适的资源管理方式,能提高代码的可读性和可维护性。 +在Python的`threading`模块中,`Condition`对象是一种用于线程间同步的原语,通常用于一种生产者-消费者模式。它允许一个或多个线程通过条件变量协调其执行。条件变量是用来实现线程间的通信和同步的机制。 + +### 主要功能 + +`Condition`对象的关键功能是允许线程等待某个条件发生,并在该条件发生后被唤醒。它是以`Lock`作为基础构建的,因此可以用来同步对共享资源的访问。同时,`Condition`可以绑定一个条件表达式,使得线程在某种特定条件下进行等待或者被唤醒。 + +### 主要方法 + +1. **`wait(timeout=None)`**: + - 线程调用此方法时会释放与`Condition`相关联的锁,并进入等待状态,直到被通知(如调用 `notify()` 或 `notify_all()`)或者超时。 + - 一旦被通知,它会重新获得锁,并继续执行。 + +2. **`notify(n=1)`**: + - 唤醒一个等待在该条件上的线程。如果有多个线程在等待,可以传递参数`n`来指定最多唤醒`n`个线程。 + +3. **`notify_all()`**: + - 唤醒所有等待在该条件上的线程。 + +### 使用示例 + +以下是一个使用`Condition`实现生产者-消费者模式的简单示例: + +```python +import threading +import time +import random + +class ProducerConsumer: + def __init__(self): + self.condition = threading.Condition() + self.buffer = [] + self.buffer_size = 5 + + def producer(self): + while True: + item = random.randint(1, 100) # 模拟生产一个物品 + with self.condition: + while len(self.buffer) >= self.buffer_size: # 如果缓冲区满 + print("Buffer is full, producer is waiting...") + self.condition.wait() # 等待消费者消费 + + print(f"Producer produced: {item}") + self.buffer.append(item) # 生产物品 + self.condition.notify() # 通知消费者 + + time.sleep(random.random()) # 模拟生产时间 + + def consumer(self): + while True: + with self.condition: + while not self.buffer: # 如果缓冲区空 + print("Buffer is empty, consumer is waiting...") + self.condition.wait() # 等待生产者生产 + + item = self.buffer.pop(0) # 消费物品 + print(f"Consumer consumed: {item}") + self.condition.notify() # 通知生产者 + + time.sleep(random.random()) # 模拟消费时间 + +# 启动生产者和消费者线程 +pc = ProducerConsumer() + +producer_thread = threading.Thread(target=pc.producer) +consumer_thread = threading.Thread(target=pc.consumer) + +producer_thread.start() +consumer_thread.start() + +producer_thread.join() +consumer_thread.join() +``` + +### 工作原理 + +1. **生产者**: + - 生产者不断尝试产生新的物品。首先它会检查缓冲区是否已满,如果缓冲区满,生产者会调用`self.condition.wait()`,此时释放锁并进入等待状态。 + - 当消费者消费了物品并调用`notify()`后,生产者会被唤醒,重新获得锁,继续生产。 + +2. **消费者**: + - 消费者检查缓冲区是否为空。如果为空,消费者调用`self.condition.wait()`,释放锁并进入等待状态。 + - 当生产者放置了物品并调用`notify()`后,消费者会被唤醒,重新获得锁,继续消费。 + +### 总结 + +- **条件变量**(`Condition`) 有助于实现更复杂的多线程同步机制,适合用于需要多个线程相互协调的场景,例如生产者-消费者模型。 +- `Condition` 允许线程在某种条件下队列等待,并在条件满足时得到通知以继续执行。 +- 使用锁结合条件变量可以有效避免死锁和其他竞争条件,确保线程安全地访问共享资源。 \ No newline at end of file