Варианты выбора
Jul. 7th, 2015 12:49 amПодходит к концу моя студенческая лицензия на Mathematica 9 и я задумался о приобретении собственной персональной лицензии на один из программных продуктов. Выбор профессиональных инструментов не особенно велик. Собственно, всего три варианта:
Maple Personal edition — $299.00
Mathematica Home Edition — $295.00
Matlab + Image Processing Toolbox + Symbolic Math Toolbox +
Partial Differential Equation Toolbox — $135 + $39 + $39 +$39 = $252
В этом отношении Matlab выглядит привлекательнее по цене, и остаётся запас для приобретения ещё одного тулбокса, правда, несколько смущает тот факт, что за ту же цену MapleSoft и Wolfram предлагают гораздо более полный набор инструментов, хотя что мне до них, если большую их часть я всё равно не буду использовать.
Поэтому следующим вопросом стал вопрос удобства использования и скорости работы.
Для начала я написал коротенький тест на скорость умножения матриц. Здесь matlab откровенно порадовал, оставив всех далеко позади.
Matlab 2015a:
Mathematica 9:
Gfortran 4.9.1:
Maple 18:
Увы, дождаться же перемножения матриц 5000х5000 мне не удалось.
Результат Maple не лезет ни в какие ворота, с ним ещё предстоит разбираться отдельно, а вот фортран откровенно огорчил, но тут, видимо, проблема не в самом фортране, а в неоптимальной реализации библиотечных процедур работы с матрицами. Очевидно, что переписав их самому, можно достичь такого же, если не лучшего результата, что и в Matlab.
Однако мне в реальной жизни не нужно перемножать такие матрицы, а если и нужно, то не больше, чем 4х4. Основные численные расчеты, с которыми я работаю, относятся к решению дифференциальных уравнений. Поэтому для следующего теста я выбрал классическую задачу двух тел, в первую очередь, потому что для неё известно точное аналитическое решение.
И вот, что получилось...
На этот раз начну с фортрана, поскольку на нем у меня давно написаны все рабочие алгоритмы. Задача решалась модифицированным методом Рунге-Кутты-Фельберга (Runge–Kutta–Fehlberg), более известного как RK45, с переменным шагом интегрирования.
Я привожу программу целиком:
Gfortran 4.9.1:
Фортран как честная числомолотилка ожидаемо занял первое место.
Результат работы приведен на следующем рисунке:

Теперь переходим к Mathematica 9
Результат приведен на следующем рисунке:

Для Maple 18 я задачу двух тел писать не стал, потому что до этого запускал на нём задачу трёх тел, и она считалась за точно такое же время, как и в Mathematica. Поэтому я предполагаю, что время расчёта в Maple должно составить около 17 секунд.
И наконец, Matlab 2015a:
Производительность матлаба оказалась, на первый взгляд, на высоте, но результат несколько обескураживает:

Очевидно, что функция ode45 в Matlab требует дополнительной настройки. Для того, чтобы получить такой же результат, который дают Mathematica и фортрановская программа, пришлось несколько модифицировать матлабовский сценарий (добаленный текст выделен полужирным):
Это примерно соответствует времени Mathematica и Maple. Но, по крайней мере, результат стал похож на правду:

Несколько неожиданным оказался тот факт, что функция численного решения дифференциальных уравнений требует в Matlab дополнительных настроек, в то время как в Mathematica и Maple всё корректно считается с первого раза с настройками по умолчанию. Очевидно, что при численном моделировании желательно иметь достаточно точное представление о том, что именно делает та или иная функция, и с этой точки зрения мне, пожалуй, больше подойдёт фортрановская программа, полную реализацию которой я вижу перед глазами. Да и время работы ее в три раза меньше, чем время работы матлабовского скрипта.
Ради очистки совести я запустил оба приведенных здесь матлабовских скрипта на Octave 4.0.0 (в скрипт для решения задачи двух тел в начало функции orbit() необходимо добавить строку: pkg load odepkg).
Результаты получились следующие:
Умножение матриц 5000x5000: 7.0 с.
Задача двух тел (1000 периодов): 327.3 с
Последний результат, к сожалению, делает Octave совершенно неприемлемым для моих задач. Если кто-нибудь знает, как ускорить odepkg в octave, буду благодарен за совет.
С другой стороны, при запуске первого варианта скрипта для задачи двух тел Octave сразу же выдал мне предупреждение:
С подставленными им значениями по умолчанию скрипт посчитался за 160 секунд и выдал, хотя и не вполне корректный, но, по крайней мере, похожий на правду результат:

