Просто чтобы сообщить удивительные тесты производительности обоих методов (@@@
, @@ # & /@
):
T = RandomReal[{1,100}, {1000000, 2}];
H[F_Symbol, T_List] :=
First@AbsoluteTiming[F @@@ T;]/First@AbsoluteTiming[F @@ # & /@ T;]
Table[{ToString[F], H[F, T]}, {F, {Plus, Subtract, Times, Divide, Power, Log}}]
Out[3]= {{"Plus", 4.174757},
{"Subtract", 0.2596154},
{"Times", 3.928230},
{"Divide", 0.2674164},
{"Power", 0.3148629},
{"Log", 0.2986936}}
Эти результаты не случайны, а примерно пропорциональны для очень разных размеров данных.
@@@
примерно в 3-4 раза быстрее для Subtract
, Divide
, Power
, Log
, в то время как @@ # & /@
в 4 раза быстрее для Plus
и Times
, что вызывает другие вопросы что (как можно поверить) может быть немного
уточнена следующая оценка:
Attributes@{Plus, Subtract, Times, Divide, Power, Log}
Только Plus
и Times
имеют атрибуты Flat
и Orderless
, в то время как среди остальных только Power
(который кажется там наиболее эффективным) также имеет атрибут OneIdentity
.
Редактировать
Надежное объяснение наблюдаемому повышению производительности (благодаря замечаниям Леонида Шифрина) должно идти другим путем.
По умолчанию MapCompileLength -> 100
, так как мы можем проверить оценку SystemOptions["CompileOptions"]
.
Для сброса автокомпиляции карты мы можем оценить:
SetSystemOptions["CompileOptions" -> "MapCompileLength" -> Infinity]
Теперь мы можем проверить относительную производительность обоих методов, еще раз оценив нашу H
- функцию тестирования производительности для связанных символов и списка:
Table[{ToString[F], H[F, T]}, {F, {Plus, Subtract, Times, Divide, Power, Log}}]
Out[15]= {{"Plus", 0.2898246},
{"Subtract", 0.2979452},
{"Times", 0.2721893},
{"Divide", 0.3078512},
{"Power", 0.3321622},
{"Log", 0.3258972}}
Имея эти результаты, мы можем заключить, что в целом подход Йоды (@@@
) наиболее эффективен, в то время как предложенный Андреем лучше в случае Plus
и Times
из-за автоматической компиляции Map
, позволяющей лучшая производительность (@@ # & /@
).