разница в скорости в создании таблицы - PullRequest
14 голосов
/ 03 января 2012

Я читал полезный пост в блоге 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} в обоих случаях.

Итак, я думал, что это произойдет так:

  1. Evaluator заменяет a на 1000 в аргументах таблицы
  2. Вызовы Evaluator Tableс результатом, который теперь весь числовой.
  3. Таблица компилируется и теперь работает быстрее.

Но то, что, кажется, происходит, это нечто другое.(из-за HoldAll?)

  1. Таблица принимает свои аргументы, как есть.Поскольку он имеет HoldAll, он видит a, а не 1000.
  2. Он не вызывает Compile, поскольку его аргументы не все числа.
  3. Теперь он генерирует таблицу с aпредел, Evaluator оценивает a до 1000
  4. Таблица теперь генерируется, все ограничения числовые, но теперь медленнее, поскольку код не компилируется.

Вопрос:выше рода что происходит?Может ли кто-нибудь объяснить шаги, которые должны были произойти, чтобы объяснить эту разницу во времени?

Кроме того, Как можно обеспечить, чтобы таблица компилировалась в обоих случаях в приведенном выше примере,если кто-то использует переменную для ограничения?Не всегда возможно жестко закодировать числа для пределов таблицы, но иногда нужно использовать переменные для них.Следует ли явно использовать команду 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) Спасибо всем за ответы, я думаю, что я многому их научил.

Вот резюме, просто чтобы убедиться, что у меня все в порядке.

enter image description here

edit (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. Использование функций Array и Table в Mathematica.Что лучше, когда

Ответы [ 3 ]

16 голосов
/ 03 января 2012

Так вот что я думаю, что происходит.Причина, по которой вы видите замедление между числовым и символическим пределом на Table, заключается в том, что вы делаете двойной индекс.Каждая вложенная таблица (например, проходящая по всем индексам j для фиксированного индекса i) составляется отдельно, и, когда ограничение является символическим, существует дополнительный шаг для определения этого ограничения перед построением каждой вложенной таблицы.Вы можете убедиться в этом, изучив, например,

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

Дэвид дает хороший пример того, почему вы хотите выполнить эту проверку для каждого подсписка.Относительно того, почему 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}
3 голосов
/ 04 января 2012

Ключевыми вещами для мониторинга, как уже упоминалось, являются упаковка и длина списка. Я на самом деле не вижу различий, о которых сообщает Тимо:

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}]
2 голосов
/ 09 января 2012

Это немного OT, но для скорости вы можете не использовать постатейную обработку, которая подразумевается при использовании Table. Скорее используйте Outer. Вот что я вижу в своей системе:

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

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

Совершенно существенная разница.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...