罗兰谈MATLAB的艺术

将想法转化为MATLAB

使MATLAB代码运行更快的主要案例研究

今天我要欢迎一位客座博主, 迈克裘槎 他最近加入MathWorks,担任客户成功工程师,此前他长期支持学术计算研究。金宝app

简介

我职业生涯的大部分时间都致力于与科学家和工程师合作,以改进他们的计算工作流程。有很多事情可以做,包括鼓励使用版本控制或 单元测试 但是,到目前为止,最常见的请求是 “你能让我做的这个东西跑快一点吗?”
二十多年来,这个简单的问题让我探索了技术计算的许多领域,比如并行计算、数值分析、 高性能计算和云计算 GPU计算 ,数学优化, 自动分化 还有多种语言,仅举几例。
在其核心,使程序更快的艺术归结为两个简单步骤的迭代。
  1. 找出到底什么是慢的。
  2. 尝试一些不同的方法,看看在给出正确答案的同时是否会更快
正如你所想象的,上面隐藏着一个复杂的世界。“尝试不同的东西”可能意味着使用语言特性来优化我所编写的算法,尝试并行化,甚至切换到完全不同的算法。我通常会在第2点上添加一个额外的约束,即“花费尽可能少的努力”,因为我本质上是懒惰的。
MATLAB的分析器 最近收到了一个接口大修 提供代码执行时间的火焰图视图,有助于注意第一点。探索剖析器可以提供丰富的博客内容,就像探索所有的 算法性能增强 我们从一个版本到另一个版本。
多亏了 MATLAB执行引擎 (在之前的博客中讨论过 在这里 而且 在这里 ), 各种其他优化 ,现代MATLAB比以往任何时候都要快,如下图所示,我们正在不断地在这方面进行改进。
当然,我们总是需要优化我们的代码以提高速度,我想分享一个最近内部讨论的例子。

不要害怕环形使者

受到这篇文章的启发 ,我的同事用MATLAB写了下面的程序来计数质数。
类型myCountPrimes.m
计算max_n以内有多少质数函数res = myCountPrimes(max_n) res = 0;如果MyIsPrime(i) res = res+1;Function result = MyIsPrime(n) if n <= 1 result = false;返回端q = floor(√(n));对于I = 2:q如果mod(n, I) == 0结果= false;返回最终结果= true;结束
numPrimes = myCountPrimes(1000000)
numPrimes = 78498
这段代码循环非常多——正是我在早期使用MATLAB时被训练要避免的那种事情,但多亏了执行引擎,它还不算太糟。(见 这是最近的一篇关于循环速度的文章 在MATLAB)
f = @() myCountPrimes(1000000);
originalSpeed = timeit(f)
originalSpeed = 3.7878
即便如此,我们还是可以尝试很多方法来加速这段特定的代码。我们可以利用这样一个事实,即循环的每一次迭代 myCountPrimes 是独立的还是平行的 parfor循环 .这很容易做到,但可能需要大量的计算资源。一旦你意识到你可以利用 parfor ,为了解决这个问题,你可能会抛出尽可能多的CPU内核。我最近最喜欢的方法就是 云中心集群 在合理范围内增加核心数量。

改变国防部地板上

剖析师告诉我,很多时间都花在了这条线上
如果mod(n,i) == 0
所以一种方法是问 “我们能把这条线重写得更快一点吗?” .并不总是很明显哪些替代方法可能有效,所以我经常简单地想出一个不同的方法来做某事并尝试一下。表达式的另一种写法 国防部(n, i) = = 0 Floor (n/i) == n/i 事实证明,它要快得多!我在 floorMyCountPrimes 函数,检查它是否给出正确的结果,并证明它确实快得多。
numPrimes = floorMyCountPrimes(1000000)
numPrimes = 78498
f = @() floorMyCountPrimes(1000000);
floorSpeed = timeit(f)
floorSpeed = 0.5296

为整数算法尝试整数类型

