2008-08-30 35 views
8

Cũ hơn K & R (2nd ed.) Và các văn bản C-ngôn ngữ khác mà tôi đã đọc rằng thảo luận về việc thực hiện một cấp phát bộ nhớ động trong phong cách của malloc()free() thường cũng đề cập đến , trong khi đi qua, một cái gì đó về hạn chế liên kết kiểu dữ liệu. Rõ ràng một số kiến ​​trúc phần cứng máy tính (CPU, đăng ký và truy cập bộ nhớ) hạn chế cách bạn có thể lưu trữ và xử lý các loại giá trị nhất định. Ví dụ, có thể có yêu cầu số nguyên 4 byte (long) phải được lưu trữ bắt đầu tại địa chỉ là bội số của bốn.hạn chế Alignment cho malloc()/Việt()

Những hạn chế, nếu có, thực hiện các nền tảng chính (Intel & AMD, SPARC, Alpha) áp đặt cấp phát bộ nhớ và bộ nhớ, hoặc tôi có thể bỏ qua việc phân bổ bộ nhớ theo địa chỉ cụ thể không?

Trả lời

4

Căn chỉnh vẫn còn khá quan trọng trong ngày hôm nay. Một số bộ xử lý (gia đình 68k nhảy vào tâm trí) sẽ ném một ngoại lệ nếu bạn cố gắng truy cập một giá trị từ trên một ranh giới kỳ lạ. Ngày nay, hầu hết các bộ vi xử lý sẽ chạy hai chu kỳ bộ nhớ để tìm nạp một từ chưa được ký, nhưng điều này chắc chắn sẽ chậm hơn so với tìm nạp được căn chỉnh. Một số bộ xử lý khác thậm chí sẽ không ném ngoại lệ, nhưng sẽ tìm nạp một giá trị không chính xác từ bộ nhớ!

Nếu không có lý do nào khác ngoài hiệu suất, bạn nên cố gắng tuân thủ các tùy chọn căn chỉnh của bộ xử lý. Thông thường, trình biên dịch của bạn sẽ chăm sóc tất cả các chi tiết, nhưng nếu bạn đang làm bất cứ điều gì mà bạn đặt ra cấu trúc bộ nhớ chính mình, sau đó nó có giá trị xem xét.

1

Bạn vẫn cần phải biết các vấn đề liên kết khi đặt ra một lớp hoặc cấu trúc trong C (++). Trong những trường hợp trình biên dịch sẽ làm điều đúng cho bạn, nhưng kích thước tổng thể của struct/lớp có thể có nhiều wastefull hơn mức cần thiết

Ví dụ:

struct 
{ 
    char A; 
    int B; 
    char C; 
    int D; 
}; 

Sẽ có một kích thước 4 * 4 = 16 byte (giả sử Windows trên x86) trong khi

struct 
{ 
    char A; 
    char C; 
    int B; 
    int D; 
}; 

Sẽ có kích thước 4 * 3 = 12 byte.

Điều này là do trình biên dịch thực thi căn chỉnh 4 byte cho số nguyên, nhưng chỉ 1 byte cho ký tự.

Trong các biến thành viên gói chung có cùng kích thước (loại) với nhau để giảm thiểu không gian bị lãng phí.

1

Như Greg đã đề cập nó vẫn còn quan trọng ngày nay (có lẽ nhiều hơn như vậy trong một số cách) và trình biên dịch thường chăm sóc của sự liên kết dựa trên mục tiêu của kiến ​​trúc. Trong môi trường được quản lý, trình biên dịch JIT có thể tối ưu hóa căn chỉnh dựa trên kiến ​​trúc thời gian chạy.

Bạn có thể thấy chỉ thị pragma (trong C/C++) thay đổi căn chỉnh. Điều này chỉ nên được sử dụng khi cần phải căn chỉnh rất cụ thể.

// For example, this changes the pack to 2 byte alignment. 
#pragma pack(2) 
6

Sparc, MIPS, Alpha, và hầu hết các khác "cổ điển RISC" kiến ​​trúc chỉ cho phép liên kết truy cập vào bộ nhớ, ngay cả ngày hôm nay. Một truy cập chưa được ký sẽ gây ra một ngoại lệ, nhưng một số hệ điều hành sẽ xử lý ngoại lệ bằng cách sao chép từ địa chỉ mong muốn trong phần mềm bằng cách sử dụng các tải và cửa hàng nhỏ hơn. Mã ứng dụng sẽ không biết có vấn đề gì, ngoại trừ việc hiệu năng sẽ rất tệ.

MIPS có hướng dẫn đặc biệt (lwl và lwr) có thể được sử dụng để truy cập số lượng 32 bit từ các địa chỉ chưa được ký. Bất cứ khi nào trình biên dịch có thể nói rằng địa chỉ có khả năng không được ký, nó sẽ sử dụng chuỗi lệnh này thay vì một lệnh lw bình thường.

x86 có thể xử lý truy cập bộ nhớ không được ký hiệu trong phần cứng mà không có ngoại lệ, nhưng vẫn có hiệu suất đạt tới 3 lần so với truy cập được căn chỉnh.

Ulrich Drepper đã viết một bài báo toàn diện về chủ đề này và các chủ đề liên quan đến bộ nhớ khác, What Every Programmer Should Know About Memory. Nó là một bản ghi rất dài, nhưng tràn ngập sự tốt lành dai.

1

Lưu ý rằng ngay cả trên IA-32 và AMD64, một số lệnh/nội tại SSE yêu cầu dữ liệu được căn chỉnh. Những hướng dẫn này sẽ ném một ngoại lệ nếu dữ liệu không được ký, vì vậy ít nhất bạn sẽ không phải gỡ lỗi "lỗi dữ liệu". Có những hướng dẫn tương đương chưa được ký kết, nhưng như Denton nói, chúng chậm hơn.

Nếu bạn đang sử dụng VC++, ngoài các chỉ thị gói #pragma, bạn cũng có chỉ thị __declspec (align) để căn chỉnh chính xác. Tài liệu VC++ cũng đề cập đến một hàm __aligned_malloc cho các yêu cầu căn chỉnh cụ thể.

Theo quy tắc chung, trừ khi bạn di chuyển dữ liệu qua trình biên dịch/ngôn ngữ hoặc đang sử dụng hướng dẫn SSE, bạn có thể bỏ qua các vấn đề liên kết.