更快的表索引,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);结尾结尾
评论
要发表评论,请点击这里登录您的MathWorks帐户或创建新的。