Является ли индексирование векторов в MATLAB неэффективным?

Фон

Мой вопрос мотивирован простыми наблюдениями, которые несколько подрывают убеждения / предположения, которые часто придерживаются / делают опытные пользователи MATLAB:

MATLAB очень хорошо оптимизирован, когда дело доходит до встроенных функций и основных функций языка, таких как индексирование векторов и матриц.Циклы в MATLAB являются медленными (несмотря на JIT) и, как правило, их следует избегать, если алгоритм может быть выражен в "векторизованном" виде.

Суть в том, что базовая функциональность MATLAB эффективна, и попытаться превзойти ее, используя код MATLAB, сложно, если не невозможно.

Исследование эффективности векторной индексации

Приведенные ниже примеры кодов настолько же фундаментальны, насколько это возможно: я присваиваю скалярное значение всем элементам вектора. Сначала я выделяю пустой векторx:

tic; x = zeros(1e8,1); toc
Elapsed time is 0.260525 seconds.

имеющийx Я хотел бы установить для всех его записей одно и то же значение. На практике вы бы сделали это по-другому, например,x = value*ones(1e8,1), но дело здесь в том, чтобы исследовать производительность векторной индексации. Самый простой способ - написать:

tic; x(:) = 1; toc
Elapsed time is 0.094316 seconds.

Давайте назовем это методом 1 (из значения, присвоенногоx). Кажется, это очень быстро (по крайней мере, быстрее, чем выделение памяти). Поскольку единственное, что я здесь делаю, это работаю с памятью, я могу оценить эффективность этого кода, рассчитав полученныйэффективная пропускная способность памяти и сравнивая его саппаратная пропускная способность памяти моего компьютера:

eff_bandwidth = numel(x) * 8 bytes per double * 2 / time

Выше я умножаю на2 поскольку, если не используется потоковая передача SSE, установка значений в памяти требует, чтобы вектор считывался и записывался в память. В приведенном выше примере:

eff_bandwidth(1) = 1e8*8*2/0.094316 = 17 Gb/s

STREAM-ориентированная пропускная способность памяти моего компьютера около 17,9 Гбит / с, так что действительно - MATLAB в этом случае обеспечивает почти максимальную производительность! Все идет нормально.

Метод 1 подходит, если вы хотите установитьвсе векторные элементы к некоторому значению. Но если вы хотите получить доступ к элементам каждыйstep записи, вам необходимо заменить: например,1:step:end, Ниже приведено прямое сравнение скорости с методом 1:

tic; x(1:end) = 2; toc
Elapsed time is 0.496476 seconds.

Хотя вы и не ожидаете, что он будет работать по-другому, метод 2, безусловно, является большой проблемой: замедление фактора 5 без всякой причины. Я подозреваю, что в этом случае MATLAB явно выделяет индексный вектор (1:end). Это несколько подтверждается использованием явного размера вектора вместоend:

tic; x(1:1e8) = 3; toc
Elapsed time is 0.482083 seconds.

Методы 2 и 3 работают одинаково плохо.

Другая возможность состоит в том, чтобы явно создать индексный векторid и использовать его для индексацииx, Это дает вам самые гибкие возможности индексирования. В нашем случае:

tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc
Elapsed time is 1.208419 seconds.

Теперь это действительно что-то - замедление в 12 раз по сравнению со способом 1! Я понимаю, что он должен работать хуже, чем метод 1 из-за дополнительной памяти, используемой дляid, но почему это намного хуже, чем методы 2 и 3?

Давайте попробуем попробовать петли - как бы безнадежно это ни звучало.

tic;
for i=1:numel(x)
    x(i) = 5;
end
toc
Elapsed time is 0.788944 seconds.

Большой сюрприз - петля бьетvectorized метод 4, но все же медленнее, чем методы 1, 2 и 3. Оказывается, в этом конкретном случае вы можете сделать это лучше:

tic;
for i=1:1e8
    x(i) = 6;
end
toc
Elapsed time is 0.321246 seconds.

И это, пожалуй, самый странный результат этого исследования -написанный на MATLAB цикл значительно превосходит собственную векторную индексацию, Это, конечно, не должно быть так. Обратите внимание, что JIT-петля все еще в 3 раза медленнее, чем теоретический пик, почти полученный методом 1. Таким образом, все еще есть много возможностей для улучшения. Просто удивительно (более сильное слово было бы более подходящим), чем обычная «векторизованная» индексация (1:end) еще медленнее.

Вопросов

простая индексация в MATLABочень неэффективно (методы 2, 3 и 4 медленнее, чем метод 1), или я что-то упустил?почему метод 4(намного) медленнее чем методы 2 и 3?почему с помощью1e8 вместоnumel(x) как цикл связан с ускорением кода в 2 раза?

редактировать После прочтения комментария Джонаса, вот еще один способ сделать это, используя логические индексы:

tic;
id = logical(ones(1, 1e8));
x(id) = 7;
toc
Elapsed time is 0.613363 seconds.

Гораздо лучше, чем метод 4.

Для удобства:

function test

tic; x = zeros(1,1e8); toc

tic; x(:) = 1; toc
tic; x(1:end) = 2; toc
tic; x(1:1e8) = 3; toc

tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc

tic;
for i=1:numel(x)
    x(i) = 5;
end
toc

tic;
for i=1:1e8
    x(i) = 6;
end
toc

end

Ответы на вопрос(2)

Ваш ответ на вопрос