22、代码运行时间测量与优化指南

代码性能优化全攻略

代码运行时间测量与优化指南

1. 运行时间测量技巧

测量代码的运行时间可能颇具挑战性,以下是一些能让测量尽可能准确的技巧:
1. 确保CPU独占 :运行代码时,要保证没有其他程序占用CPU。若代码运行时间较长,更需格外留意,避免在运行过程中启动如病毒扫描等定时任务。不同操作系统查看运行进程信息的方法不同:
- OS X或Linux :在终端使用 top 命令。
- Solaris或OpenSolaris :使用 prstat 命令。
- Windows :使用任务管理器。
2. 减少磁盘访问 :磁盘访问速度远低于CPU计算速度,应尽量避免读写文件,也不要使用过多内存导致程序进行磁盘交换(除非是为了对磁盘访问进行基准测试)。
3. 禁用屏幕输出 :进行基准测试时,禁用向屏幕的输出,因为这也会降低速度。
4. 多次运行取平均值 :多次运行代码并取结果的平均值。首次运行的计时值可能较慢,因为需要编译代码、加载库或数据到内存,可考虑舍弃该值。
5. 考虑环境差异 :优化效果会因编程语言、操作系统和CPU类型而异,结果可能有所不同。

2. 算法优化

解决问题所使用的算法对解决问题所需的时间有重大影响。若需减少运行时间,改进算法是可考虑的方法之一。然而,优化算法可能耗时较长,且优化后的代码可能更难调试(这就是为什么过早优化被视为万恶之源)。在开始优化之前,可尝试利用Sage内置的许多优化例程,也可查阅期刊文章、书籍和开源项目,看是否已有优化算法。

3. 快速碰撞检测示例

假设之前已定义了球体,可在工作表单元格中输入并运行以下代码:

%time
import numpy
collisions_2 = numpy.zeros(num_particles, dtype=numpy.bool)
r_min = 4*radius**2
for i in range(num_particles):
    for j in range(0,i):
        r_squared = (x[i] - x[j])**2 + (y[i] - y[j])**2 + (z[i] - z[j])**2
        if r_squared < r_min:
            collisions_2[i] = True
    for j in range(i+1,num_particles):
        r_squared = (x[i] - x[j])**2 + (y[i] - y[j])**2 + (z[i] - z[j])**2
        if r_squared < r_min:
            collisions_2[i] = True

此代码运行速度约快两倍。接着尝试以下代码:

%time
import numpy
collisions_3 = numpy.zeros(num_particles, dtype=numpy.bool)
r_min = 4*radius**2
for i in range(num_particles):
    for j in range(0,i):
        r_squared = (x[i] - x[j])*(x[i] - x[j]) + \
            (y[i] - y[j])*(y[i] - y[j]) + (z[i] - z[j])*(z[i] -z[j])
        if r_squared < r_min:
            collisions_3[i] = True
    for j in range(i+1,num_particles):
        r_squared = (x[i] - x[j])*(x[i] - x[j]) + \
            (y[i] - y[j])*(y[i] - y[j]) + (z[i] - z[j])*(z[i] - z[j])
        if r_squared < r_min:
            collisions_3[i] = True

检查结果是否正确:

print((collisions_1 == collisions_2).all())
print((collisions_2 == collisions_3).all())

结果应为 True 。这是因为做了两个改变:一是无需计算球心距离的平方根,直接比较距离的平方与 (2r)² ,消除平方根运算可使执行时间减半;二是将半径平方的计算移到循环外,仅计算一次。此外,使用乘法代替指数运算符进行平方运算,又使运行时间减少了50%以上。

4. 使用NumPy优化

NumPy不仅功能实用,还能加速代码。使用NumPy可对数组进行操作而无需使用Python的 for 循环,因为NumPy的关键部分用C语言编写并针对速度进行了优化,其操作比Python循环快得多。

4.1 使用NumPy定义球体

在新单元格中输入以下代码:

import numpy
dimension = 20
num_particles = 500
radius = 1.0
rng = numpy.random.mtrand.RandomState(seed=[1])
x_np = rng.uniform(0, dimension, num_particles)
y_np = rng.uniform(0, dimension, num_particles)
z_np = rng.uniform(0, dimension, num_particles)
4.2 使用NumPy检测碰撞

在另一个单元格中输入以下代码:

