2012-01-06 29 views
38

sau đây trích từ heremalloc hiểu sự liên kết như thế nào?

pw = (widget *)malloc(sizeof(widget)); 

phân bổ lưu trữ thô. Trên thực tế, cuộc gọi malloc phân bổ lưu trữ đó là đủ lớn và phù hợp liên kết để giữ một đối tượng loại phụ tùng

cũng thấy fast pImpl từ Herb Sutter, ông nói:

Alignment. Bất kỳ bộ nhớ Alignment. Bất kỳ bộ nhớ đó là phân bổ động qua mới hoặc malloc được đảm bảo để được sắp xếp đúng cho đối tượng của bất kỳ loại, nhưng bộ đệm mà không được cấp phát động không có sự bảo đảm như

Tôi tò mò về vấn đề này, làm thế nào hiện malloc biết sự liên kết của các loại tùy chỉnh?

+2

mới và malloc, theo mặc định, căn chỉnh địa chỉ thành 8 byte (x86) hoặc 16 byte (x64), là tối ưu cho hầu hết dữ liệu phức tạp. Ngoài ra là sizeof() nhiệm vụ để có được kích thước chính xác struct ** với ** đệm nội bộ cho sự liên kết, nếu cần thiết. –

Trả lời

38

Yêu cầu căn chỉnh là đệ quy: Căn chỉnh của bất kỳ struct đơn giản là sự liên kết lớn nhất của bất kỳ thành viên nào của nó và điều này được hiểu một cách đệ quy. Ví dụ, và giả sử rằng sự sắp xếp của từng loại cơ bản bằng kích thước của nó (điều này không phải lúc nào cũng đúng), struct X { int; char; double; } có sự liên kết của double, và nó sẽ được đệm thành bội số của kích thước gấp đôi (ví dụ 4 (int), 1 (char), 3 (padding), 8 (double)). Các struct Y { int; X; float; } có sự liên kết của X, là lớn nhất và bằng sự liên kết của double, và Y được đặt ra cho phù hợp: 4 (int), 4 (padding), 16 (X), 4 (float), 4 (đệm).

(Tất cả số chỉ là ví dụ và có thể khác trên máy tính của bạn.)

Do đó, bằng cách phá vỡ nó xuống cho các loại cơ bản, chúng ta chỉ cần biết một số ít các sắp xếp cơ bản, và trong số những người có một nổi tiếng lớn nhất. C++ thậm chí xác định loại maxalign_t (Tôi nghĩ) có căn chỉnh là căn chỉnh lớn nhất.

Tất cả malloc() cần làm là chọn địa chỉ là bội số của giá trị đó.

+1

Điều quan trọng cần chỉ ra là điều này không bao gồm các chỉ thị tùy chỉnh 'align' cho trình biên dịch có thể điều chỉnh dữ liệu quá mức. – Mehrdad

+2

Mặc dù nếu bạn sử dụng chúng đã nằm ngoài phạm vi của tiêu chuẩn, xin lưu ý rằng bộ nhớ được phân bổ theo cách này có thể * sẽ không * đáp ứng các yêu cầu căn chỉnh cho các loại được xây dựng như _m256 có sẵn dưới dạng tiện ích mở rộng trên một số nền tảng. – jcoder

+0

Điều gì xảy ra khi bạn chỉ định căn chỉnh tùy chỉnh thông qua 'alignas' lớn hơn sự liên kết lớn nhất của kiểu dữ liệu nguyên thủy? – Curious

4

Thông tin duy nhất mà malloc() có thể sử dụng là kích thước của yêu cầu được chuyển đến nó. Nói chung, nó có thể làm một cái gì đó giống như làm tròn kích thước đã truyền cho công suất lớn hơn (hoặc bằng) gần nhất, và căn chỉnh bộ nhớ dựa trên giá trị đó. Cũng có thể có một giới hạn trên trên giá trị căn chỉnh, chẳng hạn như 8 byte.

Ở trên là một cuộc thảo luận giả thuyết và việc triển khai thực tế phụ thuộc vào kiến ​​trúc máy và thư viện thời gian chạy mà bạn đang sử dụng. Có lẽ malloc() của bạn luôn trả về các khối được căn chỉnh trên 8 byte và nó không bao giờ phải làm bất kỳ điều gì khác.

+0

