制作表格的速度差异
我正在阅读一篇有用的帖子WRI 关于提高代码速度的博客,我需要帮助来理解这一点。
比较这些速度
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
的限制是数字与非数字,则编译 Table
,这是因为它的属性是 保留全部
。
但我的问题是:上面的内容实际上如何工作,因为 Table
的限制在某一时刻必须变成数字?我不能写
Clear[a]
tbl = Table[i + j, {i, 1, a}, {j, 1, a}]
上面给出了一个错误。
因此,对我来说,在 Table
外部写入 a=1000
与在 Table
内部写入应该没有什么区别,因为没有 a
具有数值, Table[]
不能做任何事情。因此,在 Table[]
可以做任何有用的事情之前,评估器必须在某个时间点将 a
替换为数字 1000,不是吗?
换句话说,在这两种情况下,Table
最终应该看到的是 {i, 1, 1000}, {j, 1, 1000}
。
因此,我认为发生这种情况的方式是这样的:
- 评估器将表参数中的
a
替换为 1000 - 评估器用结果调用
Table
,现在结果都是数字。 - 表编译,并且运行速度更快。
但似乎发生的却是另一回事。 (由于 HoldAll
?)
- Table 按原样接受其参数。因为它有 HoldAll,所以它看到的是
a
而不是 1000。 - 它不会调用 Compile,因为它的参数并不全是数字。
- 它现在生成一个具有
a
限制的表,评估器将a
评估为 1000 - 现在生成表,所有限制都是数字,但现在速度较慢,因为代码未编译。
问题是:上面的情况会发生吗?有人可以解释一下解释这种时间差异的步骤吗?
另外,在上面的示例中,如何确保在两种情况下都编译了表,即使使用变量为极限?并不总是可以对表限制的数字进行硬编码,但有时必须使用变量来表示这些数字。是否应该显式使用 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 代码运行得更快。
I was reading a useful post at WRI blog on improving speed of code, and I need help in understanding this one.
Compare these speeds
Timing[
tbl = Table[i + j, {i, 1, 1000}, {j, 1, 1000}];
]
{0.031, Null}
and
Timing[
a = 1000;
tbl = Table[i + j, {i, 1, a}, {j, 1, a}];
]
{0.422, Null}
So it is much faster when putting the actual value for the limit inside the table itself vs outside. The explanation for this, which I am sure it is correct, but I need help in understanding, is that Table
is compiled if its limit are numeric vs. not, this is because its Attributes is HoldAll
.
But my question is: How would the above actually work, because the limits to Table
must, at one point, become numeric anyway? I can't write
Clear[a]
tbl = Table[i + j, {i, 1, a}, {j, 1, a}]
The above gives an error.
So, for me, writing a=1000
outside Table
vs. inside, should have made no difference, since without a
having a numerical value, Table[]
can't do anything. So the replacing of a
by the number 1000 must occur at one point of time by evaluator before Table[]
can do anything useful, would it not?
In other words, what Table
should see, eventually, is {i, 1, 1000}, {j, 1, 1000}
in both cases.
So, the way I thought this would happen is this:
- Evaluator replaces
a
by 1000 in the arguments of table - Evaluator calls
Table
with the result, which is now all numeric. - Table Compiles, and runs faster now.
But what seems to happen is something else. (due to HoldAll
?)
- Table takes its arguments, as is. Since it has HoldAll, so it sees
a
and not 1000. - It does not call Compile since its arguments are not all numbers.
- It now generate a table with the
a
limit, Evaluator evaluatesa
to 1000 - Table is generated now all limits are numeric, but slower now since code is not compiled.
Question is: Does the above sort of what happens? Could someone explain the steps that would have happened to explain this difference in timing?
Also, how would one insure that Table is Compiled in both cases in the above example, even if one uses a variable for the limit? It is not always possible to hardcode the numbers for the table limits, but one must sometime use a variables for these. Should one explicitly use the Compile
command? (I do not use Compile
directly, since I assumed it is done automatically when needed).
edit(1)
In answer to post by Mike below on finding no difference in timing when using a call.
ClearAll[tblFunc];
Timing[a = 1000;
tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}];
Developer`PackedArrayQ[tblFunc[a]]
]
gives
{0.031, True}
But that is because a
is now the number 1000
INSIDE the function, once it is called. Since M passes things by VALUE.
If we force the call to be by reference, so that a
is left unevaluated, then we get
ClearAll[tblFunc];
Timing[a = 1000;
tblFunc[a_] := Table[i + j, {i, 1, a}, {j, 1, a}];
Developer`PackedArrayQ[tblFunc[Unevaluated@a]]
]
now we see the expected result, since now a
is still symbolic INSIDE the function, we are back to square one, and now it is slow, since not packed. And since it is not packed, Compile is not used.
{0.437, False}
edit(2)
Thanks to everyone for the answers, I think I learned allot from them.
Here is an executive summary, just to make sure I got everything ok.
edit(3)
Here are links I have specially related to hints to use to making Mathematica code runs faster.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
所以这就是我认为正在发生的事情。您看到
Table
上的数字和符号限制之间速度变慢的原因是由于您执行了双索引。每个子表(例如,遍历固定索引i
的所有索引j
)都是单独构造的,并且当限制是符号时,需要一个额外的步骤来计算出在构建每个子表之前限制。您可以通过检查来看到这一点,例如David 给出了一个很好的例子,说明为什么您要对每个子列表。至于为什么Mathematica无法弄清楚何时不需要此检查,我不知道。如果您只有一个索引要求和,则符号版本和数字版本之间的速度没有差异
回答您关于速度的后续问题;对于数字和符号限制,使
tbl
成为一个函数会更快。vs.
如果您打算重用
tbl
结构,您将获得更快的速度。So this is what I think is happening. The reason why you see the slow down between a numeric and a symbolic limit on
Table
is due to the fact that you do a double index. Each sub-table (e.g. going over all indicesj
for a fixed indexi
) is constructed separately and when the limit is symbolic there is an extra step involved in figuring out that limit before constructing each sub table. You can see this by examining, e.g.David gives a good example for why you would want to do this check for every sub list. As to why Mathematica cannot figure out when this check is not needed I have no clue. If you only have one index to sum over there is no difference in speed between the symbolic and numeric version
To answer your follow up regarding speed; making
tbl
a function is faster for both numeric and symbolic limits.vs.
You gain even more speed if you intend to reuse the
tbl
construction.正如其他人提到的,要监控的关键是包装和清单长度。实际上,我没有看到蒂莫报告的差异:
所以对我来说,唯一的区别是列表是否已打包。它是否是一个函数对我的设置时间没有影响。正如预期的那样,当您关闭自动编译时,上述所有内容的计时都是相同的,因为没有发生打包:
重置表自动编译长度:
The key things to monitor, as others have mentioned, are packing and list length. I actually don't see the differences that Timo reports:
So for me the only difference is if the list is packed. Whether it is a function makes no difference to timing on my set up. And as expected when you switch off autocompilation the timings are the same for all of the above because no packing occurs:
reset the table autocompile length:
这有点 OT,但为了速度,您可能希望避免使用 Table 中隐含的逐项处理。相反,使用外部。这是我在我的系统上看到的情况:
相当显着的差异。
This is slightly OT, but for speed here you might want to avoid using the item-by-item processing that's implicit in using Table. Rather, use Outer. Here's what I'm seeing on my system:
Quite a dramatic difference.