%time
collisions_4 = numpy.zeros(num_particles, dtype=numpy.bool)
r_min = numpy.float64(4*radius**2)
for i in range(num_particles):
    for j in range(0,i):
        r_squared = (x_np[i] - x_np[j])*(x_np[i] - x_np[j]) \
            + (y_np[i] - y_np[j])*(y_np[i] - y_np[j]) \
            + (z_np[i] - z_np[j])*(z_np[i] - z_np[j])
        if r_squared < r_min:
            collisions_4[i] = True
    for j in range(i+1,num_particles):
        r_squared = (x_np[i] - x_np[j])*(x_np[i] - x_np[j]) \
            + (y_np[i] - y_np[j])*(y_np[i] - y_np[j]) \
            + (z_np[i] - z_np[j])*(z_np[i] - z_np[j])
        if r_squared < r_min:
            collisions_4[i] = True

在另一个单元格中尝试以下代码:

%time
collisions_5 = numpy.zeros(num_particles, dtype=numpy.bool)
r_min = numpy.float64(4*radius**2)
for i in range(num_particles):
    if i>0:
        d2 = numpy.power((x_np[i]-x_np[0:i]),2) \
            + numpy.power((y_np[i]-y_np[0:i]),2) \
            + numpy.power((z_np[i] - z_np[0:i]),2)
        if d2.min() < r_min:
           collisions_5[i] = True
    if i+1 < num_particles:
        d2 = numpy.power((x_np[i]-x_np[i+1:]),2) \
            + numpy.power((y_np[i]-y_np[i+1:]),2) \
            + numpy.power((z_np[i]-z_np[i+1:]),2)
        if d2.min() < r_min:
            collisions_5[i] = True

最后检查结果是否一致:

print((collisions_4 == collisions_5).all())

结果应为 True

在第一个例子中,用NumPy函数替换Sage函数和运算符实际上增加了运行时间,说明简单替换并不能加速代码。但第二个例子展示了如何利用NumPy优化执行速度,通过将内层 for 循环替换为NumPy向量运算,提高了效率。许多数值算法包含嵌套循环,优化内层循环能获得最大收益。

5. NumPy更多特性

NumPy向量运算速度快,因为其库由高度优化的编译代码组成。编译代码速度快的原因之一是利用了并行处理,现代CPU(即使是单核)也能并行执行多条指令。使用NumPy数组的向量运算可利用并行执行加速数学运算。几乎所有常见的数学运算在NumPy中都有对应的函数或方法,如加法、减法、乘法、除法和指数运算等,这些运算按元素进行,结果数组长度与原数组相同。所有NumPy函数的分类列表可在 此处 查看。

6. 使用Cython优化

Cython是一种为Python语言编写C扩展的语言,它与Python非常相似,但支持一些额外特性,可编译成高度优化的C代码,在Sage中使用Cython很容易。

6.1 用Cython定义函数
%cython
import numpy
def cython_collisions(x, y, z, radius):
    num_particles = len(x)
    collisions = numpy.zeros(num_particles, dtype=numpy.bool)
    r_min = numpy.float64(4*radius**2)
    for i in range(num_particles):
        if i>0:
            d2 = numpy.power((x[i]-x[0:i]),2) \
                + numpy.power((y[i]-y[0:i]),2) \
                + numpy.power((z[i] - z[0:i]),2)
            if d2.min() < r_min:
               collisions[i] = True
        if i+1 < num_particles:
            d2 = numpy.power((x[i]-x[i+1:]),2) \
                + numpy.power((y[i]-y[i+1:]),2) \
                + numpy.power((z[i]-z[i+1:]),2)
            if d2.min() < r_min:
                collisions[i] = True
    return collisions

首次运行此单元格时,编译可能需要一两分钟。

6.2 创建粒子并调用Cython函数

在另一个单元格中输入以下代码:

%time
import numpy
dimension = 20
num_particles = 500
radius = 1.0
rng = numpy.random.mtrand.RandomState(seed=[1])
x_np = rng.uniform(0, dimension, num_particles)
y_np = rng.uniform(0, dimension, num_particles)
z_np = rng.uniform(0, dimension, num_particles)
collisions_6 = cython_collisions(x_np, y_np, z_np, radius)
6.3 验证结果
print((collisions_4 == collisions_6).all())

