洛伦谈MATLAB的艺术

将想法转化为MATLAB

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

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

目录

那么,有什么改进,怎么样?

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

在两种版本(R2019B和R2020A)上交付了改进。所以,在这篇文章中,我将R2020A的性能与R2019A进行比较,最新版本没有任何这些改进。总的来说,我们看到,对于这些数据类型,对数组元素的分配比R2019A更快多次。

  • 1.5-2x对a的元素赋值更快表格时间表多变的
  • 数量级指定给的元素时更快(至少更快)日历,分类,datetime,期间阵列

您可以在发行说明中找到完整的详细信息,包括说明不同版本之间性能改进的测试代码。碰巧,我们有一种新的格式来描述更定量的性能增强。这些链接直接将您带到R2019b和R2020a的性能发行说明:

有了这种细节,还有更多需要说吗?是的。我将向您展示如何最好地利用这些性能改进。我还将解释这些改进的一些情况不要生效。

分配表元素对于-环形

下面是一个充分利用索引性能改进的简单示例。在这个示例中,我计算了固定时间步长下的投射物位置。我使用了一个公式,其中每一步的位置和速度取决于前一步的位置和速度。我创建了一个表,并分配了位置和速度使用对于-环形。该图显示了该示例中的射弹所采取的路径。

下面是一个函数,命名为弹丸,计算位置和速度,然后将它们分配到一张表格中。(它取决于另一个函数,,它在一次步骤中计算位置和速度。)注意对于-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 v*cosd(角度)v*sind(角度)];对于i=2:高度(T)[T.x(i),T.y(i),T.vx(i),T.vy(i)]=步长(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 table 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

现在让我们使用抽搐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.70倍!您将看到至少这件良好的性能改进,具有非常大的桌子和时间表(数百万行),也是在分类datetime数组。

现在,请注意,我以强制使用A的方式写下这个例子对于-循环。如果可以,最好的策略就是vectorize您的代码将获得最佳性能。“矢量化”代码相当于在数组上操作,而不是在数组的元素上操作。例如,打电话更有效Z = x + y而不是打电话z(i)= x(i)+ y(i)在一个对于-环形。

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

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

脚本和试一试被认为有害的

好的,有害的这是一种夸张。将代码放在脚本中或试一试在所有这些情况下,我为弹丸函数返回完全相同的表。

但是,如果该代码位于脚本或try-catch块中,或者您正在以交互方式处理工作区变量,则没有性能增强projector_script.m.在脚本中,我分配相同的起始速度和角度,如上所述。(我附上了一份副本projector_script.m直到这篇博文的结尾。)

%射弹脚本.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 v*cosd(角度)v*sind(角度)];对于i=2:高度(T)[T.x(i),T.y(i),T.vx(i),T.vy(i)]=步长(T.x(i-1),T.y(i-1),T.vx(i-1),T.vy(i-1),dt);终止

现在我调用函数并计时:

tic脚本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);

要利用这些数据类型的就地优化,您必须在函数中执行索引。如果使用工作区变量或在脚本中执行此操作,您将看不到全面的性能改进。

此外,当索引代码在a内时,改进丢失试一试块,即使该块本身在函数中。但在这种情况下,您可以通过将索引代码放入单独的函数中来恢复性能。(注意,嵌套函数也是如此。)

我不想详细讨论这个问题试一试这篇文章中的块。但是,我已将两个文件附加到本帖子的末尾,射弹,试着抓projectile_try_regained.m..这些文件显示了问题及其解决方法。

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

示例文件 代码类型 执行时间
射弹 函数 7.64秒
projector_script.m 剧本 11.83秒
射弹,试着抓 中的索引代码试一试 11.75秒
projectile_try_regained.m. 试一试块,但单独函数中的索引代码 7.43秒

最佳实践

总而言之,在为数据类型(例如,XML)编写代码时,要记住以下最佳实践,以获得最佳性能表格,datetime,分类:

  • 尽可能将代码矢量化。例如,对表变量进行操作(T.X)而不是表变量的元素(T.X(i))
  • 把你的表格,datetime,分类函数中的索引代码,如果您正在进行大量索引,并且无法向上化代码。
  • 避免脚本,至少对于需要大量索引的代码。将索引代码放在函数中。
  • 避免尝试使用catch块来获取执行大量索引的代码。将其放入自己的函数中。

由于MATLAB数据类型团队继续努力提高性能,因此我们希望更多地了解您的数据类型的体验。请使用桌子,时刻表,告诉我们更多关于您的挑战的信息,datetime,或分类阵列这里

代码示例

函数[x,y,vx,vy] =步骤(x,y,vx,vy,dt)%步骤M-根据输入位置计算位置和速度,%速度和局部重力加速度。以米为单位的位置,%速度单位为m/s,dt单位为秒。g = -9.8;%-9.8 m / s ^ 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 v*cosd(角度)v*sind(角度)];对于i=2:高度(T)[T.x(i),T.y(i),T.vx(i),T.vy(i)]=步长(T.x(i-1),T.y(i-1),T.vx(i-1),T.vy(i-1),dt);终止终止%射弹脚本.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 v*cosd(角度)v*sind(角度)];对于i=2:高度(T)[T.x(i),T.y(i),T.vx(i),T.vy(i)]=步长(T.x(i-1),T.y(i-1),T.vx(i-1),T.vy(i-1),dt);终止函数t = projectile_try_catch(v,角度)%投射物\u TRY\u CATCH.M-投射物.M的副本,但带有一个TRY CATCH%block.R2020a就地优化由于try-catch而丢失%街区。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:高度(T)[T.x(i),T.y(i),T.vx(i),T.vy(i)]=步长(T.x(i-1),T.y(i-1),T.vx(i-1),T.vy(i-1),dt);终止抓住终止终止函数t = projectile_try_regate(v,角度)% PROJECTILE_TRY_REGAINED。M -投射体的副本。M,但是要试着接住%块放置在单独的函数中。R2020A就地优化是因为try-catch块在一个单独的本地函数中。试一试T = projectile_local (v,角);抓住终止终止函数T = projectile_local (v,角)%这是为重新获得的抛射物创建表的代码。%为了获得最佳性能,不要在此处使用try-catch,而是在%调用函数。dt=0.001;T=表('尺寸',[30 / dt 4],“VariableTypes”,[“替身”,“替身”,“替身”,“替身”],“VariableNames”,[“x”,“y”,“vx”,“v”]); T{1,:}=[0 v*cosd(角度)v*sind(角度)];对于i=2:高度(T)[T.x(i),T.y(i),T.vx(i),T.vy(i)]=步长(T.x(i-1),T.y(i-1),T.vx(i-1),T.vy(i-1),dt);终止终止




发布与MATLAB®R2020A

|

댓글

댓글을남기려면링크를 클릭하여 数学作品계정에 로그인하거나 계정을 새로 만드십시오.