Теперь я сведу все результаты в одну табличку:
Я, конечно, ещё поиграюсь с матлабом до истечения триального периода, но пока склоняюсь к тому, чтобы вообще отказаться от проприетарных систем компьютерной алгебры; для решения численных задач небесной механики продолжить использовать фортран, для работы с матрицами — Octave, а для символьной математики — Maxima.
Maple Personal edition — $299.00
Mathematica Home Edition — $295.00
Matlab + Image Processing Toolbox + Symbolic Math Toolbox +
Partial Differential Equation Toolbox — $135 + $39 + $39 +$39 = $252
В этом отношении Matlab выглядит привлекательнее по цене, и остаётся запас для приобретения ещё одного тулбокса, правда, несколько смущает тот факт, что за ту же цену MapleSoft и Wolfram предлагают гораздо более полный набор инструментов, хотя что мне до них, если большую их часть я всё равно не буду использовать.
Поэтому следующим вопросом стал вопрос удобства использования и скорости работы.
Для начала я написал коротенький тест на скорость умножения матриц. Здесь matlab откровенно порадовал, оставив всех далеко позади.
Matlab 2015a:
% File: TestSpeed.m N = 5000; tic % Запускаем "таймер" A = rand (N, N); % Создаём матрицу nxn и заполняем случайными числами AM=A'*A; % Произведение исходной и транспонированной матриц toc % Останавливаем "таймер"Время вычислений: 4.3 с.
Mathematica 9:
(* File: TestSpeed.nb *)
n = 5000;
Start = AbsoluteTime[]; (* Запускаем "таймер" *)
A = Table[RandomReal[], {i, n}, {j, n}];(* Создаём матрицу nxn
и заполняем случайными числами *)
AT = Transpose[A]; (* Транспонируем матрицу *)
AM = AT.A; (* Вычисляем произведение исходной и транспонированной матриц *)
AbsoluteTime[] - Start (* Останавливаем "таймер" *)
Время вычислений: 21.9 с.Gfortran 4.9.1:
! File: TestSpeed.f90
program main
integer, parameter :: n = 5000
real a(n,n),at(n,n),am (n,n),ss ! Три матрицы nxn и переменная "таймера"
ss = secnds(0.) ! Запускаем "таймер"
call random_number(a) ! Заполняем матрицу случайными числами
at = transpose(a) ! Транспонируем матрицу
am = matmul(at,a) ! Произведение исходной и транспонированной матриц
ss = secnds(0.) - ss ! Останавливаем "таймер"
write(6,"('Processing time', f8.3, ' sec')"), ss
end program main
Время вычислений: 82.3 с.Maple 18:
restart : with(LinearAlgebra) : start := time() : A := RandomMatrix(1000, 1000) : MatrixMatrixMultiply(Transpose(A), A) : time()-startВремя вычислений: 91.2 с.
Увы, дождаться же перемножения матриц 5000х5000 мне не удалось.
Результат Maple не лезет ни в какие ворота, с ним ещё предстоит разбираться отдельно, а вот фортран откровенно огорчил, но тут, видимо, проблема не в самом фортране, а в неоптимальной реализации библиотечных процедур работы с матрицами. Очевидно, что переписав их самому, можно достичь такого же, если не лучшего результата, что и в Matlab.
Однако мне в реальной жизни не нужно перемножать такие матрицы, а если и нужно, то не больше, чем 4х4. Основные численные расчеты, с которыми я работаю, относятся к решению дифференциальных уравнений. Поэтому для следующего теста я выбрал классическую задачу двух тел, в первую очередь, потому что для неё известно точное аналитическое решение.
И вот, что получилось...
На этот раз начну с фортрана, поскольку на нем у меня давно написаны все рабочие алгоритмы. Задача решалась модифицированным методом Рунге-Кутты-Фельберга (Runge–Kutta–Fehlberg), более известного как RK45, с переменным шагом интегрирования.
Я привожу программу целиком:
Gfortran 4.9.1:
! File: orbit.f90
Program RK45F
real*8 RK45 ! Объявление имени функции
real*8 y(8), T, Tmax, eps, M1, M2, V, dt
real*8 prec
common M1, M2, dt
M1 = 50. ! Масса первого тела
M2 = 10. ! Масса второго тела
V = 4. ! Начальная скорость первого тела
y(1) = 0. ! Начальная координата x первого тела
y(2) = 0. ! Начальная координата y первого тела
y(3) = 5. ! Начальная координата x второго тела
y(4) = 0. ! Начальная координата y второго тела
y(5) = 0. ! Начальный компонент скорости Vx первого тела
y(6) = -V*M2/M1 ! Начальный компонент скорости Vy первого тела
y(7) = 0. ! Начальный компонент скорости Vx второго тела
y(8) = V ! Начальный компонент скорости Vy второго тела
T=0. ! Начальный момент времени
Tmax = 401*1000. ! 1000 периодов обращения двойной системы
prec=0.05 ! Точность интегрирования
dt = 1. ! Начальный шаг интегрирования
open (1, file = 'orbit.dat') ! Открываем файл для записи данных
TOTAL = SECNDS(0.) ! Запускаем "таймер"
do while (T<=Tmax) ! Главный цикл
eps = RK45(y) ! Возвращаем максимальное из ускорений
dt = prec/eps ! Чем больше ускорение, тем меньше шаг
write (1,*), (y(i), i=1,4),eps ! Пишем данные в файл
T = T + dt ! Приращение времени
end do !
TOTAL = SECNDS(0.0) - TOTAL ! Останавливаем "таймер"
WRITE(6,1) Total
1 FORMAT('FORTRAN: Total time = ', F6.2, ' sec')
call system ('gnuplot orbit.plt')
! Вызов внешней программы gnuplot, которая в качестве параметра
! получает файл orbit.plt с инструкциями:
!
! set terminal windows
! set grid
! set linetype 1 lc rgb "red" lw 1 pt 0
! set linetype 2 lc rgb "blue" lw 1 pt 7
! set title "Orbits" textcolor lt 1
! plot 'orbit.dat' using 1:2 with lines lw 1 title "M1", 'orbit.dat' using 3:4 with lines lw 1 title "M2"
! pause 1000
!
! При запуске под Windows необходимо создать в каталоге командный файл
! gnuplot.cmd, в котором прописать полный путь к программе wgnuplot.exe.
! Например, у меня этот файл состоит из единственной строки:
!
! "C:\Program Files (x86)\Maxima-5.31.2\gnuplot\bin\wgnuplot.exe" %1 %2 %3
!
! При запуске под Linux командный файл создавать не надо, командный
! процессор сам найдет программу gnuplot (если она установлена), но
! из файла orbit.plt необходимо удалить первую и последнюю строки
! (set terminal windows и pause 1000).
close(1)
End program RK45F
Module Derivatives
! Система дифференциальных уравнений.
! Функция возвращает максимальное значение модуля вторых производных.
Implicit NONE
Contains
real*8 Function gr(f,y,FLAG)
! y(8) — входной массив
! f(8) — выходной массив
real*8 M1, M2, r12, f(8), y(8), dt
common M1, M2, dt
integer FLAG ! Флаг: вычислять или нет максимальный модуль ускорения
r12 = sqrt((y(1)-y(3))**2+(y(2)-y(4))**2) ! Расстояние между компонентами
r12=1.0/(r12**3) ! Оптимизация: замена четырёх делений
! одним делением и четырьмя умножениями.
f(1)=y(5) ! Компонент скорости Vx первого тела
f(2)=y(6) ! Компонент скорости Vy первого тела
f(3)=y(7) ! Компонент скорости Vx второго тела
f(4)=y(8) ! Компонент скорости Vy второго тела
f(5)=M2*(y(3)-y(1))*r12 ! Компонент ускорения ax первого тела
f(6)=M2*(y(4)-y(2))*r12 ! Компонент ускорения ay первого тела
f(7)=M1*(y(1)-y(3))*r12 ! Компонент ускорения ax второго тела
f(8)=M1*(y(2)-y(4))*r12 ! Компонент ускорения ay второго тела
if (FLAG.eq.1) then
gr = dmax1(abs(f(5)),abs(f(6)),abs(f(7)),abs(f(8)))
! Максимальный модуль компонента ускорения
else
gr=0
endif
End Function gr
End Module Derivatives
real*8 Function RK45 (y) ! Реализация модифицированного алгоритма RK45.
! Вычисление погрешности на каждом шаге не
! используется, вместо этого в основной программе
! производится автоматическое изменение шага.
use Derivatives
real*8 M1, M2, dt
common M1, M2, dt
real*8 k1(8), k2(8), k3(8), k4(8), k5(8), y(8), y1(8), f(8)
real*8 eps
real*8, parameter :: c21=1./4.
real*8, parameter :: c31=3./32., c32=9./32.
real*8, parameter :: c41=1932./2197., c42=-7200./2197., c43=7296./2197.
real*8, parameter :: c51=439./216.,c52=-8.,c53=3680./513.,c54=-845./4104.
real*8, parameter :: a1=25./216.,a3=1408./2565.,a4=2197./4104.,a5=-1./5.
integer i
eps = gr(f,y,0)
do i=1,8
k1(i) = dt*f(i)
y1(i) = y(i)+c21*k1(i)
end do
eps = gr(f,y1,0)
do i=1,8
k2(i) = dt*f(i)
y1(i) = y(i)+c31*k1(i)+c32*k2(i)
end do
eps = gr(f,y1,0)
do i=1,8
k3(i) = dt*f(i)
y1(i) = y(i)+c41*k1(i)+c42*k2(i)+c43*k3(i)
end do
eps = gr(f,y1,0)
do i=1,8
k4(i) = dt*f(i)
y1(i) = y(i)+c51*k1(i)+c52*k2(i)+c53*k3(i)+c54*k4(i)
end do
eps = gr(f,y1,1) ! Модуль ускорения вычисляется только
! при последнем обращении к функции
do i=1,8
k5(i) = dt*f(i)
y(i) = y(i)+a1*k1(i)+a3*k3(i)+a4*k4(i)+a5*k5(i)
end do
RK45 = eps
End function RK45
Время работы фортрановской программы: 7.6 с.Фортран как честная числомолотилка ожидаемо занял первое место.
Результат работы приведен на следующем рисунке:

