【问题标题】:difference of speed in making a Table制作表格的速度差异
【发布时间】:2012-01-03 07:33:03
【问题描述】:

我正在阅读a useful post at WRI blog on improving speed of code,我需要帮助来理解这一点。

比较这些速度

Timing[
 tbl = Table[i + j, {i, 1, 1000}, {j, 1, 1000}];     
]

{0.031, Null}

Timing[
 a = 1000;
 tbl = Table[i + j, {i, 1, a}, {j, 1, a}];
 ]

{0.422, Null}

因此,将限制的实际值放入表本身与外部相比要快得多。对此的解释,我确信它是正确的,但我需要帮助理解,是 Table 如果其限制是数字而不是非数字,则编译,这是因为它的属性是 HoldAll

但我的问题是:上述内容实际上如何工作,因为Table 的限制必须在某一时刻变成数字?我不会写

Clear[a]
tbl = Table[i + j, {i, 1, a}, {j, 1, a}]

上面给出了一个错误。

所以,对我来说,在Table 和内部写a=1000 应该没有区别,因为没有a 有一个数值,Table[] 不能做任何事情。因此,在 Table[] 可以做任何有用的事情之前,评估者必须在某个时间点将 a 替换为数字 1000,不是吗?

换句话说,Table 在这两种情况下最终应该看到的是{i, 1, 1000}, {j, 1, 1000}

所以,我认为这会发生的方式是这样的:

  1. Evaluator 将表参数中的 a 替换为 1000
  2. 评估员调用 Table 并得到结果,现在全是数字。
  3. Table 已编译,现在运行速度更快。

但似乎发生的事情是另一回事。 (由于HoldAll?)

  1. 表按原样接受它的参数。因为它有 HoldAll,所以它看到 a 而不是 1000。
  2. 它不调用 Compile,因为它的参数并非全是数字。
  3. 它现在生成一个带有 a 限制的表,Evaluator 将 a 评估为 1000
  4. 现在已生成表,所有限制都是数字,但现在速度较慢,因为未编译代码。

问题是:会发生上述情况吗?有人能解释一下解释这种时间差异的步骤吗?

另外,在上面的例子中,如何确保 Table 在两种情况下都被编译,即使使用一个变量作为限制?并非总是可以硬编码表格限制的数字,但有时必须为这些数字使用变量。是否应该明确使用Compile 命令? (我不直接使用Compile,因为我认为它是在需要时自动完成的。

编辑(1)

在下面 Mike 发表的关于使用呼叫时发现时间没有差异的回答中。

ClearAll[tblFunc];
Timing[a = 1000;
 tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}];
 Developer`PackedArrayQ[tblFunc[a]]
 ]

给予

{0.031, True}

但那是因为a 现在是函数内部的数字1000,一旦它被调用。由于 M 通过 VALUE 传递事物。

如果我们强制调用是通过引用进行的,这样a 就不会被评估,那么我们得到

ClearAll[tblFunc];
Timing[a = 1000;
 tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}];
 Developer`PackedArrayQ[tblFunc[Unevaluated@a]]
 ]

现在我们看到了预期的结果,因为现在a 在函数内部仍然是符号,我们回到第一方,现在它很慢,因为没有打包。而且由于没有打包,所以没有使用Compile。

{0.437, False}

编辑(2) 感谢大家的回答,我想我从他们那里学到了分配。

这是一个执行摘要,只是为了确保我一切正常。

编辑(3)

以下是我专门提供的与使 Mathematica 代码运行得更快的提示相关的链接。

  1. http://library.wolfram.com/howtos/faster/
  2. http://blog.wolfram.com/2011/12/07/10-tips-for-writing-fast-mathematica-code/
  3. https://stackoverflow.com/questions/4721171/performance-tuning-in-mathematica
  4. Using Array and Table Functions in Mathematica. Which is best when

【问题讨论】:

  • 请注意,您可以使用 Table[i + j, {i, 1, 1000}, {j, 1, i}] 之类的东西,这是当限制不是全部数字时不预编译 Table 的原因。
  • @David 我同意你的说法,只是想强调一下,Table 对于符号迭代器来说很慢的真正原因似乎是它无法确定结果是否会是 rectangular 数组,因此不能使用压缩数组。它不能使用Compile 的事实由此而来——因为打包数组是Compile 效率提升的原因(至少在编译为 MVM 目标时,我认为这是在自动编译中发生的)。
  • 另一个有趣的观察:Timing[Table[i + j, {i, Range[5000]}, {j, Range[5000]}];] 似乎也未编译,可能出于相同的原因。然而,在这里,唯一实用的内联选项是使用With,如With[{rr = Range[5000]}, Table[i + j, {i, rr}, {j, rr}]];。输入 5000 个值作为输入单元格不是很实用...
  • 一个很好的问答。节省了我很多时间在未知中徘徊。在@Nasser Edit(1) 上的一项观察“对于更高维度的表,如果至少一个索引是符号的,则不是打包/慢速”,我发现 它仅在内部索引上是正确的。在给定的示例中,它是外部符号索引,它对我来说仍然很快;一旦改为内部符号索引就会变慢。

标签: wolfram-mathematica


【解决方案1】:

