Как я могу сказать Compile
, что один из ее аргументов - это не произвольная матрица n * m, а специальная матрица n * 2 или n * 3, поэтому он может выполнять эту оптимизацию автоматически, а не с использованием общего сложения векторов функция для добавления крошечных векторов длины-2 или длины-3?
Вы пытаетесь выручить Титаник с помощью ложки!
На моей машине с Mathematica 7.0.1 ваш первый пример занимает 4 с, ваш второй - 2 с, а Total
- 0,1 с. Таким образом, у вас есть объективно крайне неэффективное решение (Total
в 40 раз быстрее!), И вы правильно определили и исправили один из наименее важных факторов, влияющих на низкую производительность (делая его в 2 раза быстрее).
Низкая производительность Mathematica связана с тем, как оценивается код Mathematica. С точки зрения языка программирования термин переписчик терминов Mathematica может быть мощным решением для компьютерной алгебры, но он безумно неэффективен для оценки программ общего назначения. Следовательно, такие оптимизации, как специализация для двухмерных векторов, в Mathematica не окупятся, как в компилируемых языках.
Я быстро пошел по оптимизации в Mathematica, написав код для добавления двух векторных элементов за раз:
fun3 = Compile[{{vec, _Real, 2}},
Module[{x = vec[[1]][[1]], y = vec[[1]][[2]]},
Do[x += vec[[i]][[1]]; y += vec[[i]][[2]], {i, 2, Length[vec]}];
{x, y}]]
Это на самом деле еще медленнее - 5,4 с.
Но это Mathematica в худшем. Mathematica полезна только когда:
Вы действительно не заботитесь о производительности, или
Обширная стандартная библиотека Mathematica уже включает функции (например, Total
в данном конкретном случае), которые позволяют эффективно решать вашу проблему либо с помощью одного вызова, либо путем написания сценария для нескольких вызовов.
Как сказал Рубенко, Mathematica предоставляет встроенную функцию для решения этой проблемы за один вызов (Total
), но она бесполезна в общем случае, о котором вы спрашиваете. Объективно, лучшее решение состоит в том, чтобы избежать основной неэффективности Mathematica, перенося вашу программу на язык, который оценивается более эффективно.
Например, наиболее наивное возможное решение в F # (скомпилированный язык, который я должен передать, но подойдет почти любой другой), - это использовать 2D-массив:
let xs =
let rand = System.Random()
Array2D.init 1000000 2 (fun _ _ -> rand.NextDouble())
let sum (xs: float [,]) =
let total = Array.zeroCreate (xs.GetLength 1)
for i=0 to xs.GetLength 0 - 1 do
for j=0 to xs.GetLength 1 - 1 do
total.[j] <- total.[j] + xs.[i,j]
total
for i=1 to 10 do
sum xs |> ignore
Это сразу в 8 раз быстрее, чем ваше самое быстрое решение! Но подождите, вы можете добиться большего, используя систему статических типов через собственный 2D-векторный тип:
[<Struct>]
type Vec =
val x : float
val y : float
new(x, y) = {x=x; y=y}
static member (+) (u: Vec, v: Vec) =
Vec(u.x+v.x, u.y+v.y)
let xs =
let rand = System.Random()
Array.init 1000000 (fun _ -> Vec(rand.NextDouble(), rand.NextDouble()))
let sum (xs: Vec []) =
let mutable u = Vec(0.0, 0.0)
for i=1 to xs.Length-1 do
u <- u + xs.[i]
u
Это решение занимает всего 0,057 с, что в 70 раз быстрее, чем у вашего оригинала, и существенно быстрее, чем любое решение на базе Mathematica, опубликованное здесь до сих пор! Язык, скомпилированный для эффективного кода SSE, вероятно, будет еще лучше.
Вы можете подумать, что 70x - это необычный особый случай, но я видел много примеров, когда перенос кода Mathematica на другие языки давал огромные ускорения: