Quy trình sao chép bộ nhớ có thể phức tạp hơn và nhanh hơn nhiều so với việc sao chép bộ nhớ đơn giản thông qua các con trỏ như:
void simple_memory_copy(void* dst, void* src, unsigned int bytes)
{
unsigned char* b_dst = (unsigned char*)dst;
unsigned char* b_src = (unsigned char*)src;
for (int i = 0; i < bytes; ++i)
*b_dst++ = *b_src++;
}
Cải tiến
Cải tiến đầu tiên mà người ta có thể thực hiện là căn chỉnh một trong các con trỏ trên một ranh giới từ (tôi có nghĩa là kích thước số nguyên gốc, thường là 32 bit / 4 byte, nhưng có thể là 64 bit / 8 byte trên các kiến trúc mới hơn) và sử dụng kích thước từ di chuyển / sao chép hướng dẫn. Điều này yêu cầu sử dụng bản sao byte sang byte cho đến khi con trỏ được căn chỉnh.
void aligned_memory_copy(void* dst, void* src, unsigned int bytes)
{
unsigned char* b_dst = (unsigned char*)dst;
unsigned char* b_src = (unsigned char*)src;
// Copy bytes to align source pointer
while ((b_src & 0x3) != 0)
{
*b_dst++ = *b_src++;
bytes--;
}
unsigned int* w_dst = (unsigned int*)b_dst;
unsigned int* w_src = (unsigned int*)b_src;
while (bytes >= 4)
{
*w_dst++ = *w_src++;
bytes -= 4;
}
// Copy trailing bytes
if (bytes > 0)
{
b_dst = (unsigned char*)w_dst;
b_src = (unsigned char*)w_src;
while (bytes > 0)
{
*b_dst++ = *b_src++;
bytes--;
}
}
}
Các kiến trúc khác nhau sẽ hoạt động khác nhau dựa trên việc nguồn hoặc con trỏ đích có được căn chỉnh thích hợp hay không. Ví dụ: trên bộ xử lý XScale, tôi có hiệu suất tốt hơn bằng cách căn chỉnh con trỏ đích thay vì con trỏ nguồn.
Để cải thiện hơn nữa hiệu suất, một số thao tác giải nén vòng lặp có thể được thực hiện, để nhiều thanh ghi của bộ xử lý được tải dữ liệu hơn và điều đó có nghĩa là các lệnh tải / lưu trữ có thể được xen kẽ và ẩn độ trễ của chúng bằng các lệnh bổ sung (chẳng hạn như đếm vòng lặp, v.v.). Lợi ích mà điều này mang lại thay đổi khá nhiều bởi bộ xử lý, vì độ trễ của lệnh tải / lưu trữ có thể khá khác nhau.
Ở giai đoạn này, mã kết thúc được viết bằng Assembly thay vì C (hoặc C ++) vì bạn cần phải đặt các lệnh tải và lưu trữ theo cách thủ công để có được lợi ích tối đa của việc ẩn độ trễ và thông lượng.
Nói chung, toàn bộ dòng dữ liệu trong bộ nhớ cache nên được sao chép trong một lần lặp lại của vòng lặp chưa được cuộn.
Điều này đưa tôi đến cải tiến tiếp theo, thêm tính năng tìm nạp trước. Đây là những lệnh đặc biệt yêu cầu hệ thống bộ nhớ đệm của bộ xử lý tải các phần cụ thể của bộ nhớ vào bộ nhớ đệm của nó. Vì có sự chậm trễ giữa việc đưa ra lệnh và điền vào dòng bộ đệm, nên các lệnh cần được đặt theo cách sao cho dữ liệu có sẵn ngay khi nó được sao chép và không sớm / muộn.
Điều này có nghĩa là đặt các hướng dẫn tìm nạp trước khi bắt đầu hàm cũng như bên trong vòng lặp sao chép chính. Với hướng dẫn tìm nạp trước ở giữa vòng lặp sao chép, dữ liệu sẽ được sao chép trong nhiều lần lặp lại.
Tôi không thể nhớ, nhưng cũng có thể có lợi khi tìm nạp trước địa chỉ đích cũng như địa chỉ nguồn.
Các nhân tố
Các yếu tố chính ảnh hưởng đến tốc độ sao chép của bộ nhớ là:
- Độ trễ giữa bộ xử lý, bộ nhớ đệm và bộ nhớ chính.
- Kích thước và cấu trúc của các dòng bộ nhớ đệm của bộ xử lý.
- Hướng dẫn sao chép / di chuyển bộ nhớ của bộ xử lý (độ trễ, thông lượng, kích thước thanh ghi, v.v.).
Vì vậy, nếu bạn muốn viết một quy trình xử lý bộ nhớ hiệu quả và nhanh chóng, bạn sẽ cần phải biết khá nhiều về bộ xử lý và kiến trúc mà bạn đang viết. Chỉ cần nói rằng, trừ khi bạn đang viết trên một nền tảng nhúng nào đó sẽ dễ dàng hơn nhiều nếu chỉ sử dụng các quy trình sao chép bộ nhớ được tích hợp sẵn.