2010-05-13 43 views
29

Tôi đang trong quá trình thay đổi một phần ứng dụng C++ của mình từ việc sử dụng mảng kiểu C cũ sang lớp chứa C++ templated. Xem this question để biết chi tiết. Trong khi các giải pháp đang làm việc rất tốt, mỗi thay đổi nhỏ tôi thực hiện cho mã templated gây ra một số lượng rất lớn của biên dịch lại diễn ra, và do đó làm chậm đáng kể thời gian xây dựng. Có cách nào lấy mã mẫu ra khỏi tiêu đề và quay lại tệp cpp, do đó, các thay đổi nhỏ trong triển khai không gây ra việc xây dựng lại lớn không?Cách giảm thời gian biên dịch với các mẫu C++

Trả lời

14

Tôi nghĩ rằng các quy tắc chung sẽ được áp dụng. Cố gắng giảm sự ghép nối giữa các phần của mã. Chia nhỏ các tiêu đề mẫu quá lớn thành các nhóm chức năng nhỏ hơn được sử dụng cùng nhau, do đó, toàn bộ nội dung sẽ không được bao gồm trong mỗi tệp nguồn.

Ngoài ra, hãy thử đưa tiêu đề vào trạng thái ổn định nhanh, có thể thử nghiệm chúng với chương trình thử nghiệm nhỏ hơn, vì vậy chúng sẽ không cần thay đổi (quá nhiều) khi được tích hợp vào chương trình lớn hơn.

(Như với bất kỳ tối ưu hóa, nó có thể là ít giá trị để tối ưu hóa cho tốc độ của trình biên dịch khi giao dịch với các mẫu, chứ không phải là tìm kiếm một "thuật toán" tối ưu hóa mà làm giảm việc tải đáng kể ở nơi đầu tiên.)

+3

+100 Bạn không thử nghiệm một mẫu bên trong một dự án không liên quan lớn. Các mẫu nên được như là lỗi miễn phí nhất có thể trước khi nó đi vào một dự án không liên quan. – jmucchiello

+0

+1. Ngoài ra Unit Test như điên! –

+0

Oups, không thấy bạn trả lời trước khi tôi đăng bài của tôi mặc dù tôi đoán tôi là một chút rõ ràng hơn ... trên đường về phía dấu 10k;)? –

4
  • Bạn có thể nhận được trình biên dịch hỗ trợ từ khóa export, nhưng điều đó không có khả năng kéo dài.

  • Bạn có thể sử dụng explicit instantiation, nhưng thật không may, yêu cầu bạn phải dự đoán các loại mẫu bạn sẽ sử dụng trước.

  • Nếu bạn có thể loại trừ các loại templated từ thuật toán của mình, bạn có thể đặt nó trong tệp .cc của riêng nó.

  • Tôi sẽ không đề xuất điều này, trừ khi đó là vấn đề lớn, nhưng: Bạn có thể cung cấp giao diện chứa mẫu được triển khai với các cuộc gọi đến triển khai void*.

+3

'xuất' sẽ bị xóa trong C++ 0x. Bạn thậm chí không nên nghĩ đến việc sử dụng nó ngay bây giờ. – pmr

+0

+1 cho sự khởi đầu rõ ràng, tôi đã không đi qua điều này trước đây và nghĩ rằng nó có thể giúp rất nhiều. –

2

Bạn có thể xác định lớp cơ sở không có mẫu và di chuyển phần lớn việc triển khai ở đó. Mảng templated sau đó sẽ xác định chỉ các phương thức proxy, sử dụng lớp cơ sở cho mọi thứ.

18

Một số phương pháp:

  • Các export keyword về mặt lý thuyết có thể giúp đỡ, nhưng nó đã kém được hỗ trợ và đã chính thức bị loại bỏ trong C++ 11.
  • Trình diễn mẫu rõ ràng (xem here hoặc here) là cách tiếp cận đơn giản nhất, nếu bạn có thể dự đoán trước thời gian cần thiết (và nếu bạn không nhớ duy trì danh sách này).
  • Extern templates, đã được một số trình biên dịch hỗ trợ dưới dạng tiện ích mở rộng. Đó là sự hiểu biết của tôi rằng các mẫu bên ngoài không nhất thiết cho phép bạn di chuyển các định nghĩa mẫu ra khỏi tệp tiêu đề, nhưng chúng làm cho việc biên dịch và liên kết nhanh hơn (bằng cách giảm số lần mã mẫu phải được khởi tạo và liên kết).
  • Tùy thuộc vào thiết kế mẫu của bạn, bạn có thể di chuyển phần lớn sự phức tạp của nó vào tệp .cpp. Ví dụ tiêu chuẩn là một lớp mẫu vector an toàn loại chỉ bao bọc một loại véc tơ không an toàn loại void*; tất cả sự phức tạp trong vector void* nằm trong tệp .cpp. Scott Meyers đưa ra một ví dụ chi tiết hơn trong Hiệu quả C++ (mục 42, "Sử dụng quyền thừa kế riêng tư", trong ấn bản thứ hai).
+2

Mục 42: thích ngăn chặn hơn bất cứ lúc nào bạn có thể ... –

+1

Tôi không có ấn bản hiện tại. –

+0

