Loren在Matlab的艺术上

将想法转化为MATLAB

更快的表索引,datetime数组,和其他数据类型

今天我想介绍一位客座博主Stephen Doe,他在MathWorks的MATLAB文档团队工作。在今天的文章中,Stephen讨论了在索引表时如何利用最近的性能改进。同样的方法适用于许多不同的数据类型。虽然发布说明描述了性能改进,但在今天的帖子中,Stephen还基于一个简单的代码示例提供了进一步的建议。

内容

那么,是什么改善,如何?

从R2020a开始,MATLAB数据类型团队已经为索引特定类型的数组提供了实质性的性能改进。改进的性能来自于适当的优化。类中访问多个数组元素或给多个数组元素赋值时,它们最明显为了-环形。改进的数据类型是:

这些改进都超过两个版本(R2019b和R2020a)交付。因此,在这篇文章中我比较R2020a到R2019a,最新版本的性能,无需任何的这些改进。总体而言,我们看到,对于这些数据类型,分配到数组元素是比R2019a快很多倍。

  • 1.5-2X.对a的元素赋值更快表格时间表多变的
  • 量级分配给元素时更快(至少)日历分类datetime,期间数组

您可以找到完整的详细信息,包括测试代码,说明发行说明中发行说明中的​​序列之间的性能改进。在发生时,我们有一种新的格式,用于描述具有更多定量细节的性能增强功能。这些链接将您直接带到R2019B和R2020A的性能发行说明:

有了这样的细节,有没有更多的可说的需要呢?是的.我将向您展示如何最好利用这些性能改进。我也将解释一些这些改进的情况生效。

分配表元素为了-环形

这是一个简单的例子,可以充分利用索引性能改进。在此示例中,我在规则的时间步骤计算射弹的位置。我使用公式,其中每个步骤的位置和速度取决于前一步的位置和速度。我创建一个表,我使用a将位置和速度分配给表的行为了-环形。该图显示通过弹丸在该例子中所采用的路径。

下面是一个函数,命名为弹丸,计算位置和速度,然后将它们分配到一张表格中。(它取决于另一个函数,,其计算的位置和速度在一个时间步长)。注意的是,在为了-loop时,我使用点符号来访问表变量。然后我使用循环计数器索引变量(T.x(I)T.y(I), 等等)。(我附弹丸功能这篇博客文章的末尾)。

函数T =弹丸(V,角度)%PROJECTILE.M  - 函数来创建弹位置的表在自由落体的速度%。dt = 0.001;%0.001秒t =表('尺寸'[30 / DT 4],“VariableTypes”,[“替身”“替身”“替身”“替身”],“VariableNames”,[“x”“y”“vx”“v”]);t {1级别:} = [0 0 V * COSD(角度)V * SIND(角)];为了i = 2:height(T) [T.x(i), T.y(i), T.vx(i), T.vy(i)] = step(T.x(i-1), T.y(i-1), T.vx(i-1), T.vy(i-1), dt);结尾结尾

我们现在把弹丸起始速度50米/秒,角度45度。通过这个调用,输出表有30000行。我将使用函数显示前5行。

45 T =弹(50);头(T, 5)
ANS = 5×4表X Y VX VY ________ ________ ______ ______ 0 0 35.355 35.355 0.035355 0.035341 35.355 35.346 0.070711 0.070671 35.355 35.336 0.10607 0.10599 35.355 35.326 0.14142 0.1413 35.355 35.316

现在让我们使用Tic.toc估计执行时间这一功能在两个不同版本的MATLAB:R2019a(最新改进前)和R2020a。

tic T =弹丸(50,45);toc

R2019a:13.06

R2020A:7.64秒。

(我在同一台机器上进行了这两个调用:一台Windows 10、Intel®Xeon®W-2133 @ 3.60 GHz的测试系统。)

此代码是关于R2020a快1.70x倍!你会看到性能提升至少这个好与非常大的表和时间表(百万行),以及在分类datetime数组。

现在,请注意,我以这样的方式写了这个例子,以强制使用的为了-环形。如果你可以,最好的策略是vectorize您的代码是最好的性能。“矢量化”代码几乎意味着在阵列上运行而不是数组的元素。例如,呼叫更有效Z = x + y而不是打电话Z(I)= X(I)+ Y(I)在一个为了-环形。

但是,如果您有无法向量化的代码,因为它访问许多其他表或数组元素——就像我的示例中的代码一样——那么这些数据类型的性能改进将帮助您的代码运行得更快。

现在我们可以开始了,对吧?没有那么快。

脚本和试着抓被认为有害

好的,有害的夸张。将代码放在脚本中或内部的内容完全正常试着抓块,或使用工作区中的变量。在所有这些情况下,我为此写的代码弹丸函数返回完全相同的表。

但如果该代码处于脚本或试验块,或者如果要与工作区变量交互式工作,则没有性能增强。例如,让我在文件中重写代码作为脚本projectile_script.m..在该脚本中,我分配相同的起始速度和角度如上。(我已经附加的副本projectile_script.m.直到这篇博文的结尾。)

%projectile_script.m  - 创建弹丸位置表的脚本%和自由落体下的速度。dt = 0.001;%0.001秒v = 50;% 50 m / s角度= 45;% 45度t =表('尺寸'[30 / DT 4],“VariableTypes”,[“替身”“替身”“替身”“替身”],“VariableNames”,[“x”“y”“vx”“v”]);t {1级别:} = [0 0 V * COSD(角度)V * SIND(角)];为了i = 2:height(T) [T.x(i), T.y(i), T.vx(i), T.vy(i)] = step(T.x(i-1), T.y(i-1), T.vx(i-1), T.vy(i-1), dt);结尾