结果应为 True 。运行Cython代码时,它会将代码编译成C扩展。点击生成的左链接可查看Cython生成的C代码,右链接可查看优化报告。优化报告显示代码仍有优化空间,但运行测试代码时,执行速度比之前的例子快了三倍多。可在 Cython官网 了解更多信息。

7. 进一步使用Cython优化

可增加示例中的粒子数量,直到Cython代码在计算机上运行需要几秒时间,然后根据Cython网站上的说明和示例继续优化代码,利用优化报告确定代码中最接近纯C的部分。

8. 从Python调用Sage

可创建Python脚本利用Sage的功能,而无需直接运行Sage。可将Sage视为一个可导入Python程序的大型模块,然后从命令行运行。以下是一个从Python脚本调用Sage的示例。

8.1 创建Python脚本

创建一个新的纯文本文件,输入以下代码并保存为 .py 文件:

#!/usr/bin/env sage -python
from sage.all import *
vars={}
# Define limits of integration
vars['a'] = 0
vars['b'] = 8
sage_eval('None', cmds='f(x)=e^x*cos(x)', locals=vars)
print vars['f']
sage_eval('None', cmds='value, tol = numerical_integral(f,a,b)', locals=vars)
print(vars['value'])
8.2 运行脚本的两种方式
  • 方式一:在终端输入命令
    • 若Sage安装路径已添加到系统路径,可直接输入:
$ sage –python /path/to/script/4460_10_8.py
- 若Sage未在系统路径中,需提供Sage可执行文件的完整路径或切换到Sage安装的顶级目录,例如:
$ /Applications/sage_source/sage-4.6.1/sage -python 4460_10_8.py
结果应为:
x |--> e^x*cos(x)
1257.25293971
  • 方式二:直接执行脚本
    在Solaris、OS X或Linux系统上,使用 chmod 命令:
bash$ chmod u+x 4460_10_8.py

确保Sage安装目录已添加到系统 PATH 变量中,例如在UNIX或类UNIX系统上:

bash$ echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/texbin:/usr/X11/bin
bash$ PATH=$PATH:/Applications/sage
bash$ export PATH
bash$ echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/texbin:/usr/X11/bin:/Applications/sage

然后从命令行运行脚本:

bash$ ./4460_10_8.py

结果同样为:

x |--> e^x*cos(x)
1257.25293971
8.3 原理说明

编写使用Sage的Python脚本与编写Sage脚本的方法不同。脚本必须是有效的Python代码,不能直接使用Sage特定的语法,如数学函数定义 f(x)=e^x*cos(x) 。首先需使用 from sage.all import * 语句访问Sage中的名称,然后使用 sage_eval 函数“包装”Sage表达式。该函数接受四个参数:
| 参数 | 关键字 | 默认值 | 描述 |
| ---- | ---- | ---- | ---- |
| source | 无 | 待评估的字符串 | 无 |
| locals | 局部变量 | None | Sage使用的变量字典 |
| cmds | 命令 | ‘’ | 在评估源之前要执行的命令字符串 |
| preparse | 预解析源 | True | 禁用Sage预解析器 |

通过这些步骤和方法,可有效测量代码运行时间并进行优化,提高代码的性能和效率。

代码运行时间测量与优化指南(续)

9. 总结与优化流程梳理

为了更清晰地展示代码优化的流程,下面通过一个 mermaid 流程图来总结上述的优化步骤。

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B(测量代码运行时间):::process
    B --> C{是否满足性能要求?}:::decision
    C -->|是| D([结束]):::startend
    C -->|否| E(考虑算法优化):::process
    E --> F(利用Sage内置优化例程):::process
    F --> G(尝试NumPy优化):::process
    G --> H(考虑使用Cython优化):::process
    H --> I(从Python调用Sage功能):::process
    I --> J(再次测量运行时间):::process
    J --> C

从这个流程图可以看出,代码优化是一个循环迭代的过程。首先进行运行时间的测量,判断是否满足性能要求。若不满足,则依次尝试算法优化、使用Sage内置优化例程、NumPy优化、Cython优化,甚至从Python调用Sage功能。每次优化后都再次测量运行时间,直到满足性能要求为止。

10. 不同优化方法对比