所以这就是我认为正在发生的事情。您看到Table 上的数字和符号限制之间速度变慢的原因是您执行了双索引。每个子表(例如,遍历所有索引 j 以获得固定索引 i)是单独构建的,当限制是符号时,在构建每个子表之前需要一个额外的步骤来确定该限制。您可以通过检查来看到这一点,例如

Trace[a = 3;
      tbl = Table[i + j, {i, 1, a}, {j, 1, a}];
     ]

David 提供了一个很好的示例,说明您为什么要对每个子列表进行此检查。至于为什么 Mathematica 无法弄清楚何时不需要此检查,我不知道。如果你只有一个索引来求和,那么符号版本和数字版本之间的速度没有区别

Timing[tbl = Table[i + j, {j, 1, 1000}];]
{0.0012, Null}

Timing[a = 1000;
       tbl = Table[i + j, {j, 1, a}];
      ]
{0.0013, Null}

回答您对速度的跟进;使tbl 成为一个函数对于数字和符号限制都更快。

Timing[a = 1000;
       tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}];
       tblFunc[a];
      ]

{0.045171, Null}

对比

Timing[tbl = Table[i + j, {i, 1, 1000}, {j, 1, 1000}];]
{0.066864, Null}

Timing[a = 1000;
       tbl = Table[i + j, {i, 1, a}, {j, 1, a}];
      ]
{0.632128, Null}

如果您打算重用 tbl 构造,您将获得更快的速度。

b=1000;
Timing[tblFunc[b];]
{0.000013, Null}

【讨论】:

  • 我认为 tblFunc 很快,因为定义函数的 SetDelayed 在评估 tblFunc 时就像 With 一样,即。在计算 rhs 表达式之前,a 被其数值替换。因此,我们可以将限制作为符号参数,但仍然可以回到数值参数的情况。
  • @Faysal,我认为你是对的。用tblFunc 中的{j,1,i} 替换第二个迭代器会得到0.34 的时间,这再次与非函数符号版本相当(尽管快一点)。
【解决方案2】:

正如其他人所提到的,要监控的关键事项是包装和清单长度。我实际上没有看到 Timo 报告的差异:

ClearAll[tblFunc];
Timing[a = 1000;
 tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}];
 Developer`PackedArrayQ[tblFunc[a]]]

{0.077706, True}

vs

ClearAll[tbl];
Timing[
 tbl = Table[i + j, {i, 1, 1000}, {j, 1, 1000}];
 Developer`PackedArrayQ[tbl]]

{0.076661, True}

ClearAll[tbl];
Timing[a = 1000;
 tbl = Table[i + j, {i, 1, a}, {j, 1, a}];
 Developer`PackedArrayQ[tbl]]

{1.02879, False}

所以对我来说,唯一的区别是列表是否已打包。它是否是一个功能对我的设置时间没有影响。正如预期的那样,当您关闭自动编译时,上述所有时间都相同,因为没有发生打包:

SetSystemOptions["CompileOptions" -> {"TableCompileLength" -> Infinity}];

{1.05084, False}

vs

{1.00348, False}

{1.01537, False}

重置表自动编译长度:

SetSystemOptions["CompileOptions" -> {"TableCompileLength" -> 250}]

【讨论】:

  • 但是你发现时间上没有差异的原因不是因为当你调用一个函数时,然后在函数内部,a现在是一个数字吗?因为 M 调用是按 VALUE?请参阅我的帖子中的编辑(1)以了解我的意思。谢谢
  • 我只是在复制 Timo 的代码,并指出我没有看到他看到的时间差异。原因似乎是两者都挤满了——所以我同意你的观点。
【解决方案3】:

这有点 OT,但为了速度,您可能希望避免使用使用 Table 时隐含的逐项处理。相反,使用外部。这是我在系统上看到的内容:

   Timing[Outer[Plus, Range[5000], Range[5000]];]
{0.066763,Null}

   Timing[Table[i + j, {i, 1, 5000}, {j, 1, 5000}];]
{0.555197,Null}

相当大的差异。

【讨论】:

  • 有趣的观察!我相信这是由于Outer 对某些常见参数(例如PlusTimes)的特殊情况。我的预感是,对于一般功能,Table 更有可能更快,因为它可能会编译。另一个有趣的数据点:Timing[Table[i + j, {i, Range[5000]}, {j, Range[5000]}];] --> 在我的机器上 16 秒(即未编译)
  • 我得到的时间是在具有 3.4 GHz Core i7、16 GB RAM 的 iMac 上的 OS X 10.6.8 下的 Mathematica 8.0.4。每次评估都有一个新内核。
  • 我的意思是,使用Table 更精确地模拟Outer 显然会很慢。 Outer[Plus,Range[5000],Range[5000] 非常快(仅适用于 Plus 和类似功能),Table[i+j, {i,5000}, {j,5000}]With[{r=Range[5000]}, Table[i+j, {i,r}, {j,r}]] 非常快,而 Table[i+j, {i,Range[5000]}, {j,Range[5000]}] 非常慢。根据这个问题的其他答案,这是因为(缺乏)自动编译。我对Outer 如此之快的原因的猜测是它是针对Plus 的特殊情况(优化)。
  • 是的,Outer 的优势明显不同于其他功能。例如,使用 f[i_,j_] := Sin[i^2. j],使用 Outer 的 Timing 结果大约是 34.5,而使用 Table 的 Timing 结果刚好超过 42。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多