Теперь переходим к Mathematica 9
(* File orbit.nb *)
M1 = 50; M2 = 10; V = 4.0; T = 1000; Start = AbsoluteTime[];
System = {
x1'[t]==vx1[t],
vx1'[t]==M2(x2[t]-x1[t])/((x2[t]-x1[t])^2 + (y2[t]-y1[t])^2)^(3/2),
y1'[t]==vy1[t],
vy1'[t]==M2(y2[t]-y1[t])/((x2[t]-x1[t])^2 + (y2[t]-y1[t])^2)^(3/2),
x2'[t]==vx2[t],
vx2'[t]==M1(x1[t]-x2[t])/((x1[t]-x2[t])^2 + (y1[t]-y2[t])^2)^(3/2),
y2'[t]==vy2[t],
vy2'[t]==M1(y1[t]-y2[t])/((x1[t]-x2[t])^2 + (y1[t]-y2[t])^2)^(3/2)
};
Solution = NDSolve[{System,
x1[0] == 0, y1[0] == 0,
vx1[0] == 0, vy1[0] == -V*M2/M1,
x2[0] == 5, y2[0] == 0,
vx2[0] == 0, vy2[0] == V},
{x1, vx1, y1, vy1, x2, vx2, y2, vy2},
{t, 0, 401*T}, MaxSteps -> 1000000];
ParametricPlot[{{{x1[t], y1[t]} /. Solution}, {{x2[t], y2[t]} /.Solution}},
{t, 0, 401*T}, PlotPoints -> 10000]
AbsoluteTime[] - Start
Время вычислений: 16.9 с.Результат приведен на следующем рисунке:

Для Maple 18 я задачу двух тел писать не стал, потому что до этого запускал на нём задачу трёх тел, и она считалась за точно такое же время, как и в Mathematica. Поэтому я предполагаю, что время расчёта в Maple должно составить около 17 секунд.
И наконец, Matlab 2015a:
% File: orbit.m
function orbit()
global M1;
global M2;
M1=50.0; M2=10.0; V = 4.0;
tic
[t,h]=ode45(@body2,[0,401*1000],[0,0,5,0,0,-V*M2/M1,0,V]);
x1=h(:,1); y1=h(:,2);
x2=h(:,3); y2=h(:,4);
plot(x1,y1,x2,y2);
toc
function f=body2(t,x)
global M1;
global M2;
r12=sqrt((x(1)-x(3))^2+(x(2)-x(4))^2);
r12=1.0/r12^3;
f=[x(5);x(6);x(7);x(8);...
M2*(x(3)-x(1))*r12;...
M2*(x(4)-x(2))*r12;...
M1*(x(1)-x(3))*r12;...
M1*(x(2)-x(4))*r12];
Время работы: 8.4 с.Производительность матлаба оказалась, на первый взгляд, на высоте, но результат несколько обескураживает:

Очевидно, что функция ode45 в Matlab требует дополнительной настройки. Для того, чтобы получить такой же результат, который дают Mathematica и фортрановская программа, пришлось несколько модифицировать матлабовский сценарий (добаленный текст выделен полужирным):
% File: orbit.m
function orbit()
global M1;
global M2;
M1=50.0; M2=10.0; V = 4.0;
tic
options=odeset('RelTol',0.0000001,'AbsTol',0.0000001,...
'InitialStep',1.0, 'MaxStep', 1000000);
[t,h]=ode45(@body2,[0,401*1000],[0,0,5,0,0,-V*M2/M1,0,V],options);
x1=h(:,1); y1=h(:,2);
x2=h(:,3); y2=h(:,4);
plot(x1,y1,x2,y2);
toc
function f=body2(t,x)
global M1;
global M2;
r12=sqrt((x(1)-x(3))^2+(x(2)-x(4))^2);
r12=1.0/r12^3;
f=[x(5);x(6);x(7);x(8);...
M2*(x(3)-x(1))*r12;...
M2*(x(4)-x(2))*r12;...
M1*(x(1)-x(3))*r12;...
M1*(x(2)-x(4))*r12];
Время расчета увеличилось до 22.6 сЭто примерно соответствует времени Mathematica и Maple. Но, по крайней мере, результат стал похож на правду:

Несколько неожиданным оказался тот факт, что функция численного решения дифференциальных уравнений требует в Matlab дополнительных настроек, в то время как в Mathematica и Maple всё корректно считается с первого раза с настройками по умолчанию. Очевидно, что при численном моделировании желательно иметь достаточно точное представление о том, что именно делает та или иная функция, и с этой точки зрения мне, пожалуй, больше подойдёт фортрановская программа, полную реализацию которой я вижу перед глазами. Да и время работы ее в три раза меньше, чем время работы матлабовского скрипта.
Ради очистки совести я запустил оба приведенных здесь матлабовских скрипта на Octave 4.0.0 (в скрипт для решения задачи двух тел в начало функции orbit() необходимо добавить строку: pkg load odepkg).
Результаты получились следующие:
Умножение матриц 5000x5000: 7.0 с.
Задача двух тел (1000 периодов): 327.3 с
Последний результат, к сожалению, делает Octave совершенно неприемлемым для моих задач. Если кто-нибудь знает, как ускорить odepkg в octave, буду благодарен за совет.
С другой стороны, при запуске первого варианта скрипта для задачи двух тел Octave сразу же выдал мне предупреждение:
warning: Option "RelTol" not set, new value 0.000001 is used warning: called from ode45 at line 113 column 5 orbit at line 10 column 5 warning: Option "AbsTol" not set, new value 0.000001 is used warning: Option "InitialStep" not set, new value 40100.000000 is used warning: Option "MaxStep" not set, new value 40100.000000 is used
С подставленными им значениями по умолчанию скрипт посчитался за 160 секунд и выдал, хотя и не вполне корректный, но, по крайней мере, похожий на правду результат:

