2010-02-01 21 views
26

Cá nhân, tôi hoàn toàn thích các thư viện chỉ có tiêu đề, nhưng có những tuyên bố rằng chúng gây ra sự phồng lên mã do quá nội tuyến (cũng như vấn đề rõ ràng khác về thời gian biên dịch dài hơn).Khi nào thư viện chỉ có tiêu đề được chấp nhận?

Tôi đã tự hỏi, có bao nhiêu sự thật là có những tuyên bố này (một về sưng lên)?

Hơn nữa, chi phí 'hợp lý'? (Rõ ràng là có những trường hợp không thể tránh khỏi như khi một thư viện được triển khai hoàn toàn hoặc chủ yếu với các mẫu, tuy nhiên tôi quan tâm hơn đến trường hợp có sẵn một sự lựa chọn.)

Tôi biết không có quy tắc cứng nhắc và nhanh chóng nào , vv như xa như những thứ như thế này đi, nhưng tôi chỉ cố gắng để có được một cảm giác cho những gì người khác nghĩ về vấn đề này.

P.S. Vâng, đây là một câu hỏi rất mơ hồ và chủ quan, tôi biết, và vì vậy tôi đã gắn thẻ nó như vậy.

Trả lời

6

Tôi làm việc cho một công ty có bộ phận "Phần mềm trung gian" để duy trì một vài trăm thư viện thường được nhiều nhóm sử dụng.

Mặc dù ở trong cùng một công ty, chúng tôi chỉ e ngại từ cách tiếp cận đầu trang và thích ưu tiên khả năng tương thích nhị phân so với hiệu suất vì dễ bảo trì.

Sự đồng thuận chung là hiệu suất đạt được (nếu có) sẽ không đáng để gây rắc rối.

Hơn nữa, cái gọi là "mã-bloat" có thể có tác động tiêu cực đến hiệu suất khi nhiều mã được tải trong bộ nhớ cache ngụ ý nhớ cache nhiều hơn, và đó là những kẻ giết hiệu suất.

Trong một thế giới lý tưởng tôi giả sử rằng trình biên dịch và các mối liên kết có thể là đủ thông minh KHÔNG để tạo ra những "nhiều định nghĩa" quy tắc, nhưng miễn là nó không phải là trường hợp, tôi sẽ (cá nhân) ủng hộ:

  • khả năng tương thích nhị phân
  • phi nội tuyến (đối với phương pháp mà hơn một vài dòng)

Tại sao bạn không thử? Chuẩn bị hai thư viện (chỉ một tiêu đề và phần còn lại không có các phương thức nội tuyến qua một vài dòng) và kiểm tra hiệu năng tương ứng của chúng trong trường hợp CỦA BẠN.

EDIT:

Nó được chỉ ra bởi 'jalf' (nhờ) rằng tôi nên chính xác những gì tôi có nghĩa là chính xác bởi khả năng tương thích nhị phân.

2 phiên bản của một thư viện nhất định được cho là tương thích nhị phân nếu bạn có thể (thường) liên kết với nhau hoặc không có bất kỳ thay đổi nào trong thư viện của riêng bạn.

Vì bạn chỉ có thể liên kết với một phiên bản của một thư viện cụ thể Target, tất cả thư viện được sử dụng Target sẽ sử dụng cùng một phiên bản ... và đây là nguyên nhân của sự chuyển đổi của thuộc tính này.

MyLib --> Lib1 (v1), Lib2 (v1) 
Lib1 (v1) --> Target (v1) 
Lib2 (v1) --> Target (v1) 

Bây giờ, nói rằng chúng ta cần một sửa chữa trong Target cho một tính năng duy nhất được sử dụng bởi Lib2, chúng tôi cung cấp một phiên bản mới (v2).Nếu (v2) tương thích với (v1) nhị phân, sau đó chúng ta có thể làm:

Lib1 (v1) --> Target (v2) 
Lib2 (v1) --> Target (v2) 

Tuy nhiên, nếu nó không phải là trường hợp, sau đó chúng tôi sẽ có:

Lib1 (v2) --> Target (v2) 
Lib2 (v2) --> Target (v2) 

Yep, bạn đọc nó đúng, mặc dù Lib1 làm không yêu cầu sửa chữa, bạn phải xây dựng lại phiên bản này với phiên bản Target mới vì phiên bản này là bắt buộc đối với các cập nhật Lib2Executable chỉ có thể liên kết với một phiên bản Target.

Với thư viện chỉ tiêu đề, vì bạn không có thư viện, bạn có hiệu quả không tương thích nhị phân. Do đó, mỗi khi bạn sửa chữa (bảo mật, lỗi nghiêm trọng, v.v ...), bạn cần cung cấp một phiên bản mới và tất cả các thư viện phụ thuộc vào bạn (thậm chí gián tiếp) sẽ phải được xây dựng lại dựa trên phiên bản mới này!