为了让大家更直观地了解不同优化方法的效果和特点,下面通过一个表格进行对比。
| 优化方法 | 适用场景 | 优势 | 劣势 | 示例代码 |
| ---- | ---- | ---- | ---- | ---- |
| 算法优化 | 代码运行时间长,算法复杂度高 | 从根本上降低时间复杂度 | 可能需要大量时间和精力,优化后代码难调试 | 如碰撞检测中避免计算平方根 |
| NumPy优化 | 涉及大量数组操作的数值计算 | 利用C语言编写的核心部分,支持并行处理,速度快 | 简单替换函数可能无法提升性能 | 用NumPy向量运算替代Python循环 |
| Cython优化 | 对性能要求极高的场景 | 可编译成高度优化的C代码,大幅提升速度 | 首次编译可能耗时,需要一定C语言基础 | 定义Cython函数进行碰撞检测 |
| 从Python调用Sage | 需要使用Sage特定功能的Python脚本 | 无需直接运行Sage,可灵活调用功能 | 需使用特殊函数包装Sage表达式 | 用Python脚本调用Sage的数值积分器 |

11. 优化实践中的注意事项

在进行代码优化的实践过程中,还需要注意以下几点:
1. 备份代码 :在进行任何优化之前,务必备份原始代码。优化过程中可能会引入错误,备份可以让你在出现问题时恢复到原始状态。
2. 逐步优化 :不要一次性进行大量的修改,而是逐步进行优化。每次只进行一项优化操作,然后测量运行时间,观察优化效果。这样可以准确判断每个优化步骤的有效性。
3. 测试用例覆盖 :确保有完善的测试用例,在优化前后都运行测试用例,保证代码的功能正确性。优化过程中,可能会因为修改代码而引入新的错误。
4. 考虑可维护性 :虽然优化的目的是提高性能,但也要考虑代码的可维护性。过于复杂的优化可能会导致代码难以理解和维护,在优化时要在性能和可维护性之间找到平衡。

12. 未来优化趋势展望

随着计算机硬件技术的不断发展和编程语言的持续更新,代码优化也会呈现出一些新的趋势:
1. 并行计算普及 :多核CPU和GPU的广泛应用,使得并行计算成为提高代码性能的重要手段。未来,更多的优化方法会围绕并行计算展开,如使用CUDA、OpenMP等技术。
2. 人工智能辅助优化 :人工智能技术可以通过分析代码的结构和运行特征,自动找到优化的方向和方法。例如,使用深度学习模型预测代码的性能瓶颈,并提出优化建议。
3. 跨语言优化 :不同编程语言有各自的优势,未来可能会出现更多跨语言的优化方法,将不同语言的优点结合起来,实现更高效的代码。

通过掌握上述的代码运行时间测量和优化方法,以及注意事项和对未来趋势的了解,开发者可以在实际项目中更有效地提高代码的性能,满足不同场景下的需求。无论是小型的脚本还是大型的应用程序,都能从这些优化技巧中受益。

(SCI三维路径规划对比)25年最新五种智能算法优化解决无人机路径巡检三维路径规划对比(灰雁算法真菌算法吕佩尔狐阳光生长研究(Matlab代码实现)内容概要:本文档主要介绍了一项关于无人机三维路径巡检规划的研究,通过对比2025年最新的五种智能优化算法(包括灰雁算法、真菌算法、吕佩尔狐算法、阳光生长算法等),在复杂三维环境中优化无人机巡检路径的技术方案。所有算法均通过Matlab代码实现,并重点围绕路径安全性、效率、能耗和避障能力进行性能对比分析,旨在为无人机在实际巡检任务中的路径规划提供科学依据和技术支持。文档还展示了多个相关科研方向的案例代码资源,涵盖路径规划、智能优化、无人机控制等多个领域。; 适合人群:具备一定Matlab编程基础,从事无人机路径规划、智能优化算法研究或自动化、控制工程方向的研究生、科研人员及工程技术人员。; 使用场景及目标:① 对比分析新型智能算法在三维复杂环境下无人机路径规划的表现差异;② 为科研项目提供可复现的算法代码实验基准;③ 支持无人机巡检、灾害监测、电力线路巡查等实际应用场景的路径优化需求; 阅读建议:建议结合文档提供的Matlab代码进行仿真实验,重点关注不同算法在收敛速度、路径长度和避障性能方面的表现差异,同时参考文中列举的其他研究案例拓展思路,提升科研创新能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值