Я читал полезный пост в блоге 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
компилируется, если его предел числовой, а не нет, это потому, что его атрибуты HoldAll
.
Но мой вопрос заключается в следующем: как на самом деле сработает вышеперечисленное, потому что пределы Table
в какой-то момент должны стать числовыми?Я не могу написать
Clear[a]
tbl = Table[i + j, {i, 1, a}, {j, 1, a}]
Вышеприведенное выдает ошибку.
Так что для меня запись a=1000
снаружи Table
против внутренней части не должно было иметь никакого значения, посколькубез a
, имеющего числовое значение, Table[]
ничего не может сделать.Таким образом, замена a
на число 1000 должно произойти в один момент времени, прежде чем Table[]
сможет сделать что-нибудь полезное, не так ли?
Другими словами, что Table
должно видеть,в конечном итоге {i, 1, 1000}, {j, 1, 1000}
в обоих случаях.
Итак, я думал, что это произойдет так:
- Evaluator заменяет
a
на 1000 в аргументах таблицы - Вызовы Evaluator
Table
с результатом, который теперь весь числовой. - Таблица компилируется и теперь работает быстрее.
Но то, что, кажется, происходит, это нечто другое.(из-за HoldAll
?)
- Таблица принимает свои аргументы, как есть.Поскольку он имеет HoldAll, он видит
a
, а не 1000. - Он не вызывает Compile, поскольку его аргументы не все числа.
- Теперь он генерирует таблицу с
a
предел, Evaluator оценивает a
до 1000 - Таблица теперь генерируется, все ограничения числовые, но теперь медленнее, поскольку код не компилируется.
Вопрос:выше рода что происходит?Может ли кто-нибудь объяснить шаги, которые должны были произойти, чтобы объяснить эту разницу во времени?
Кроме того, Как можно обеспечить, чтобы таблица компилировалась в обоих случаях в приведенном выше примере,если кто-то использует переменную для ограничения?Не всегда возможно жестко закодировать числа для пределов таблицы, но иногда нужно использовать переменные для них.Следует ли явно использовать команду Compile
?(Я не использую Compile
напрямую, поскольку я предполагал, что это делается автоматически при необходимости).
edit (1)
В ответе на сообщение Майка нижепри обнаружении разницы во времени при использовании вызова.
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
все еще является символом ВНУТРИ функции, мы вернулись к исходной точке, и теперь это медленно, так как не упаковано.И поскольку он не упакован, компиляция не используется.
{0.437, False}
edit (2) Спасибо всем за ответы, я думаю, что я многому их научил.
Вот резюме, просто чтобы убедиться, что у меня все в порядке.
edit (3)
Вот ссылки, которые я специально связал с советами, которые помогут сделать код Mathematica более быстрым.
- http://library.wolfram.com/howtos/faster/
- http://blog.wolfram.com/2011/12/07/10-tips-for-writing-fast-mathematica-code/
- https://stackoverflow.com/questions/4721171/performance-tuning-in-mathematica
- Использование функций Array и Table в Mathematica.Что лучше, когда