Hãy xem xét chương trình máy tính rất đơn giản sau đây:
for i = 1 to n:
y[i] = x[p[i]]
Ở đây và y là mảng n- byte của byte và p là mảng n -element của từ. Ở đây n là lớn, ví dụ, n = 2 31 (do đó chỉ một phần không đáng kể của dữ liệu phù hợp với bất kỳ loại bộ nhớ đệm nào).
Giả sử bao gồm các số ngẫu nhiên , phân bố đồng đều giữa và .1 n
Từ quan điểm của phần cứng hiện đại, điều này có nghĩa như sau:
- đọc là rẻ (đọc tuần tự)
- đọc rất tốn kém (đọc ngẫu nhiên; hầu như tất cả các lần đọc là lỗi bộ nhớ cache; chúng tôi sẽ phải tìm nạp từng byte riêng lẻ từ bộ nhớ chính)
- viết là rẻ (viết tuần tự).
Và đây thực sự là những gì tôi đang quan sát. Chương trình này rất chậm so với một chương trình chỉ đọc và ghi tuần tự. Tuyệt quá.
Bây giờ đến câu hỏi: chương trình này song song như thế nào trên các nền tảng đa lõi hiện đại?
Giả thuyết của tôi là chương trình này không song song tốt. Rốt cuộc, nút cổ chai là bộ nhớ chính. Một lõi đơn đã lãng phí phần lớn thời gian của nó chỉ chờ một số dữ liệu từ bộ nhớ chính.
Tuy nhiên, đây không phải là những gì tôi quan sát thấy khi tôi bắt đầu thử nghiệm một số thuật toán trong đó nút cổ chai là loại hoạt động này!
Tôi chỉ đơn giản thay thế vòng lặp for ngây thơ bằng vòng lặp song song OpenMP (về bản chất, nó sẽ chỉ phân chia phạm vi thành các phần nhỏ hơn và chạy song song các phần này trên các lõi CPU khác nhau).
Trên các máy tính cấp thấp, tăng tốc thực sự là nhỏ. Nhưng trên các nền tảng cao cấp hơn, tôi đã ngạc nhiên rằng mình đang có được sự tăng tốc gần như tuyến tính tuyệt vời. Một số ví dụ cụ thể (thời gian chính xác có thể là một chút, có rất nhiều biến thể ngẫu nhiên; đây chỉ là những thử nghiệm nhanh):
2 x Xe 4 lõi (trong tổng số 8 lõi): tăng tốc 5-8 lần so với phiên bản đơn luồng.
2 x Xe 6 lõi (trong tổng số 12 lõi): tăng tốc hệ số 8-14 so với phiên bản đơn luồng.
Bây giờ điều này là hoàn toàn bất ngờ. Câu hỏi:
Chính xác thì tại sao loại chương trình này song song tốt như vậy ? Điều gì xảy ra trong phần cứng? (Dự đoán hiện tại của tôi là một cái gì đó dọc theo các dòng này: số lần đọc ngẫu nhiên từ các luồng khác nhau là "pipelined" và tỷ lệ trung bình để có câu trả lời cho các câu hỏi này cao hơn nhiều so với trường hợp của một luồng.)
Có nhất thiết phải sử dụng nhiều luồng và nhiều lõi để đạt được bất kỳ sự tăng tốc nào không? Nếu một loại đường ống thực sự diễn ra trong giao diện giữa bộ nhớ chính và CPU, thì không thể là một ứng dụng đơn luồng cho bộ nhớ chính biết rằng nó sẽ sớm cần , x [ p [ i + 1 ] ] , ... và máy tính có thể bắt đầu tìm nạp các dòng bộ đệm có liên quan từ bộ nhớ chính không? Nếu điều này là có thể về nguyên tắc, làm thế nào để tôi đạt được nó trong thực tế?
Mô hình lý thuyết đúng mà chúng ta có thể sử dụng để phân tích loại chương trình này (và đưa ra dự đoán chính xác về hiệu suất) là gì?
Chỉnh sửa: Hiện tại có một số mã nguồn và kết quả điểm chuẩn có sẵn tại đây: https://github.com/suomela/abul-random-read
Một số ví dụ về số liệu sân bóng ( ):
- khoảng 42 ns mỗi lần lặp (đọc ngẫu nhiên) với một chuỗi
- khoảng 5 ns mỗi lần lặp (đọc ngẫu nhiên) với 12 lõi.