+3

Làm thế nào để tiêu đề chỉ bao hàm sự libs mã sưng lên? Trình biên dịch thường sẽ không nội tuyến nếu nó có nghĩa là mã lớn hơn đáng kể. Và, cho rằng vấn đề, làm thế nào là khả năng tương thích nhị phân bị ảnh hưởng bởi libs tiêu đề chỉ? – jalf

+0

@Matthieu: Thử nghiệm thực sự là luôn luôn tốt để xác định đó sẽ mang lại kết quả tốt nhất. – DrYak

+1

@Jalf: *** 1. Kịch bản chỉ tiêu đề: đối với mỗi thay đổi duy nhất đối với thư viện, bản cập nhật cho thư viện có nghĩa là biên dịch lại mọi dự án cuối cùng có sự phụ thuộc vào nó. (Hãy suy nghĩ về tình hình của Matthieu: hàng trăm thư viện, nhiều nhóm - rất nhiều biên dịch lại liên quan). *** 2. Kịch bản nhị phân duy nhất: đối với nhiều thay đổi khác nhau, bản cập nhật sẽ đơn giản liên quan đến việc thả tệp .SO hoặc .DLL mới, miễn là ABI vẫn giữ nguyên. Mặc dù tính năng mới có thể thay đổi chữ ký phương thức và cấu trúc dữ liệu, các cập nhật quan trọng (bảo mật hoặc tính ổn định) hiếm khi xảy ra. – DrYak

1

Nội tuyến quá có lẽ là điều cần được giải quyết bởi người gọi, điều chỉnh tùy chọn trình biên dịch của họ, thay vì bằng cách cố gắng kiểm soát nó thông qua các công cụ rất cùn của từ khóa và định nghĩa trong tiêu đề inline. Ví dụ GCC có -finline-limit và bạn bè, vì vậy bạn có thể sử dụng các quy tắc nội tuyến khác nhau cho các đơn vị dịch khác nhau. Có gì quá nội tuyến cho bạn có thể không quá nội tuyến đối với tôi, tùy thuộc vào kiến ​​trúc, kích thước và tốc độ bộ nhớ cache hướng dẫn, cách sử dụng chức năng, v.v. Không phải tôi đã từng thực hiện điều chỉnh này: trong thực tế khi nó có đáng lo ngại về điều đó, nó đáng được viết lại, nhưng đó có thể là sự trùng hợp. Dù bằng cách nào, nếu tôi là người dùng của một thư viện thì mọi thứ khác bằng nhau tôi muốn có tùy chọn để nội tuyến (tùy thuộc vào trình biên dịch của tôi, và cái mà tôi có thể không mất) hơn là không thể nội tuyến.

Tôi nghĩ rằng sự khủng bố của mã bloat từ thư viện chỉ tiêu đề đến nhiều hơn từ lo lắng rằng các mối liên kết sẽ không thể loại bỏ các bản sao dự phòng của mã. Vì vậy, bất kể cho dù chức năng thực sự được inlined tại các trang web cuộc gọi hay không, mối quan tâm là bạn kết thúc với một bản sao có thể gọi của hàm (hoặc lớp) cho mỗi tập tin đối tượng sử dụng nó. Tôi không thể nhớ liệu các địa chỉ được đưa vào các hàm nội tuyến trong các đơn vị dịch khác nhau trong C++ có phải so sánh bằng nhau hay không, nhưng thậm chí giả sử chúng làm, sao cho có một bản sao "chuẩn" của hàm trong mã được liên kết, nó không nhất thiết có nghĩa là trình liên kết thực sự sẽ xóa các hàm trùng lặp đã chết. Nếu hàm chỉ được định nghĩa trong một đơn vị dịch, bạn có thể tự tin một cách hợp lý sẽ chỉ có một bản sao độc lập trên mỗi thư viện tĩnh hoặc tệp thực thi có sử dụng nó.

Tôi thành thật không biết làm thế nào căn cứ vào nỗi sợ này. Tất cả mọi thứ tôi đã làm việc trên đã bị hạn chế về bộ nhớ vì chúng tôi đã sử dụng inline chỉ với các chức năng static inline quá nhỏ đến nỗi chúng tôi không mong đợi phiên bản nội tuyến lớn hơn đáng kể so với mã để thực hiện cuộc gọi và không tâm trí trùng lặp, hoặc người nào khác để hạn chế lỏng lẻo mà chúng tôi không quan tâm đến bất kỳ bản sao bất cứ nơi nào. Tôi chưa bao giờ đạt được nền tảng trung gian của việc tìm kiếm và đếm các bản sao trên các trình biên dịch khác nhau. Tôi thỉnh thoảng đã nghe từ những người khác rằng đó là một vấn đề với mã mẫu, vì vậy tôi tin rằng có sự thật trong các tuyên bố.

