#matlab #matrix #indexing #interpreter
#matlab #матрица #индексирование #интерпретатор
Вопрос:
Я пытаюсь ускорить код MATLAB, которому необходимо получить доступ к некоторым членам a(i,j)
большой матрицы a
в цикле for. Есть части, в которых один термин может потребоваться в пяти или более различных вычислениях. В этих случаях код присваивает термин a(i,j)
другой переменной k
.
Я думал, что это приводит к ненужному присваиванию (в контексте пяти вычислений), но, к моему удивлению, все наоборот. На самом деле, это назначение действительно ускоряет выполнение кода. Доступ к пятикратному члену большой матрицы происходит медленнее, чем передача его в скалярную переменную и обращение к этой скалярной переменной пять раз.
Эти результаты могут быть воспроизведены в простой тестовой функции:
r = 5e6;
i = 50;
j = 50000;
a = zeros(i,j);
%
tic
for ii = 1:r
b = a(i,j) a(i,j) a(i,j) a(i,j) a(i,j);
end
toc
%
tic
for ii = 1:r
k = a(i,j);
b = k k k k k;
end
toc
Первый код занимает в 3,5 раза больше времени, чем второй.
Должен ли MATLAB быть настолько медленным для доступа к данным из матрицы размером ~ 20 Мб?
РЕДАКТИРОВАТЬ 1:
Очевидно, что после ответа Криса Луэнго возникла проблема с установкой MATLAB, с которой я работаю (R2019a). Предыдущий результат был получен с помощью M-файла.
Следующий код выдает выходные данные. Кажется, что компиляции вообще нет.
r = 5e6;
i = 50;
j = 50000;
a = zeros(i,j);
aux_rgb = lines(2);
figure('Color','White','Name','Code with drawnow'); hold on;
legend('location','bestoutside'); ylim([0,1.05]);
xlabel('number of terms in summation');
ylabel('relative time spent');
h1 = animatedline(NaN,NaN,'LineWidth',2.5,'Color',aux_rgb(1,:),'DisplayName','a(i,j)');
h2 = animatedline(NaN,NaN,'LineWidth',2.5,'Color',aux_rgb(2,:),'DisplayName','k');
%%
n = 1;
t1 = tic;
for ii = 1:r
b = a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);
t2 = tic;
for ii = 1:r
k = a(i,j);
b = k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);
drawnow
%%
n = 2;
t1 = tic;
for ii = 1:r
b = a(i,j) a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);
t2 = tic;
for ii = 1:r
k = a(i,j);
b = k k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);
drawnow
%%
n = 3;
t1 = tic;
for ii = 1:r
b = a(i,j) a(i,j) a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);
t2 = tic;
for ii = 1:r
k = a(i,j);
b = k k k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);
drawnow
%%
n = 4;
t1 = tic;
for ii = 1:r
b = a(i,j) a(i,j) a(i,j) a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);
t2 = tic;
for ii = 1:r
k = a(i,j);
b = k k k k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);
drawnow
%%
n = 5;
t1 = tic;
for ii = 1:r
b = a(i,j) a(i,j) a(i,j) a(i,j) a(i,j);
end
t1 = toc(t1);
addpoints(h1,n,t1/t1);
t2 = tic;
for ii = 1:r
k = a(i,j);
b = k k k k k;
end
t2 = toc(t2);
addpoints(h2,n,t2/t1);
drawnow
РЕДАКТИРОВАТЬ 2:
Снова следуя ответу Криса Луэнго, это результат, полученный с помощью функции M-file (а не скрипта M-file). Теперь компиляция выполняет свою работу.
Ответ №1:
Существует разница в выполнении кода путем копирования-вставки в командную строку или внутри функции. Я вижу:
Elapsed time is 0.062195 seconds.
Elapsed time is 0.034381 seconds.
при копировании-вставке в командной строке и
Elapsed time is 0.024922 seconds.
Elapsed time is 0.025392 seconds.
если я создам M-файл функции с тем же кодом в нем и запущу функцию (в M-файле функции есть строка, начинающаяся с function
ключевого слова в качестве первой строки без комментариев). В этом случае два цикла одинаково быстры (первый может быть немного быстрее, но разница невелика).
Вы также заметите, что оба цикла выполняются быстрее, чем при копировании-вставке в командной строке. При запуске функции функция компилируется, а затем выполняется (это называется «компиляцией точно в срок» или JIT). Этот компилятор способен оптимизировать несколько операций индексирования, эффективно индексируя только один раз.
В командной строке компиляция не происходит, и поэтому местоположение индекса вычисляется пять раз, а значение извлекается пять раз.
Предполагается, что M-файлы сценариев (M-файлы, которые не начинаются с function
ключевого слова) будут скомпилированы JIT, но, похоже, это происходит не всегда, по крайней мере, не с таким же количеством оптимизаций.
Комментарии:
1. Я согласен с ответом Криса здесь. Я бы также упомянул, что, по моему мнению, причина , по которой это работает, заключается в том, что когда вы работаете
a(i,j)
в Matlab, вы не просто ссылаетесь на примитивный элемент внутри массива (как это было бы в C или Java), но вы фактически создаете новый mxArray размером 1 на 1, содержащий это значение, которое имеет накладные расходы. Таким образом, ожидается, что выполнениеk = a(i,j);
и повторноеk
использование должны быть быстрее, чем повторныеa(i,j)
ссылки при обычных обстоятельствах. Только JIT может оптимизировать это.