Tóm lại, 'malloc' sử dụng sự liên kết 'trường hợp xấu nhất' bởi vì nó không biết gì tốt hơn. Điều đó có nghĩa là 'calloc' có thể thông minh hơn vì phải mất hai args, số lượng đối tượng và kích thước của một đối tượng đơn lẻ? –

+0

Có thể. Có thể không.Bạn sẽ phải xem nguồn thư viện thời gian chạy của mình để tìm hiểu. –

+1

-1, xin lỗi. Câu trả lời của bạn bao gồm sự thật, nhưng nó cũng bao gồm thông tin không chính xác. Nó không phải là một thứ "có lẽ, có lẽ không"; nó được ghi chép cụ thể để hoạt động theo cách không phụ thuộc vào kích thước. (Dunno * tại sao * không, mặc dù. Nó có vẻ như nó sẽ làm cho cảm giác hoàn hảo cho nó để làm như vậy.) – ruakh

14

Tôi nghĩ rằng một phần có liên quan hầu hết các quote Herb Sutter là phần Tôi đã đánh dấu in đậm:

Alignment. Bất kỳ bộ nhớ Alignment. Bất kỳ bộ nhớ đó là phân bổ tự động qua mới hoặc malloc được đảm bảo để được sắp xếp đúng cách cho các đối tượng của bất kỳ loại, nhưng bộ đệm mà không được phân bổ tự động không có sự bảo đảm như

Nó không nhất thiết phải biết những gì gõ bạn có trong tâm trí, bởi vì nó sắp xếp cho mọi loại. Trên bất kỳ hệ thống cụ thể nào, có kích thước căn chỉnh tối đa cần thiết hoặc có ý nghĩa; ví dụ, một hệ thống có các từ bốn byte sẽ có khả năng liên kết tối đa bốn byte.

này cũng được làm rõ bởi the malloc(3) man-page, mà nói trong phần:

Các malloc()calloc() chức năng trả về một con trỏ tới bộ nhớ phân bổ được phù hợp liên kết cho bất kỳ loại biến.

+2

ý nghĩa của bất kỳ loại biến nào? nó không trả lời câu hỏi của tôi. nó có nghĩa là malloc sẽ luôn luôn sử dụng kích thước liên kết tối đa trong bất kỳ hệ thống nhất định, phải không? – Chang

+2

@Chang: hiệu quả, vâng. Cũng lưu ý, báo giá là sai. 'new' chỉ được đảm bảo có sự liên kết" bất kỳ "khi phân bổ' char' hoặc 'unsigned char'. Đối với những người khác, nó có thể có sự liên kết nhỏ hơn. –

+0

@Chang: Đúng, kích thước căn chỉnh tối đa. "Phù hợp phù hợp với bất kỳ loại biến" có nghĩa là "phù hợp phù hợp cho một' int' * và * phù hợp phù hợp cho một con trỏ * và * phù hợp phù hợp với bất kỳ 'struct' * và *...". – ruakh

3

1) Căn chỉnh với bội số chung ít nhất của tất cả các sắp xếp. ví dụ. nếu ints yêu cầu 4 byte căn chỉnh, nhưng con trỏ yêu cầu 8, sau đó phân bổ tất cả mọi thứ để 8 byte liên kết. Điều này khiến mọi thứ được căn chỉnh.

2) Sử dụng đối số kích thước để xác định căn chỉnh chính xác. Đối với các kích thước nhỏ, bạn có thể phỏng đoán loại, chẳng hạn như malloc(1) (giả sử các loại kích thước khác không phải là 1) luôn là char. C++ new có lợi ích là loại an toàn và do đó luôn có thể đưa ra quyết định căn chỉnh theo cách này.

+0

Bạn có thể mở rộng từ viết tắt LCM không? Tôi có thể đoán, nhưng tôi không cần phải làm vậy. –

+0

Ngoài ra, còn có các loại khác trong C++ có thể là 1 byte. Tuy nhiên, hàm ý của bạn là chính xác, nó vẫn có thể căn chỉnh dựa trên kích thước của kiểu. –

2

Trước khi liên kết C++ 11 được xử lý khá đơn giản bằng cách sử dụng căn chỉnh lớn nhất nơi giá trị chính xác không xác định và malloc/calloc vẫn hoạt động theo cách này. Điều này có nghĩa là phân bổ malloc được căn chỉnh chính xác cho bất kỳ loại nào.