默认情况下,MATLAB中的所有数字都是浮点类型 这对于很多数值计算来说是很好的但是这里我们要处理的是一个纯整数算法。在代码中明确这一点有时会带来好处,这就是其中之一!事实证明 国防部 函数在处理浮点数时必须做的工作比这个特定示例严格必要的工作多得多。作为程序员,我知道我的算法完全是基于整数的,所以我可以切换到使用 uint32 对于一些变量。也就是说,我改变了的主循环 MyIsPrime
Q =√(n);
N = uint32(N);
I = uint32(2):q
如果Mod (n,i) == 0
结果= false;
返回
结束
结束
这使得MATLAB可以使用专门的和更快的版本 国防部 函数,因为它现在知道我们处理的是整数而不是双精度对象。
我在 uint32MyIsPrime 函数定义在这篇文章的最后,并通过uint32调用它 MyCountPrimes。 首先让我们检查结果是否与原始函数一致,然后计时
numPrimes = uint32MyCountPrimes(1000000)
numPrimes = 78498
f = @() uint32MyCountPrimes(1000000);
uint32Speed = timeit(f)
uint32Speed = 0.5660
使用这种方法或基于地板的方法,我们实现了相对较少的工作和技术的显著性能提升,例如 parfor 如果我们需要更快的速度,并行化仍然是摆在桌面上的。

MATLAB作为算法库

最后,我们可以认识到,我们用来计算质数的基本算法并不是最优的,而是使用另一种算法。举个极端的例子,我们可以记住MATLAB是一个数值算法库,也是一种编程语言,所以要找到一个直接解决我们想要解决的问题的库,而不是自己动手。快速搜索文档可以发现 质数 返回质数数组的函数。我们生成这个数组并计算元素,如下所示。
numPrimes = nummel(质数(1000000))
numPrimes = 78498
F = @()数字(质数(1000000));
builtinSpeed = timeit(f)
builtinSpeed = 0.0029
对我来说已经足够快了!

系统信息

如果您在自己的机器上运行这个脚本,几乎肯定会得到不同的结果。使用 版本 功能和卓越 CPU信息 这是我的机器和MATLAB版本的细节。
versionInfo = [MATLAB Release R版本(“发布”)]
versionInfo =MATLAB版本R2021a
cpuinfo ()
ans =带字段的结构:
名称:'英特尔(R)酷睿(TM) i7-8650U CPU @ 1.90GHz'时钟:'2112 MHz'缓存:'1024 KB' NumProcessors: 4 OSType: 'Windows' OSVersion: 'Microsoft Windows 10 Pro'

你对速度的需求?

MATLAB比以往任何时候都要快,而且在未来还会越来越快。这与改进的硬件相结合,将帮助您回答比以前更大更复杂的问题。然而,总有一些场合我们需要更多。你觉得MATLAB中哪些加速技术是有用的?你希望MATLAB中的哪些加速技术更快?让我们知道 在这里
辅助函数
函数result = floorMyIsPrime(n)
检查n是否是质数的函数
如果N <= 1
结果= false;
返回
结束
Q =√(n);
I = 2:q
如果Floor (n/i) == n/i
结果= false;
返回
结束
结束
结果= true;
结束
函数res = floorMyCountPrimes(max_n)
计算在max_n以内有多少质数
Res = 0;
I = 2:max_n
如果floorMyIsPrime(我)
Res = Res +1;
结束
结束
结束
函数result = uint32MyIsPrime(n)
检查n是否是质数的函数
如果N <= 1
结果= false;
返回
结束
Q =√(n);
N = uint32(N);
I = uint32(2):q
如果Mod (n,i) == 0
结果= false;
返回
结束
结束
结果= true;
结束
函数res = uint32MyCountPrimes(max_n)
计算在max_n以内有多少质数
Res = 0;
I = 2:max_n
如果uint32MyIsPrime(我)
Res = Res +1;
结束
结束
结束
The MathWorks, Inc.版权所有
|

评论

如欲留言,请点击在这里登录您的MathWorks帐户或创建一个新帐户。