"Sử dụng quyền thừa kế riêng tư" là 39 trong bản sao của tôi (ấn bản thứ 3), nhưng cảm ơn con trỏ. Tôi thực sự nên đọc lại Myers hai cuốn sách hiệu quả một lần nữa. –

5

Trước hết, để hoàn thành, tôi sẽ đề cập đến giải pháp đơn giản: chỉ sử dụng mã templated khi cần thiết và dựa trên mã không phải mẫu (với việc triển khai trong tệp nguồn của chính nó).

Tuy nhiên, tôi nghi ngờ rằng vấn đề thực sự là bạn sử dụng chương trình chung như bạn sẽ sử dụng chương trình OO điển hình và kết thúc với một lớp học cồng kềnh.

Hãy lấy một ví dụ:

// "bigArray/bigArray.hpp" 

template <class T, class Allocator> 
class BigArray 
{ 
public: 
    size_t size() const; 

    T& operator[](size_t index); 
    T const& operator[](size_t index) const; 

    T& at(size_t index); 
    T const& at(size_t index); 

private: 
    // impl 
}; 

Liệu này sốc bạn? Chắc là không. Nó có vẻ khá tối giản sau khi tất cả. Vấn đề là, nó không phải. Các phương pháp at có thể là yếu tố ra mà không mất tính tổng quát:

// "bigArray/at.hpp" 

template <class Container> 
typename Container::reference_type at(Container& container, 
             typename Container::size_type index) 
{ 
    if (index >= container.size()) throw std::out_of_range(); 
    return container[index]; 
} 

template <class Container> 
typename Container::const_reference_type at(Container const& container, 
              typename Container::size_type index) 
{ 
    if (index >= container.size()) throw std::out_of_range(); 
    return container[index]; 
} 

Được rồi, này thay đổi gọi nhẹ:

// From 
myArray.at(i).method(); 

// To 
at(myArray,i).method(); 

Tuy nhiên, nhờ tra cứu Koenig, bạn có thể gọi cho họ không đủ tiêu chuẩn miễn là bạn đặt chúng vào cùng một không gian tên, vì vậy nó chỉ là vấn đề của thói quen.

Ví dụ này được giả tạo nhưng điểm chung là viết tắt. Lưu ý rằng vì tính hào phóng của nó at.hpp không bao giờ phải bao gồm bigArray.hpp và vẫn sẽ tạo ra mã chặt chẽ như thể nó là một phương thức thành viên, nó chỉ là chúng ta có thể gọi nó trên các thùng chứa khác nếu chúng ta muốn.

Và bây giờ, một người sử dụng của BigArray không cần phải bao gồm at.hpp nếu cô ấy không sử dụng nó ... do đó làm giảm sự phụ thuộc của mình và không bị ảnh hưởng nếu bạn thay đổi mã trong tập tin đó: ví dụ thay đổi std::out_of_range cuộc gọi đến tính năng tên tệp và số dòng, địa chỉ của vùng chứa, kích thước của nó và chỉ mục mà chúng tôi đã cố gắng truy cập.

Ưu điểm khác (không rõ ràng) là nếu ràng buộc toàn vẹn của BigArray bị vi phạm, thì at rõ ràng là nguyên nhân vì nó không thể gây rối với nội bộ của lớp, do đó làm giảm số lượng nghi phạm.

này được khuyến khích bởi nhiều tác giả, chẳng hạn như Herb Sutters trong C++ Coding Standards:

mục 44: thích bằng văn bản chức năng nonfriend viên đệ

và đã được sử dụng rộng rãi trong Boost ... Nhưng bạn phải thay đổi thói quen viết mã của mình! Sau đó, tất nhiên bạn chỉ cần bao gồm những gì bạn làm phụ thuộc vào, có phải là tĩnh C + + phân tích mã mà báo cáo bao gồm nhưng tập tin tiêu đề không sử dụng mà có thể giúp tìm ra điều này.

+0

Cảm ơn bạn đã phản hồi, và bạn đang khá đúng về mã mẫu đang bị cồng kềnh. Tôi ban đầu dựa trên nó MFC CArray, mà tôi có kể từ khi phát hiện là morbidly béo phì! Trình phân tích mã tĩnh nào liệt kê các tiêu đề không được sử dụng? Tôi hiện đang sử dụng PC-LINT và chưa thấy tính năng đó. –

+0

Tôi nghĩ rằng nó sẽ là một điều phổ biến, nhưng tôi sợ tôi nhầm lẫn với ngôn ngữ khác:/Thật lạ vì (ví dụ) ngay cả bộ chỉnh màu C++ từ Eclipse kiểm tra quá tải (và chỉ tô màu nếu phát hiện quá tải chính xác). .. xin lỗi vì đã gây hiểu lầm cho bạn. –

+0

Tôi biết thủ thuật này và nó cũng được đề xuất bởi Scott Meyers. Tuy nhiên tôi nghĩ nó ảnh hưởng tiêu cực đến khả năng đọc mã của bạn. Ngoài ra visualisers như trong Visual Studio không nhận các chức năng toàn cầu, và những người có thể tăng tốc độ phát triển của bạn là tốt. Đồ thị tăng cường gần như được thiết kế hoàn toàn theo cách này (tức là các chức năng miễn phí hoạt động trên các khái niệm đồ thị trừu tượng) và mặc dù nó rất linh hoạt và mạnh mẽ nhưng rất khó để người mới bắt đầu sử dụng nó. – gast128

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