Căn chỉnh sai có thể dẫn đến hành vi không xác định theo tiêu chuẩn nhưng tôi đã thấy các trình biên dịch x86 là hào phóng và chỉ trừng phạt với hiệu suất thấp hơn.

Lưu ý rằng bạn cũng có thể tinh chỉnh căn chỉnh thông qua tùy chọn trình biên dịch hoặc chỉ thị. (pragma gói cho VisualStudio ví dụ).

Nhưng khi nói đến vị trí mới, sau đó C++ 11 đưa chúng ta từ khóa mới được gọi là alignofalignas. Dưới đây là một số mã cho thấy hiệu ứng nếu liên kết tối đa của trình biên dịch lớn hơn 1. Vị trí đầu tiên mới bên dưới tự động tốt nhưng không phải là thứ hai.

#include <iostream> 
#include <malloc.h> 
using namespace std; 
int main() 
{ 
     struct A { char c; }; 
     struct B { int i; char c; }; 

     unsigned char * buffer = (unsigned char *)malloc(1000000); 
     long mp = (long)buffer; 

     // First placment new 
     long alignofA = alignof(A) - 1; 
     cout << "alignment of A: " << std::hex << (alignofA + 1) << endl; 
     cout << "placement address before alignment: " << std::hex << mp << endl; 
     if (mp&alignofA) 
     { 
      mp |= alignofA; 
      ++mp; 
     } 
     cout << "placement address after alignment : " << std::hex <<mp << endl; 
     A * a = new((unsigned char *)mp)A; 
     mp += sizeof(A); 

     // Second placment new 
     long alignofB = alignof(B) - 1; 
     cout << "alignment of B: " << std::hex << (alignofB + 1) << endl; 
     cout << "placement address before alignment: " << std::hex << mp << endl; 
     if (mp&alignofB) 
     { 
      mp |= alignofB; 
      ++mp; 
     } 
     cout << "placement address after alignment : " << std::hex << mp << endl; 
     B * b = new((unsigned char *)mp)B; 
     mp += sizeof(B); 
} 

Tôi đoán hiệu suất của mã này có thể được cải thiện bằng một số thao tác bitwise.

EDIT: Thay thế tính toán modulo đắt tiền bằng các thao tác bitwise. Vẫn hy vọng rằng ai đó tìm thấy một cái gì đó thậm chí còn nhanh hơn.

+1

Nó không thực sự là trình biên dịch, nó chính là phần cứng. Trên x86 một truy cập bộ nhớ không đúng thiết bị chỉ đơn giản là buộc bộ vi xử lý lấy hai bên của ranh giới bộ nhớ và kết quả mảnh lại với nhau, vì vậy nó luôn "đúng" nếu chậm hơn. Ví dụ: một số bộ vi xử lý ARM, bạn sẽ nhận được một lỗi bus và một sự cố chương trình.Đây là một vấn đề nhỏ bởi vì nhiều lập trình viên không bao giờ được tiếp xúc với bất kỳ thứ gì khác ngoài x86, và vì vậy có thể không biết rằng hành vi này thực sự không được xác định thay vì chỉ giảm hiệu năng. – Thomas

+0

Bạn là chính xác, phần cứng hoặc phần mềm mã hóa cpu của nó nhưng không phải là trình biên dịch thực sự giúp bạn tiết kiệm kiến ​​trúc x86. Tôi thực sự tự hỏi tại sao không có api thuận tiện hơn để xử lý này. Như thể các nhà thiết kế C/C++ muốn các nhà phát triển bước vào bẫy. Nhắc tôi về std :: numeric_limits :: min() bẫy. Bất cứ ai cũng nhận được quyền đó lần đầu tiên? –

+0

Vâng, một khi bạn biết những gì đang xảy ra, nó không phải là quá khó để thay đổi phong cách lập trình của bạn từ tất cả các loại điên-punning để gõ mã tốt, may mắn thay. Hệ thống loại C làm cho nó khá dễ dàng để duy trì sự liên kết kiểu miễn là bạn không làm công cụ thao tác bit điên rồ mà không chú ý. Bây giờ mã con trỏ-aliasing-miễn phí mặt khác có một số ngữ nghĩa khó khăn hơn nhiều ... – Thomas

Các vấn đề liên quan