Як вже говорилося, проблема полягає в магазин в комірку пам'яті в масиві: x[i][j]
. Ось трохи розуміння, чому:
У вас є двовимірний масив, але пам'ять у комп'ютері за своєю суттю є одновимірною. Тож поки ви уявляєте свій масив таким чином:
0,0 | 0,1 | 0,2 | 0,3
----+-----+-----+----
1,0 | 1,1 | 1,2 | 1,3
----+-----+-----+----
2,0 | 2,1 | 2,2 | 2,3
Ваш комп'ютер зберігає його в пам'яті як єдиний рядок:
0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | 1,2 | 1,3 | 2,0 | 2,1 | 2,2 | 2,3
У другому прикладі ви отримуєте доступ до масиву, перенісши спочатку 2-е число, тобто:
x[0][0]
x[0][1]
x[0][2]
x[0][3]
x[1][0] etc...
Це означає, що ти б’єш їх усі по порядку. Тепер подивіться на 1-у версію. Ти робиш:
x[0][0]
x[1][0]
x[2][0]
x[0][1]
x[1][1] etc...
Через те, як C виклав 2-денний масив в пам'ять, ви просите його стрибати всюди. Але тепер для кікера: Чому це важливо? Усі доступ до пам'яті однаковий, правда?
Ні: через кеші. Дані з вашої пам’яті надходять до центрального процесора невеликими шматками (звані «лінії кеша»), як правило, 64 байти. Якщо у вас є 4-байтові цілі числа, це означає, що ви отримуєте 16 послідовних цілих чисел у акуратному маленькому пакеті. Це насправді досить повільно, щоб отримати ці шматки пам'яті; ваш процесор може зробити багато роботи за час, необхідний для завантаження однієї лінії кешу.
Тепер огляньтесь на порядок доступу: Другий приклад - (1) захоплення шматка 16 дюймів, (2) модифікація всіх, (3) повторення 4000 * 4000/16 разів. Це приємно і швидко, і у процесора завжди є над чим працювати.
Перший приклад - (1) захопити шматок 16 дюймів, (2) змінити лише один з них, (3) повторити 4000 * 4000 разів. Для цього потрібно буде в 16 разів перевищити кількість "витягів" з пам'яті. Ваш процесор насправді повинен буде провести час сидячи навколо, чекаючи, коли ця пам'ять з’явиться, і, поки він сидить навколо, ви витрачаєте цінний час.
Важлива примітка:
Тепер, коли у вас є відповідь, ось цікава примітка: немає притаманної причини, що ваш другий приклад повинен бути швидким. Наприклад, у Фортрансі перший приклад був би швидким, а другий повільним. Це тому, що замість того, щоб розширювати речі на концептуальні "рядки", як це робить C, Fortran розширюється на "стовпці", тобто:
0,0 | 1,0 | 2,0 | 0,1 | 1,1 | 2,1 | 0,2 | 1,2 | 2,2 | 0,3 | 1,3 | 2,3
Макет C називається «рядком-мажор», а Фортран - «стовпчик-мажор». Як ви бачите, дуже важливо знати, чи мова вашого програмування є рядковою чи основною для стовпців! Ось посилання для отримання додаткової інформації: http://en.wikipedia.org/wiki/Row-major_order