Який найефективніший спосіб записати петлі "для" в Matlab?


12

Я читав, що якщо, наприклад, у мене є подвійний forцикл, який працює над індексами матриці, то введення індексу запущеного стовпця у зовнішній цикл є більш ефективним. Наприклад:

a=zeros(1000);
for j=1:1000
 for i=1:1000
  a(i,j)=1;
 end
end

Який найефективніший спосіб кодування, якщо у мене є три або більше forциклів?

Наприклад:

a=zeros(100,100,100);
for j=1:100
 for i=1:100
  for k=1:100
   a(i,j,k)=1;
  end
 end
end

4
Forпетлі дуже повільні в MATLAB. Ви повинні уникати явних циклів у MATLAB, коли це можливо. Замість цього, як правило, проблема може виражатися в матричних / векторних операціях. Це MATLABic шлях. Існує також багато вбудованих функцій для ініціалізації матриць тощо. Наприклад, є функція one () , яка встановить всі елементи матриці на 1 (шляхом розширення, на будь-яке значення шляхом множення (скаляр) помножене на матрицю all-one)). Він також працює на 3-D масивах (на мою думку, тут висвітлено приклад).
Пітер Мортенсен

3
@PeterMortensen За яким фактором (приблизно) ефективність циклів у Matlab менша порівняно із C та Python? І чому це? Крім того, чи не покращилася ефективність циклів у Matlab за останні кілька років?
TensoR

3
@PeterMortensen "Зазвичай проблема може бути виражена через матричні / векторні операції" - для певних значень "зазвичай", так. IMO, точніше сказати, що люди, що працюють в Matlab тощо, мають культуру багаторічної ігнорування всіх речей, які неможливо зробити за допомогою матричних / векторних операцій, настільки, що все виглядає як цвях для цього молотка . І ми не повинні просто говорити «бо петлі в Matlab - це повільно», але «Matlab is slow» (це, мабуть, пов'язане з швидкою бібліотекою приміщень LA, написаних на C і Fortran).
Лише приблизно

5
Продуктивність для петель є спірним: matlabtips.com/matlab-is-no-longer-slow-at-for-loops
ohreally

@leftaroundabout Правда. Будучи занепокоєним швидкістю інтерпретованої (або напівінтерпретованої) мови, є досить чіткою ознакою, що у вас є проблема XY, де власне рішення - "не використовуйте цю мову". Виняток, звичайно, полягає в тому, що ви використовуєте генерацію коду в Simulink, але тоді питання полягає в тому, що C створює генератор коду і наскільки це ефективно.
Грем

Відповіді:


18

Коротка відповідь, ви хочете мати крайній лівий покажчик на самій внутрішній петлі. У вашому прикладі індекси циклу йдуть k, j, i, а індекси масиву будуть i, j, k. Це пов'язано з тим, як MATLAB зберігає в пам'яті різні розміри. Докладніше див. № 13 цієї публікації про редагування .


2
Або використовувати вбудовані функції one () .
Пітер Мортенсен

5
Приклад @Peter OP - це майже напевно лише іграшковий приклад циклу for, який щось робить, а не фактичного випадку використання.
Метт

@Matt Ви маєте рацію.
TensoR

11

Дещо довший відповідь, який пояснює, чому більш ефективно лівий індекс змінюється найшвидше. Є дві ключові речі, які вам потрібно зрозуміти.

По-перше, MATLAB (і Fortran, але не C та більшість інших мов програмування) зберігає масиви в пам'яті в "стовпчику основного порядку". наприклад, якщо A - матриця 2 на 3 на 10, то записи зберігатимуться в пам'яті в порядку

A (1,1,1)

A (2,1,1)

A (1,2,1)

A (2,2,1)

A (1,3,1)

A (2,3,1)

A (1,1,2)

A (2,1,2)

...

A (2,3,10)

Цей вибір основного порядку стовпців є довільним - ми можемо просто прийняти конвенцію про "основний рядок рядків", і насправді це робиться в C та деяких інших мовах програмування.

Друга важлива річ, яку вам потрібно зрозуміти, - це те, що сучасні процесори не мають доступу до пам’яті одночасно, а швидше завантажують і зберігають «кеш-лінії» з 64 або навіть 128 суміжних байтів (8 або 16 подвійних точних чисел з плаваючою комою) за один раз із пам’яті. Ці шматки даних тимчасово зберігаються в швидкому кеш-пам'яті та записуються назад за потребою. (На практиці архітектура кеша зараз досить складна з цілим 3 або 4 рівнями кеш-пам’яті, але основну думку можна пояснити однорівневим кешем такого типу, який мали комп'ютери в мої молодші дні.)

A

Якщо петлі вкладені таким чином, щоб внутрішній цикл оновлював індексний рядок, тоді записи до масиву будуть доступні в порядку A (1,1), A (2,1), A (3,1), ... Коли Перший запис A (1,1) доступний, система внесе кеш-рядок, що містить A (1,1), A (2,1), ..., A (8,1) в кеш з головної пам'яті . Наступні 8 ітерацій внутрішнього циклу працюють на цих даних без додаткових передач основної пам'яті.

Якщо в альтернативному варіанті, ми структуруємо петлі так, щоб індекс стовпця змінювався у самій внутрішній петлі, то до записів A можна отримати доступ у порядку A (1,1), A (1,2), A (1,3 ), ... У цьому випадку перший доступ принесе A (1,1), A (2,1), ..., A (8,1) в кеш з головної пам'яті, але 7/8 ці записи не використовуються. Тоді доступ до A (1,2) у другій ітерації приведе ще 8 записів із основної пам'яті тощо. До того моменту, коли код зайнявся роботою над другою лінією матриці, запис A (2,1) цілком може бути видалений з кешу, щоб отримати доступ до інших необхідних даних. В результаті код генерує в 8 разів більше трафіку, ніж необхідно.

Деякі оптимізуючі компілятори здатні автоматично реструктурувати петлі, щоб уникнути цієї проблеми.

Багато алгоритми чисельних лінійних алгебр для множення матриць і множника можуть бути оптимізовані для ефективної роботи зі схемою упорядкування рядка-магістраль або стовпчик-основний залежно від мови програмування. Це неправильний спосіб може мати істотний негативний вплив на продуктивність.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.