Как уже говорилось, проблема заключается в магазин в ячейку памяти в массиве: 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 называется 'row-major', а Fortran называется 'column-major'. Как видите, очень важно знать, является ли ваш язык программирования основным или основным столбцом! Вот ссылка для получения дополнительной информации: http://en.wikipedia.org/wiki/Row-major_order