Làm điều này như tôi đi ngay bây giờ, tôi nghĩ nếu bạn gửi thư viện chỉ tiêu đề, người dùng luôn có thể gây rối nếu họ không thích. Viết một tiêu đề mới khai báo tất cả các hàm và một đơn vị dịch mới bao gồm các định nghĩa.Chức năng quy định tại các lớp học sẽ phải được chuyển đến định nghĩa bên ngoài, vì vậy nếu bạn muốn hỗ trợ sử dụng này mà không đòi hỏi người dùng đến ngã ba mã của bạn, bạn có thể tránh làm điều đó và cung cấp hai tiêu đề:

// declare.h 
inline int myfunc(int); 

class myclass { 
    inline int mymemberfunc(int); 
}; 

// define.h 
#include "declare.h" 
int myfunc(int a) { return a; } 

int myclass::mymemberfunc(int a) { return myfunc(a); } 

Người gọi người lo lắng về mã sưng lên có lẽ có thể đánh lừa trình biên dịch của họ bằng cách bao gồm declare.h trong tất cả các file của họ, sau đó viết:

// define.cpp 
#include "define.h" 

họ có lẽ cũng cần phải tránh tối ưu hóa toàn bộ chương trình để đảm bảo các mã sẽ không được sắp xếp theo hàng nhưng sau đó bạn không thể chắc chắn rằng ngay cả một chức năng không nội tuyến sẽ không được gạch chân bởi tối ưu hóa toàn bộ chương trình.

Người gọi không lo lắng về mã bloat có thể sử dụng define.h trong tất cả các tệp của họ.

6

Theo kinh nghiệm của tôi sưng lên không phải là một vấn đề:

  • header chỉ thư viện cung cấp cho các trình biên dịch khả năng lớn hơn để nội tuyến, nhưng họ không buộc các trình biên dịch nội tuyến - nhiều trình biên dịch coi từ khoá inline như không có gì hơn hơn một lệnh để bỏ qua nhiều định nghĩa giống hệt nhau.

  • Trình biên dịch thường có các tùy chọn để tối ưu hóa để kiểm soát lượng nội tuyến;/Os trên trình biên dịch của Microsoft.

  • Tốt hơn nên cho phép trình biên dịch quản lý tốc độ so với các vấn đề về kích thước. Bạn sẽ chỉ nhìn thấy bloat từ các cuộc gọi thực sự đã được inlined, và trình biên dịch sẽ chỉ inline chúng nếu heuristics của nó chỉ ra rằng nó nội tuyến sẽ cải thiện hiệu suất.

Tôi sẽ không xem xét mã sưng lên như một lý do để tránh xa tiêu đề chỉ thư viện - nhưng tôi sẽ mong bạn để xem xét bao nhiêu một tiêu đề duy nhất cách tiếp cận sẽ tăng biên dịch lần.

3

Tôi đồng ý, thư viện nội tuyến dễ tiêu thụ hơn nhiều.

Inline bloat chủ yếu phụ thuộc vào nền tảng phát triển mà bạn đang làm việc - đặc biệt là trên trình biên dịch/khả năng liên kết. Tôi sẽ không mong đợi nó là một vấn đề lớn với VC9 ngoại trừ trong một vài trường hợp góc.

Tôi đã nhìn thấy một số thay đổi đáng chú ý ở kích thước cuối cùng ở một số địa điểm của dự án VC6 lớn, nhưng thật khó để đưa ra "cụ thể, nếu ...". Bạn probalby cần phải thử với mã của bạn trong devenv của bạn.

Vấn đề thứ hai có thể là thời gian biên dịch, ngay cả khi sử dụng tiêu đề biên dịch sẵn (cũng có sự cân bằng).

Thứ ba, một số cấu trúc có vấn đề - ví dụ: các thành viên dữ liệu tĩnh được chia sẻ trên các đơn vị dịch - hoặc tránh có một cá thể riêng biệt trong mỗi đơn vị dịch thuật.


tôi đã thấy cơ chế sau đây để cung cấp cho người dùng một sự lựa chọn:

// foo.h 
#ifdef MYLIB_USE_INLINE_HEADER 
#define MYLIB_INLINE inline 
#else 
#define MYLIB_INLINE 
#endif 

void Foo(); // a gazillion of declarations 

#ifdef MYLIB_USE_INLINE_HEADER 
#include "foo.cpp" 
#endif 

// foo.cpp 
#include "foo.h" 
MYLIB_INLINE void Foo() { ... } 
Các vấn đề liên quan