罗兰关于MATLAB的艺术

将想法转化为MATLAB

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

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

介绍

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

不要害怕环形使者

受到这篇文章的启发 ,一个同事用MATLAB写了下面这段来计算质数。
类型myCountPrimes.m
max_n = myCountPrimes(max_n) res = 0;for i = 2:max_n if MyIsPrime(i) res = res+1;Function result = MyIsPrime(n) if n <= 1 result = false;返回q = floor(√(n));For I = 2:q if mod(n, I) == 0 result = false;result = true;结束
numPrimes = myCountPrimes (1000000)
numPrimes = 78498
这段代码在循环上非常重——这正是我在早期的MATLAB时代被训练避免的事情,但感谢执行引擎,它不是太糟糕。(见 这篇最近的文章更多关于循环的速度 在MATLAB)
f = @() myCountPrimes(1000000);
originalSpeed时间= (f)
originalSpeed = 3.7878
尽管如此,我们还是可以尝试许多方法来加快这段代码的速度。我们可以利用循环的每一次迭代 myCountPrimes 是独立的还是使用并行的 parfor循环 .这很容易做到,但可能需要大量的计算资源。一旦你意识到你可以利用 parfor 在这种情况下,通过尽可能多的CPU核来解决问题变得很诱人。这些天来,我最喜欢的方法就是点燃一个 云中心集群 把核心数量提高到有意义的水平。

改变国防部地板上

侧写师告诉我很多时间都花在排队上了
如果mod(n,i) == 0
一种方法是问 “我们能把这句台词改快一点吗?” .哪一种选择可能有效并不总是显而易见的,所以我通常只是想出一种不同的方法来做某事,并尝试一下。写表达式的另一种方法 国防部(n, i) = = 0 地板(n / i) = = n /我 结果证明它要快得多!我在 floorMyCountPrimes 函数,检查它是否给出正确的结果,并证明它确实要快得多。
numPrimes = floorMyCountPrimes (1000000)
numPrimes = 78498
f = @() floorMyCountPrimes(1000000);
floorSpeed时间= (f)
floorSpeed = 0.5296

尝试整数类型的整数算法

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

MATLAB作为一个算法库

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

系统信息

如果您在自己的机器上运行此脚本,几乎肯定会得到与我不同的结果。使用 版本 功能与卓越 CPU信息 by Ben Tordoff,这里是我的机器和MATLAB版本的细节。
versionInfo = [“MATLAB释放R”版本(“发布”)]
versionInfo =“R2021a MATLAB版本”
cpuinfo ()
ans =结构体字段:
名称:“Intel(R) Core(TM) i7-8650U CPU @ 1.90GHz”时钟:“2112 MHz”缓存:“1024kb”NumProcessors: 4 OSType:“Windows”OSVersion:“Microsoft Windows 10 Pro”

你对速度的需求?

MATLAB比以往任何时候都要快,而且在未来还会继续变得更快。这与改进的硬件相结合,将帮助您回答比以前更大、更复杂的问题。然而,总有一些场合,我们需要更多。有什么MATLAB加速技术你觉得有用,你希望在MATLAB中更快?让我们知道 在这里
辅助函数
函数结果= floorMyIsPrime (n)
函数检查n是否为素数
如果n < = 1
结果= false;
返回
结束
q =地板(sqrt (n));
我= 2:问
如果地板(n / i) = = n /我
结果= false;
返回
结束
结束
结果= true;
结束
函数res = floorMyCountPrimes (max_n)
%计算max_n之前(包括max_n)有多少质数
res = 0;
我= 2:max_n
如果floorMyIsPrime(我)
res = res + 1;
结束
结束
结束
函数结果= uint32MyIsPrime (n)
函数检查n是否为素数
如果n < = 1
结果= false;
返回
结束
q =地板(sqrt (n));
n = uint32 (n);
i = uint32 (2): q
如果国防部(n, i) = = 0
结果= false;
返回
结束
结束
结果= true;
结束
函数res = uint32MyCountPrimes (max_n)
%计算max_n之前(包括max_n)有多少质数
res = 0;
我= 2:max_n
如果uint32MyIsPrime(我)
res = res + 1;
结束
结束
结束
版权所有:The MathWorks, Inc.
|

评论

要留下评论,请点击在这里登录到您的MathWorks帐户或创建一个新帐户。