现在我调用函数并计时:

Tic Projectile_script Toc.

R2019a:12.36

R2020A:11.83

两个版本之间的区别是现在很多小!发生了什么事?

好吧,它很复杂。实质上,MATLAB适用于在该代码行中完成的索引:

[T.x(我),T.y(我),T.vx(我),T.vy (i)) =步骤(T.x(张),T.y(张),T.vx(张),T.vy(张),dt);

要利用这些数据类型的就地优化,您必须在函数内执行索引。如果您使用工作空间变量或脚本中这样做,则不会看到完整的性能改进。

此外,改进时丢失索引代码是内试着抓块,即使该块本身在函数内。但在这种情况下,您可以通过将索引代码放入单独的功能来获得性能。(P.S.嵌套函数也是如此。)

我不会进入完整的细节试着抓块在这个岗位。不过,我附上两个文件复制到这个帖子的末尾,projectile_try_catch.m.projectile_try_regained.m.这些文件显示的问题及其解决方法。

总而言之,这个表显示了我为这篇文章编写的不同代码片段,以及每种情况下您可以期待的性能。

示例文件 代码类型 执行时间
Projectile.m. 函数 7.64小号
projectile_script.m. 脚本 11.83秒
projectile_try_catch.m. 索引代码试着抓堵塞 11.75 S.
projectile_try_regained.m 试着抓在单独的功能块,但索引代码 7.43秒

最佳实践

总而言之,这里是在编写数据类型的代码时要记住最佳性能的最佳实践表格datetime,分类

  • 矢量化代码。例如,在表变量上运行(T.X)而不是表变量的元素(T.X(I)
  • 放你的表格datetime,分类索引函数的代码,如果你做了很多索引,并且不能向量化你的代码。
  • 避免脚本,至少对于需要大量索引的代码。将索引代码放在函数中。
  • 避免Try-catch块用于进行大量索引的代码。把它放到它自己的功能中。

由于MATLAB数据类型的团队继续在提高性能的工作,我们希望听到更多关于我们的数据类型,你的经验。请使用表,时间表更告诉我们您的挑战,datetime,或分类数组这里

代码样本

函数[X,Y,VX,VY =步骤(X,Y,VX,VY,DT)%步骤 - 基于输入位置计算位置和速度,%速度和局部引力加速度。米的位置,m / s中的%速度,dt以秒为单位。g = -9.8;%-9.8米/秒^ 2VY = VY + G * DT;Y = Y + VY * DT +(G / 2)* DT ^ 2;x = x + vx * dt;结尾函数T =弹丸(V,角度)%PROJECTILE.M  - 函数来创建弹位置的表在自由落体的速度%。dt = 0.001;%0.001秒t =表('尺寸'[30 / DT 4],“VariableTypes”,[“替身”“替身”“替身”“替身”],“VariableNames”,[“x”“y”“vx”“v”]);t {1级别:} = [0 0 V * COSD(角度)V * SIND(角)];为了i = 2:height(T) [T.x(i), T.y(i), T.vx(i), T.vy(i)] = step(T.x(i-1), T.y(i-1), T.vx(i-1), T.vy(i-1), dt);结尾结尾%projectile_script.m  - 创建弹丸位置表的脚本%和自由落体下的速度。dt = 0.001;%0.001秒v = 50;% 50 m / s角度= 45;% 45度t =表('尺寸'[30 / DT 4],“VariableTypes”,[“替身”“替身”“替身”“替身”],“VariableNames”,[“x”“y”“vx”“v”]);t {1级别:} = [0 0 V * COSD(角度)V * SIND(角)];为了i = 2:height(T) [T.x(i), T.y(i), T.vx(i), T.vy(i)] = step(T.x(i-1), T.y(i-1), T.vx(i-1), T.vy(i-1), dt);结尾函数T = projectile_try_catch(V,角度)%projectile_try_catch.m  -  Projectile.m的副本,但尝试捕获% 堵塞。R2020A就地优化因试用捕获而丢失% 堵塞。dt = 0.001;%0.001秒t =表('尺寸'[30 / DT 4],“VariableTypes”,[“替身”“替身”“替身”“替身”],“VariableNames”,[“x”“y”“vx”“v”]);试一试t {1级别:} = [0 0 V * COSD(角度)V * SIND(角)];为了i = 2:height(T) [T.x(i), T.y(i), T.vx(i), T.vy(i)] = step(T.x(i-1), T.y(i-1), T.vx(i-1), T.vy(i-1), dt);结尾抓住结尾结尾函数T = projectile_try_regained(V,角度)% PROJECTILE_TRY_REGAINED。M -投射体的副本。M,但是要试着接住%块放置在一个单独的函数。R2020a就地优化是因为try-catch块在一个单独的本地函数中。试一试T = projectile_local (v,角);抓住结尾结尾函数T = projectile_local (v,角)%这是创建Projectile_try_regate的表的代码。%以获得最佳性能,请勿在此处使用试用捕获,而是在%调用函数。dt = 0.001;t =表('尺寸'[30 / DT 4],“VariableTypes”,[“替身”“替身”“替身”“替身”],“VariableNames”,[“x”“y”“vx”“v”]);t {1级别:} = [0 0 V * COSD(角度)V * SIND(角)];为了i = 2:height(T) [T.x(i), T.y(i), T.vx(i), T.vy(i)] = step(T.x(i-1), T.y(i-1), T.vx(i-1), T.vy(i-1), dt);结尾结尾




发布时间与MATLAB®R2020a

|

评论

要发表评论,请点击这里登录您的MathWorks帐户或创建新的。