Như những người khác đã nói, vấn đề là lưu trữ đến vị trí bộ nhớ trong mảng : x[i][j]
. Đây là một chút hiểu biết tại sao:
Bạn có một mảng 2 chiều, nhưng bộ nhớ trong máy tính vốn dĩ là 1 chiều. Vì vậy, trong khi bạn tưởng tượng mảng của bạn như thế này:
0,0 | 0,1 | 0,2 | 0,3
----+-----+-----+----
1,0 | 1,1 | 1,2 | 1,3
----+-----+-----+----
2,0 | 2,1 | 2,2 | 2,3
Máy tính của bạn lưu trữ nó trong bộ nhớ dưới dạng một dòng duy nhất:
0,0 | 0,1 | 0,2 | 0,3 | 1,0 | 1,1 | 1,2 | 1,3 | 2,0 | 2,1 | 2,2 | 2,3
Trong ví dụ thứ 2, bạn truy cập vào mảng bằng cách lặp qua số thứ 2 trước, nghĩa là:
x[0][0]
x[0][1]
x[0][2]
x[0][3]
x[1][0] etc...
Có nghĩa là bạn đang đánh tất cả theo thứ tự. Bây giờ hãy nhìn vào phiên bản đầu tiên. Bạn đang làm:
x[0][0]
x[1][0]
x[2][0]
x[0][1]
x[1][1] etc...
Do cách C đặt ra mảng 2 chiều trong bộ nhớ, bạn đang yêu cầu nó nhảy khắp nơi. Nhưng bây giờ cho kicker: Tại sao điều này lại quan trọng? Tất cả các truy cập bộ nhớ đều giống nhau, phải không?
Không: vì bộ nhớ cache. Dữ liệu từ bộ nhớ của bạn được đưa đến CPU theo từng phần nhỏ (được gọi là 'dòng bộ đệm'), thường là 64 byte. Nếu bạn có số nguyên 4 byte, điều đó có nghĩa là bạn nhận được 16 số nguyên liên tiếp trong một gói nhỏ gọn. Nó thực sự khá chậm để lấy các khối bộ nhớ này; CPU của bạn có thể thực hiện rất nhiều công việc trong thời gian cần thiết để tải một dòng bộ đệm.
Bây giờ hãy nhìn lại thứ tự truy cập: Ví dụ thứ hai là (1) lấy một đoạn 16 ints, (2) sửa đổi tất cả chúng, (3) lặp lại 4000 * 4000/16 lần. Điều đó thật tuyệt vời và nhanh chóng, và CPU luôn có một cái gì đó để làm việc.
Ví dụ đầu tiên là (1) lấy một đoạn gồm 16 ints, (2) chỉ sửa đổi một trong số chúng, (3) lặp lại 4000 * 4000 lần. Điều đó sẽ đòi hỏi gấp 16 lần số lần "tìm nạp" từ bộ nhớ. CPU của bạn thực sự sẽ phải dành thời gian ngồi chờ bộ nhớ đó xuất hiện, và trong khi nó ngồi xung quanh bạn đang lãng phí thời gian quý báu.
Lưu ý quan trọng:
Bây giờ bạn đã có câu trả lời, đây là một lưu ý thú vị: không có lý do cố hữu nào mà ví dụ thứ hai của bạn phải là nhanh. Chẳng hạn, ở Fortran, ví dụ đầu tiên sẽ nhanh và ví dụ thứ hai chậm. Đó là bởi vì thay vì mở rộng mọi thứ thành các "hàng" khái niệm như C, Fortran mở rộng thành "các cột", tức là:
0,0 | 1,0 | 2,0 | 0,1 | 1,1 | 2,1 | 0,2 | 1,2 | 2,2 | 0,3 | 1,3 | 2,3
Bố cục của C được gọi là 'hàng chính' và Fortran được gọi là 'cột chính'. Như bạn có thể thấy, điều rất quan trọng là phải biết liệu ngôn ngữ lập trình của bạn là hàng chính hay chuyên ngành! Đây là một liên kết để biết thêm: http://en.wikipedia.org/wiki/Row-major_order