Я читал полезный пост в блоге 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}
в обоих случаях.
Итак, как я думал, это произойдет, это:
- Оценщик заменяет
a
на 1000 в аргументах таблицы - Оценщик вызывает
Table
с результатом, который теперь является все числовым. - Компилирует таблицы и работает быстрее.
Но, похоже, что-то другое. (из-за HoldAll
?)
- Таблица принимает свои аргументы, как есть. Поскольку он имеет HoldAll, он видит
a
, а не 1000. - Он не вызывает компиляцию, поскольку ее аргументы не являются всеми числами.
- Теперь он генерирует таблицу с пределом
a
, Оценщик оцениваетa
до 1000 - Теперь создается таблица, все лимиты являются числовыми, но медленнее, поскольку код не скомпилирован.
Вопрос: Есть ли что-то вроде того, что происходит? Может кто-нибудь объяснить шаги, которые могли бы произойти, чтобы объяснить эту разницу в сроках?
Кроме того, , как бы обеспечить, чтобы таблица была скомпилирована в обоих случаях в приведенном выше примере, даже если для ограничения используется переменная? Не всегда можно жестко кодировать числа для пределов таблицы, но для этого нужно использовать переменные для них. Следует ли явно использовать команду Compile
? (Я не использую Compile
напрямую, так как я предполагал, что он выполняется автоматически, когда это необходимо).
изменить (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[[email protected]]]
]
теперь мы видим ожидаемый результат, так как теперь a
по-прежнему является символом ВНУТРИ функции, мы возвращаемся к квадрату, и теперь он медленный, так как не упакован. И поскольку он не упакован, компиляция не используется.
{0.437, False}
изменить (2) Спасибо всем за ответы, я думаю, что я научился выделять их.
Вот резюме, просто чтобы убедиться, что я все понял.
изменить (3)
Вот ссылки, которые я специально связал с подсказками для использования, чтобы ускорить выполнение кода Mathematica.