Теперь я сведу все результаты в одну табличку:
Умножение матриц Задача двух тел
GFortran 82,3 с 7,6 с
Maple N/A ~17,0 c
Mathematica 21,9 с 16,9 с
Matlab 4,3 с 22,6 с
Octave 7,0 с 327,3 с
Я, конечно, ещё поиграюсь с матлабом до истечения триального периода, но пока склоняюсь к тому, чтобы вообще отказаться от проприетарных систем компьютерной алгебры; для решения численных задач небесной механики продолжить использовать фортран, для работы с матрицами — Octave, а для символьной математики — Maxima.
(no subject)
Date: 2015-07-07 05:15 pm (UTC)Из бесплатных программ могу рекомендовать numpy и scipy в Python. У меня кстати есть тест с перемножением матриц Matrix Multiplication на matrixprogramming com.
(no subject)
Date: 2015-07-07 06:01 pm (UTC)(no subject)
Date: 2015-07-07 08:05 pm (UTC)Однако вопрос денег как всегда не простой. Сложно сказать.
(no subject)
Date: 2015-07-20 09:58 am (UTC)По первому тесту, с матрицами, ВЕРОЯТНО, вмешивается нежелание Фортрана использовать SSE, и для всех вариантов, кроме matlaba, работа в один поток.
Для проверки, ниже текст первого теста, переписанный на C++ с использованием Eigen3, хорошо работающей с матрицами и векторизацией.
Используемые компиляторы:
C++ gcc version 5.1.1 20150618 (ALT Linux 5.1.1-alt2))
gfortran (gcc version 4.9.2 20150212 (ALT Linux 4.9.2-alt4)):
опции комиляции: -Ofast -mtune=native
на выполнение запускались командой time ./a.out
Результат (однопоточный вариант):
C++ : 18 секунд (стоит принять, 17.5 сек, учитывая время "печати")
F90 : 34.5 секунды
Надо будет остальные тесты тоже прогнать
#include
По первому тесту, с матрицами, ВЕРОЯТНО, вмешивается нежелание Фортрана использовать SSE, и для всех вариантов, кроме matlaba, работа в один поток.
Для проверки, ниже текст первого теста, переписанный на C++ с использованием Eigen3, хорошо работающей с матрицами и векторизацией.
Используемые компиляторы:
C++ gcc version 5.1.1 20150618 (ALT Linux 5.1.1-alt2))
gfortran (gcc version 4.9.2 20150212 (ALT Linux 4.9.2-alt4)):
опции комиляции: -Ofast -mtune=native
на выполнение запускались командой time ./a.out
Результат (однопоточный вариант):
C++ : 18 секунд (стоит принять, 17.5 сек, учитывая время "печати")
F90 : 34.5 секунды
Надо будет остальные тесты тоже прогнать
#include <eigen3/Eigen/Dense> // интерфейс к удобным векторам -- матрицам
#include <cmath> // интерфейс к общим мат функциям, определённым Стандартом языка
#include <iostream>
using namespace Eigen;
int main(int argc, char **argv) {
// как и в оригинале, память выделяею динамически
MatrixXd a = MatrixXd::Random(5000,5000); // выделена матрица (5000,5000) и заполнена случайными числами
MatrixXd at, am; // как в оригинале, аналогично --- матрицы типа double динамически изменяемого размера
at = a.transpose(); // [a] транспонируется, результат преобразуется и сохраняется в матрице at
am = a * at; // произведение исходной и транспонированной матриц
// чтобы компилятор не выбросил код при оптимизации, напечатаем получившийся элемент (3,3)
// на еденичную "печать" может уйти до 300 мсек, просто помним об этом
std::cout << "am(3,3) = " << am(3,3) << std::endl;
return 0;
}
(no subject)
Date: 2015-07-20 10:12 am (UTC)потоков время
1 18
2 20
3 22
4 22
включение многопоточности сводилось к добавлению опции компилятора -fopenmp
и дописыванию к началу программы вызовов:
// включаем openMP:
Eigen::initParallel();
setNbThreads(2);
(no subject)
Date: 2015-07-20 10:17 am (UTC)SUBROUTINE MMUL(A,B,C,N) INTEGER N REAL*8 A(N,N), B(N,N), C(N,N), TEMP DO J=1,N ! Порядок циклов изменен с учетом того, DO K=1,N ! что фортран хранит матрицы по столбцам, TEMP = B(K,J) ! так чтобы уменьшить число промахов кэша. DO I=1,N ! С этой же целью введена переменная TEMP. C(I,J) = C(I,J)+A(I,K)*TEMP ENDDO ENDDO ENDDO END SUBROUTINEДа, в последних g++ и gfortran появился замечательный ключик оптимизации -O5
(no subject)
Date: 2015-07-20 10:27 am (UTC)а ключики с высокой стеепнью оптимизации.. ой, опасное это дело! :)
(no subject)
Date: 2015-07-20 10:30 am (UTC)(no subject)
Date: 2015-07-20 10:41 am (UTC)void mmul (double **a, double **b, double **c, int n) { register double temp; register int i, j, k; for (i=0;i < n;i++) for (j=0;j < n;j++) c[i][j] = 0; for(i=0;i < n;i++) for(k=0;k < n;k++) { temp = a[i][k]; for(j=0;j < n;j++) c[i][j] += temp*b[k][j]; } }Инициализация матрицы c нулями имеет квадратичную сложность и на размерах больше 1000 ее вкладом во время вычислений, которое растет как куб n, можно пренебречь.
(no subject)
Date: 2015-07-20 11:10 am (UTC)Код выше --- это не C++, это классический Си :) Вероятно, лишнее время сжирается на обнулении матрицы (зачем? и почему не библиотечный вызов memset, который всяко будет быстрее возни с индексами?)
Ну и словечком register я бы не злоупотреблял, на x86 регистров мало и, хотя словечко и рекомендательное, пусть компилятор сам решает.
Я бы такие вещи руками не писал, есть библиотеки, типа того-же Eigen3, про выравниванием и векторизацию, оно знает, хранить матрицы по столбцам, умеет.
У меня сомнения в Фортране связаны с тем, что последние лет 10, если не больше, разработка Фортран -- компиляторов, шла по остаточному принципу (по крайней мере, в команде gcc. Посравнивать интеловский фортран да, может быть интересно)
(no subject)
Date: 2015-07-20 11:14 am (UTC)(no subject)
Date: 2015-07-20 11:42 am (UTC)Обнуление матрицы при тех n, для которых я тестирую, время не занимает, потому что миллиард в тысячу раз больше миллиона.
А библиотечные вызовы я не использую, чтобы иметь перед глазами по возможности всю реализацию.
(no subject)
Date: 2015-07-22 04:36 am (UTC)зачем?
> потому что миллиард в тысячу раз больше миллиона.
:)
>А библиотечные вызовы я не использую, чтобы иметь перед глазами по возможности всю реализацию.
позиция кажется немного странной, соревноваться в написании общего кода с командой профессионалов (или, как минимум, тратящей тысячи человеко -- часов и постоянно тестирующей код командой), не кажется продуктивным.
(no subject)
Date: 2015-07-20 12:12 pm (UTC)(no subject)
Date: 2015-07-22 04:38 am (UTC)до стабилизации g77 (период вблизи 1993-го), существовал транслятор for2c. Вероятно, это отголоски с тех времён.
(no subject)
Date: 2015-07-22 12:05 pm (UTC)Вот что получилось:
Можно предположить, что Octave использует алгоритм Штрассена, оптимизируя дополнительно вычисления. А вот Matlab, судя по показателю, использует более хитрый алгоритм.
(no subject)
Date: 2015-07-23 06:50 am (UTC)Пока нет уверенности в использованной точности, пытаться угадать алгоритм вряд-ли возможно
(no subject)
Date: 2015-07-23 09:04 am (UTC)Для очистки совести прогнал умножение матриц 3000х3000 на разных компиляторах. Чтобы сравнить результаты, я инициализировал матрицы во всех программах одинаково: A(I,J)=I*J (нумерация индексов везде начинается с единицы) и вывел элемент A(1234,2345):
Все исходники те же, что уже приводились ранее в этом посте. Время расчета заметно уменьшается при переходе от double к float только в микрософтовском компиляторе. При переходе к 80-разрядным числам время у гну-компиляторов увеличивается в 4 раза.
Получается, что Matlab не только считает в double, но ещё и выдаёт точный результат, который в других компиляторах получается только при использовании типа long double или REAL*10.
P.S. Wolfram Mathematica 9 считает 17 секунд и выдаёт точный целочисленный результат: 26056593231865000.
P.P.S. Но если "Математике" задать исходную матрицу как A = Table[i j*1.0, {i, n}, {j, n}], то время расчёта сокращается до 2,25 секунды, результат же по-прежнему остаётся верным с нужной точностью: 2.605659323186500∙1016.
Оно и понятно, использование стандартного типа быстрее, чем работа с целыми числами, не умещающимися в стандартные регистры.
(no subject)
Date: 2015-07-23 11:30 am (UTC)(no subject)
Date: 2015-07-20 10:24 am (UTC)Scilab выполнил 1-ый Матлабовский скрипт за 1.5 секунды. Но. как только я поставил печать того-же элемента AM(3,3), время выполнения увеличилось до 7 секунд.
Нет ли такого-же эффекта в Матлабе (ленивые вычисления)? Под рукой нет матлаба, проверить не могу
(no subject)
Date: 2015-07-20 10:28 am (UTC)(no subject)
Date: 2015-07-20 12:40 pm (UTC)В Matlab вывод значения одного из элементов время расчета не увеличивает.
Что с AM(3,3), что без — 4 секунды.
(Intel Core i5-3230M @ 2.60 ГГц, RAM 6 Гбайт,
Matlab 2015a).
(no subject)
Date: 2015-07-22 04:46 am (UTC)Хотя вот что, заменяю в вышеприведённом исходнике MatrixXd на MatrixXf (снижаю точность с double до float), и время вычисления в однопоточном варианте становится 4.9 секунды.
С какой точностью считает Matlab (и какая точность приемлима)? Я считал, что точность float, это табу и фуфуфу, но я нифига не расчётчик :)
Кстати, и картинка в вышеприведённом тесте намекает, похоже :)
(no subject)
Date: 2015-07-22 11:54 am (UTC)(no subject)
Date: 2015-07-23 06:37 am (UTC)Вот прямой эксперимент: C++Eigen3, компиляция gcc(C++) version 5.1.1 20150618 (ALT Linux 5.1.1-alt2), прогон на системе с процессором Intel Core i5-4590.
точность float даёт время выполнения теста в [b]точности такое-же, как у Матлаба[/b].
Более того, смотрим на графики Вашего теста, которые попросту показывают катастрофическое снижение точности расчёта в Матлаб. По-моему, Оккам требует предположить, что Matlab не столько использует хитрый алгоритм, сколько (по умолчанию?) работает с низкой точностью.
Совпадение битности регистра сопроцессора и типа данных, это конечно, хорошо, но всё ли однозначно? Есть же требование стандарта, как его, IEE75-чего-то-там, нужно выполнять преоборазования. Пробую!
Точность double (это 64 бита) приводит к увеличению времени (в однопоточном варианте) в 8 раз (до примерно, 32 секунд)
Переход в тесте к точности long double (как раз те самые 80 бит /осторожно, Microsoft не поддерживает этот тип, эмулируя его простым double/ ), оказался катастрофичен.
При компиляции с опциями -Ofast -mtune=native время выполнения теста... я не дождался! Прошло больше 30 (!!!!) минут и конца процессу не видать.
Пробую уточнить тип процессора
$grep -m1 -A3 "vendor_id" /proc/cpuinfo
vendor_id : GenuineIntel
cpu family : 6
model : 60
model name : Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz
на странице https://wiki.gentoo.org/wiki/Safe_CFLAGS#Core_i3.2Fi5.2Fi7_.26_Xeon_E3.2FE5.2FE7_.2AV2
ищу опции компиляции для данного семейства:
-march=core-avx2
При компиляции с опциями -Ofast -march=core-avx2 -ftree-vectorize -ffast-math Всё стало значительно лучше!
время выполнения теста (long double) составило 130 секунд.
Интересно... пересобираю тест для float и double с теми-же опциями компиляции, результат:
float --> секунд = 5
double --> секунд = 16
long double --> секунд = 130
Видимо, дело более сложное, чем просто соответствие битности числа и регистра сопроцессора. Ну, или компилятор откровенно лажает.
А как вариант с OpenMP?
Пересобираю, добавив опцию -fopenmp и раскомментировав инициализацию режима в Eigen3, указание задействовать 3 нити (мониторинг показывает, что действительно, загружены все 3 ядра). Результат:
float --> секунд = 7
double --> секунд = 19
long double --> секунд = 145
Н-да...
Смотрим Фортрановский вариант.
Компиляция с опциями -Ofast -mtune=native
время выполнения = 35 секунд.
-Ofast -march=core-avx2 -ftree-vectorize -ffast-math
время выполнения ТАКОЕ ЖЕ = 35 секунд! Забавно...
Ставлю в тексте фортрановского теста вместо real --> real*10,
компилирую с опциями -Ofast -march=core-avx2 -ftree-vectorize -ffast-math
время выполнения = 295 секунд.
Выводы:
1. время выполнения тем меньше, чем [b]меньше точность[/b]. с разрядностью регистров сопроцессора это дело не коррелирует :)
2. время выполнения теста существенно зависит от опций компилятора gcc (указания архитектуры и допустимых оптимизаций)
3. оптимизации размещения матриц, автоматически выполняемые шаблонным кодом библиотеки Eigen3, приводят к [b] ДВУХКРАТНОМУ выигрышу C++Eigen3 по сравнению с Фортраном (gfortran) [/b]. С другой стороны, C++Eigen3 оказывается существенно более чувствителен к опциямкомпиляции, чем Фортран: на производительность кода, порождаемого gfortran, опции компиляции почти не влияют.
(no subject)
Date: 2015-07-20 09:22 pm (UTC)(no subject)
Date: 2015-07-22 04:48 am (UTC)(no subject)
Date: 2015-07-22 05:05 am (UTC)N = 5000;
A = rand (N, N);
tic
AM=A'*A;
AM(3,3)
toc
(no subject)
Date: 2015-07-22 06:03 am (UTC)Кстати, радикально ускорить выполнение этого теста и получить аналогичный матлабовскому результат, получилось только снижением точности до float. Распараллеливание в этом тесте не даёт ощутимого выигрыша. Не проверяли?
(no subject)
Date: 2015-07-22 07:06 am (UTC)(no subject)
Date: 2015-07-22 07:20 am (UTC)(no subject)
Date: 2015-07-22 08:11 am (UTC)(no subject)
Date: 2015-07-22 07:21 am (UTC)Your client does not have permission to get URL /sEpD7fwziQWpYSJNPQt-x4k-2UkHy9Er0OrFErI_rET5Q7cs4--YwuYoBNq7pl3dU0j6ZIK-gmcmaNE=w1886-h745 from this server.
:)
(no subject)
Date: 2015-07-22 07:26 am (UTC)Ушёл пытать гуглдрайв на предмет, как по новым правилам корректно картинки оттуда в блоги вставлять.