Có nên tránh STL trong các ứng dụng lớn?


24

Điều này có vẻ như là một câu hỏi kỳ lạ, nhưng trong bộ phận của tôi, chúng tôi đang gặp rắc rối với tình huống sau:

Chúng tôi đang làm việc ở đây trên một ứng dụng máy chủ, ngày càng lớn hơn, thậm chí tại thời điểm chúng tôi đang xem xét chia nó thành các phần khác nhau (tệp DLL), tải động khi cần và dỡ tải sau đó, để có thể xử lý các vấn đề hiệu suất.

Nhưng: các hàm chúng ta đang sử dụng, đang truyền tham số đầu vào và đầu ra dưới dạng các đối tượng STL và như được đề cập trong câu trả lời Stack Overflow , đây là một ý tưởng rất tồi. (Bài đăng có chứa một số giải pháp và hack, nhưng tất cả trông không chắc chắn lắm.)

Rõ ràng chúng ta có thể thay thế các tham số đầu vào / đầu ra bằng các loại C ++ tiêu chuẩn và tạo các đối tượng STL từ các đối tượng một lần bên trong các hàm, nhưng điều này có thể làm giảm hiệu suất.

Bạn có thể kết luận rằng, trong trường hợp bạn đang xem xét xây dựng một ứng dụng, có thể phát triển đến mức mà một PC duy nhất không thể xử lý được nữa, bạn không được sử dụng STL như một công nghệ?

Thông tin thêm về câu hỏi này:
Dường như có một số hiểu lầm về câu hỏi: vấn đề là như sau:
Ứng dụng của tôi đang sử dụng một lượng lớn hiệu năng (CPU, bộ nhớ) để hoàn thành công việc của mình và tôi muốn chia nhỏ công việc này vào các phần khác nhau (vì chương trình đã được chia thành nhiều hàm), không khó để tạo một số DLL ra khỏi ứng dụng của tôi và đưa một số hàm vào bảng xuất của các DLL đó. Điều này sẽ dẫn đến tình huống sau:

+-----------+-----------+----
| Machine1  | Machine2  | ...
| App_Inst1 | App_Inst2 | ...
|           |           |    
| DLL1.1    | DLL2.1    | ...
| DLL1.2    | DLL2.2    | ...
| DLL1.x    | DLL2.x    | ...
+-----------+-----------+----

App_Inst1 là phiên bản của ứng dụng, được cài đặt trên Machine1, trong khi App_Inst2 là phiên bản của cùng một ứng dụng, được cài đặt trên Machine2.
DLL1.x là một DLL, được cài đặt trên Machine1, trong khi DLL2.x là một DLL, được cài đặt trên Machine2.
DLLx.1 bao gồm chức năng xuất1.
DLLx.2 bao gồm chức năng xuất2.

Bây giờ trên Machine1 tôi muốn thực thi hàm1 và function2. Tôi biết rằng điều này sẽ quá tải Machine1, vì vậy tôi muốn gửi tin nhắn đến App_Inst2, yêu cầu ứng dụng đó thực hiện chức năng2.

Các tham số đầu vào / đầu ra của function1 và function2 là các đối tượng STL (Thư viện loại tiêu chuẩn C ++) và thường thì tôi có thể mong đợi khách hàng thực hiện cập nhật App_Inst1, App_Inst2, DLLx.y (nhưng không phải tất cả chúng, khách hàng có thể nâng cấp Machine1 nhưng không phải Machine2, hoặc chỉ nâng cấp các ứng dụng chứ không phải DLL hoặc ngược lại, ...). Rõ ràng nếu giao diện (tham số đầu vào / đầu ra) thay đổi, thì khách hàng buộc phải thực hiện nâng cấp hoàn chỉnh.

Tuy nhiên, như được đề cập trong URL StackOverflow được đề cập, việc biên dịch lại đơn giản App_Inst1 hoặc một trong các DLL có thể khiến toàn bộ hệ thống bị phá vỡ, do đó, tiêu đề ban đầu của bài đăng này, không khuyến khích sử dụng STL (Mẫu chuẩn C ++ Thư viện) cho các ứng dụng lớn.

Tôi hy vọng rằng tôi đã xóa một số câu hỏi / nghi ngờ.


44
Bạn có chắc chắn rằng bạn đang gặp vấn đề về hiệu suất vì kích thước thực thi của bạn ? Bạn có thể thêm một số chi tiết về việc liệu có thực tế không khi giả sử tất cả phần mềm của bạn được biên dịch với cùng một trình biên dịch (ví dụ: trong một lần trên máy chủ xây dựng) hoặc nếu bạn thực sự muốn tách thành các nhóm độc lập?
nvoigt

5
Về cơ bản, bạn cần một người có công việc chuyên dụng là "trình quản lý xây dựng" và "trình quản lý phát hành", để đảm bảo rằng tất cả các dự án C ++ đang được biên dịch trên cùng một phiên bản trình biên dịch và với các cài đặt trình biên dịch C ++ giống hệt nhau, được biên dịch từ một ảnh chụp (phiên bản) nhất quán của nguồn mã, vv Thông thường, điều này được quan tâm dưới biểu ngữ "tích hợp liên tục". Nếu bạn tìm kiếm trực tuyến, bạn sẽ tìm thấy rất nhiều bài viết và công cụ. Thực tiễn lỗi thời có thể tự củng cố - một thực tiễn lỗi thời có thể dẫn đến tất cả các thực tiễn đã lỗi thời.
rwong

8
Câu trả lời được chấp nhận trong câu hỏi được liên kết nói rằng vấn đề là với các cuộc gọi C ++ nói chung. Vì vậy, "C ++ nhưng không phải STL" không giúp ích gì, bạn cần đi với C trần để ở bên an toàn (nhưng cũng xem các câu trả lời, tuần tự hóa có thể là một giải pháp tốt hơn).
Frax

52
tải động khi cần và dỡ tải sau đó, để có thể xử lý các vấn đề về hiệu năng "Vấn đề về hiệu suất" là gì? Tôi không biết về bất kỳ vấn đề nào ngoài việc sử dụng quá nhiều bộ nhớ có thể khắc phục bằng cách dỡ bỏ những thứ như DLL khỏi bộ nhớ - và nếu đó là vấn đề dễ khắc phục nhất là chỉ cần mua thêm RAM. Bạn đã cấu hình ứng dụng của bạn để xác định tắc nghẽn hiệu suất thực tế? Bởi vì điều này nghe có vẻ như là một vấn đề XY - bạn có "vấn đề về hiệu suất" không xác định và ai đó đã quyết định giải pháp.
Andrew Henle

4
@MaxBarraclough "STL" hoàn toàn được chấp nhận như là một tên thay thế cho các thùng chứa templated và các chức năng đã được đưa vào Thư viện chuẩn C ++. Trên thực tế, Nguyên tắc cốt lõi C ++, được viết bởi Bjarne Stroustrup và Herb Sutter, liên tục đưa ra tham chiếu đến "STL" khi nói về những điều này. Bạn không thể có được một nguồn có thẩm quyền nhiều hơn thế.
Sean Burton

Câu trả lời:


110

Đây là một vấn đề XY cổ điển lạnh như đá.

Vấn đề thực sự của bạn là vấn đề hiệu suất. Tuy nhiên, câu hỏi của bạn cho thấy rõ rằng bạn đã không thực hiện hồ sơ hoặc đánh giá khác về vấn đề hiệu suất thực sự đến từ đâu. Thay vào đó, bạn hy vọng rằng việc chia mã của bạn thành DLL sẽ giải quyết vấn đề một cách kỳ diệu (điều này sẽ không xảy ra đối với hồ sơ), và bây giờ bạn lo lắng về một khía cạnh của giải pháp không phải đó.

Thay vào đó, bạn cần giải quyết vấn đề thực sự. Nếu bạn có nhiều tệp thực thi, hãy kiểm tra xem cái nào gây chậm. Trong khi bạn đang ở đó, hãy chắc chắn rằng chương trình của bạn thực sự chiếm hết thời gian xử lý và không phải là trình điều khiển Ethernet được cấu hình kém hoặc đại loại như thế. Và sau đó, bắt đầu cấu hình các tác vụ khác nhau trong mã của bạn. Bộ đếm thời gian chính xác cao là bạn của bạn ở đây. Giải pháp cổ điển là theo dõi thời gian xử lý trường hợp trung bình và trường hợp xấu nhất cho một đoạn mã.

Khi bạn có dữ liệu, bạn có thể tìm ra cách giải quyết vấn đề và sau đó bạn có thể tìm ra nơi để tối ưu hóa.


54
"Thay vào đó, bạn hy vọng rằng việc chia mã của bạn thành DLL sẽ giải quyết vấn đề một cách kỳ diệu (điều này sẽ không xảy ra đối với hồ sơ)" - +1 cho việc này. Hệ điều hành của bạn gần như chắc chắn thực hiện phân trang theo yêu cầu, đạt được kết quả chính xác giống như chức năng tải và dỡ tải trong DLL, chỉ tự động chứ không yêu cầu can thiệp thủ công. Ngay cả khi bạn dự đoán tốt hơn một đoạn mã sẽ được sử dụng trong bao lâu so với hệ thống bộ nhớ ảo của hệ điều hành (điều này thực sự không thể), thì HĐH sẽ lưu trữ tệp DLL và phủ nhận mọi nỗ lực của bạn .
Jules

@Jules Xem cập nhật - họ đã làm rõ rằng các DLL chỉ tồn tại trên các máy riêng biệt, vì vậy tôi có thể thấy giải pháp này hoạt động. Hiện tại có giao tiếp trên cao, rất khó để chắc chắn.
Izkata

2
@Izkata - vẫn chưa hoàn toàn rõ ràng, nhưng tôi nghĩ những gì được mô tả là họ muốn tự động chọn (dựa trên cấu hình thời gian chạy) một phiên bản của mỗi chức năng là cục bộ hoặc từ xa. Nhưng bất kỳ phần nào của tệp EXE không bao giờ được sử dụng trên một máy nhất định sẽ không bao giờ được tải vào bộ nhớ, vì vậy việc sử dụng DLL cho mục đích này là không cần thiết. Chỉ cần bao gồm cả hai phiên bản của tất cả các chức năng trong bản dựng tiêu chuẩn và tạo một bảng các con trỏ hàm (hoặc các đối tượng có thể gọi được C ++ hoặc bất kỳ phương thức nào bạn muốn) để gọi phiên bản phù hợp của từng chức năng.
Jules

38

Nếu bạn phải phân chia một phần mềm giữa nhiều máy vật lý, bạn phải có một số dạng tuần tự hóa khi truyền dữ liệu giữa các máy vì chỉ trong một số trường hợp, bạn thực sự có thể gửi cùng một nhị phân chính xác giữa các máy. Hầu hết các phương thức tuần tự hóa không có vấn đề gì khi xử lý các loại STL vì vậy trường hợp đó không phải là điều khiến tôi lo lắng.

Nếu bạn phải chia một ứng dụng thành Thư viện chia sẻ (DLL) (trước khi thực hiện điều đó vì lý do hiệu năng, bạn thực sự cần đảm bảo rằng nó thực sự sẽ giải quyết vấn đề hiệu năng của bạn) vượt qua các đối tượng STL có thể là một vấn đề nhưng không phải như vậy. Như liên kết bạn đã cung cấp đã mô tả, việc truyền các đối tượng STL hoạt động nếu bạn sử dụng cùng một trình biên dịch và cùng các cài đặt trình biên dịch. Nếu người dùng cung cấp DLL, bạn có thể không dễ dàng tin tưởng vào điều này. Tuy nhiên, nếu bạn cung cấp tất cả các DLL và biên dịch mọi thứ cùng nhau thì bạn có thể tin tưởng vào nó và sử dụng các đối tượng STL qua các ranh giới DLL trở nên rất có thể. Bạn vẫn phải coi chừng các cài đặt trình biên dịch để không nhận được nhiều đống khác nhau nếu bạn vượt qua quyền sở hữu đối tượng, mặc dù đó không phải là vấn đề cụ thể của STL.


1
Có, và đặc biệt là phần về việc chuyển các đối tượng được phân bổ qua ranh giới DLL / so. Nói chung, cách duy nhất để tránh hoàn toàn vấn đề nhiều cấp phát là đảm bảo rằng DLL / so (hoặc thư viện!) Đã phân bổ cấu trúc cũng giải phóng nó. Đó là lý do tại sao bạn thấy rất nhiều API kiểu C được viết theo cách này: một API miễn phí rõ ràng cho mỗi API trả lại một mảng / struct được phân bổ. Vấn đề bổ sung với STL là người gọi có thể mong đợi có thể sửa đổi cấu trúc dữ liệu phức tạp đã qua (thêm / loại bỏ các yếu tố) và điều đó cũng không được phép. Nhưng thật khó để thực thi.
davidbak

1
Nếu tôi phải tách một ứng dụng như thế này, có lẽ tôi sẽ sử dụng COM, nhưng điều này thường làm tăng kích thước mã vì mọi thành phần đều mang thư viện C và C ++ của riêng chúng (có thể được chia sẻ khi chúng giống nhau, nhưng có thể phân kỳ khi cần, ví dụ như trong quá trình chuyển đổi. Tuy nhiên, tôi không tin rằng đây là cách hành động thích hợp cho vấn đề của OP.
Simon Richter

2
Như một ví dụ cụ thể, chương trình rất có thể ở đâu đó muốn gửi một số văn bản đến một máy khác. Tại một số điểm, sẽ có một con trỏ tới một số ký tự liên quan đến việc thể hiện văn bản đó. Bạn hoàn toàn không thể truyền các bit của các con trỏ đó và mong đợi hành vi được xác định ở phía bên nhận
Caleth

20

Chúng tôi đang làm việc ở đây trên một ứng dụng máy chủ, ngày càng lớn hơn, thậm chí tại thời điểm chúng tôi đang xem xét chia nó thành các phần khác nhau (DLL), tải động khi cần và dỡ tải sau đó, để có thể xử lý vấn đề hiệu năng

RAM là rẻ và do đó mã không hoạt động là giá rẻ. Tải và dỡ mã (đặc biệt là dỡ tải) là một quá trình mong manh và không có khả năng ảnh hưởng đáng kể đến hiệu suất chương trình của bạn trên phần cứng máy tính để bàn / máy chủ hiện đại.

Bộ nhớ cache đắt hơn nhưng điều đó chỉ ảnh hưởng đến mã đang hoạt động gần đây, chứ không phải mã đang sử dụng trong bộ nhớ.

Trong các chương trình chung vượt trội so với máy tính của họ vì kích thước dữ liệu hoặc thời gian CPU, không phải kích thước mã. Nếu kích thước mã của bạn ngày càng lớn đến mức gây ra vấn đề lớn thì có lẽ bạn muốn xem tại sao điều đó lại xảy ra ở nơi đầu tiên.

Nhưng: các hàm chúng ta đang sử dụng, đang chuyển tham số đầu vào và đầu ra dưới dạng các đối tượng STL và như được đề cập trong URL StackOverflow này, đây là một ý tưởng rất tồi.

Sẽ ổn thôi miễn là các dll và thực thi đều được xây dựng với cùng một trình biên dịch và được liên kết động với cùng một thư viện thời gian chạy C ++. Theo sau, nếu ứng dụng và các dll liên quan được xây dựng và triển khai thành một đơn vị thì đó không phải là vấn đề.

Nơi nó có thể trở thành vấn đề là khi các thư viện được xây dựng bởi những người khác nhau hoặc có thể được cập nhật một cách riêng biệt.

Bạn có thể kết luận rằng, trong trường hợp bạn đang xem xét xây dựng một ứng dụng, có thể phát triển đến mức một PC duy nhất không thể xử lý được nữa, bạn không được sử dụng STL như một công nghệ?

Không hẳn vậy.

Khi bạn bắt đầu truyền bá một ứng dụng trên nhiều máy, bạn có toàn bộ cân nhắc về cách bạn truyền dữ liệu giữa các máy đó. Các chi tiết về việc loại STL hoặc các loại cơ bản hơn được sử dụng có khả năng bị mất trong tiếng ồn.


2
Mã không hoạt động có thể không bao giờ được tải vào RAM ở nơi đầu tiên. Hầu hết các hệ điều hành chỉ tải các trang từ các tệp thực thi nếu chúng thực sự cần thiết.
Jules

1
@Jules: Nếu mã chết được trộn với mã trực tiếp (với kích thước trang = 4k độ chi tiết), thì nó sẽ được ánh xạ + tải. Bộ nhớ cache hoạt động trên mức độ chi tiết cao hơn (64B), do đó, hầu như đúng là các chức năng không được sử dụng không gây hại nhiều. Tuy nhiên, mỗi trang cần một mục TLB và (không giống như RAM) là tài nguyên thời gian chạy khan hiếm. (Ánh xạ tập tin hậu thuẫn thường không sử dụng hugepages, ít nhất là không phải trên Linux; Một hugepage là 2MiB trên x86-64, vì vậy bạn có thể bao gồm mã một nhiều hơn hoặc dữ liệu mà không nhận được bất kỳ bỏ lỡ TLB với hugepages.)
Peter Cordes

1
Những gì @PeterCordes lưu ý: Vì vậy, hãy chắc chắn sử dụng bản PG PG của Hồi giáo như một phần của quy trình xây dựng để phát hành của bạn!
JDługosz

13

Không, tôi không nghĩ rằng kết luận sau. Ngay cả khi chương trình của bạn được phân phối trên nhiều máy, không có lý do gì mà việc sử dụng STL trong nội bộ buộc bạn phải sử dụng nó trong giao tiếp giữa các mô-đun / quy trình.

Trên thực tế, tôi cho rằng bạn nên tách biệt thiết kế giao diện bên ngoài khỏi triển khai bên trong ngay từ đầu, vì trước đây sẽ cứng hơn / khó thay đổi so với những gì được sử dụng trong nội bộ


7

Bạn đang thiếu điểm của câu hỏi đó.

Về cơ bản có hai loại DLL. Của riêng bạn, và của người khác. "Vấn đề STL" là bạn và họ có thể không sử dụng cùng một trình biên dịch. Rõ ràng, đó không phải là vấn đề đối với DLL của riêng bạn.


5

Nếu bạn xây dựng các DLL từ cùng một cây nguồn cùng một lúc với cùng một trình biên dịch và các tùy chọn xây dựng, thì nó sẽ hoạt động tốt.

Tuy nhiên, cách "chia sẻ hương vị Windows" để chia một ứng dụng thành nhiều phần, một số trong đó có thể sử dụng lại là các thành phần COM . Chúng có thể nhỏ (điều khiển riêng hoặc codec) hoặc lớn (IE có sẵn dưới dạng điều khiển COM, trong mshtml.dll).

tải động khi cần và dỡ tải sau đó

Đối với một ứng dụng máy chủ, điều này có thể sẽ có hiệu quả khủng khiếp ; nó chỉ thực sự khả thi khi bạn có một ứng dụng di chuyển qua nhiều giai đoạn trong một khoảng thời gian dài để bạn biết khi nào một thứ gì đó sẽ không cần thiết nữa. Nó làm tôi nhớ các trò chơi DOS sử dụng cơ chế lớp phủ.

Ngoài ra, nếu hệ thống bộ nhớ ảo của bạn hoạt động tốt, nó sẽ xử lý việc này cho bạn bằng cách phân trang các trang mã không sử dụng.

có thể phát triển lớn đến mức một PC duy nhất không thể xử lý được nữa

Mua một PC lớn hơn.

Đừng quên rằng với việc tối ưu hóa đúng, một chiếc máy tính xách tay có thể vượt trội hơn một cụm hadoop.

Nếu bạn thực sự cần nhiều hệ thống, bạn phải suy nghĩ rất kỹ về ranh giới giữa chúng, vì đó là chi phí nối tiếp. Đây là nơi bạn nên bắt đầu xem xét các khung như MPI.


1
"nó chỉ thực sự khả thi khi bạn có một ứng dụng di chuyển qua nhiều giai đoạn trong một khoảng thời gian dài để bạn biết khi nào sẽ không cần thiết nữa" - thậm chí sau đó không thể giúp được gì nhiều, bởi vì HĐH sẽ lưu trữ các tập tin DLL, có khả năng cuối cùng sẽ chiếm nhiều bộ nhớ hơn là chỉ bao gồm các chức năng trực tiếp trong cơ sở thực thi của bạn. Lớp phủ chỉ hữu ích trong các hệ thống không có bộ nhớ ảo hoặc khi không gian địa chỉ ảo là yếu tố giới hạn (tôi cho rằng ứng dụng này là 64 bit, không phải 32 ...).
Jules

3
"Mua một PC lớn hơn" +1. Bây giờ bạn có thể có được các hệ thống có nhiều terabyte RAM. Bạn có thể thuê một người từ Amazon với mức giá thấp hơn hàng giờ của một nhà phát triển. Bạn sẽ dành bao nhiêu thời gian cho nhà phát triển để tối ưu hóa mã của mình để giảm mức sử dụng bộ nhớ?
Jules

2
Vấn đề lớn nhất mà tôi gặp phải với "mua một PC lớn hơn" có liên quan đến câu hỏi "ứng dụng của bạn sẽ mở rộng bao xa?". Câu trả lời của tôi là "bạn sẵn sàng chi bao nhiêu cho một bài kiểm tra? Bởi vì tôi hy vọng nó sẽ mở rộng quy mô cho đến nay, việc thuê một chiếc máy phù hợp và thiết lập một bài kiểm tra lớn đúng cách sẽ tiêu tốn hàng ngàn đô la. với những gì một PC CPU đơn có thể làm. ". Nhiều lập trình viên lớn tuổi không có ý tưởng thực tế về việc PC đã trưởng thành bao nhiêu; videocard một mình trong PC hiện đại là một siêu máy tính theo tiêu chuẩn của thế kỷ 20.
MSalters

Thành phần COM? Trong những năm 1990 có thể, nhưng bây giờ?
Peter Mortensen

@MSalters - phải ... bất kỳ ai có bất kỳ câu hỏi nào về việc ứng dụng có thể mở rộng bao xa trên một PC nên xem thông số kỹ thuật cho loại đối tượng Amazon EC2 x1e.32xlarge - tổng số 72 lõi xử lý vật lý trong máy cung cấp 128 lõi ảo 2.3GHz (có thể bùng nổ đến 3.1GHz), có khả năng băng thông bộ nhớ 340GB / giây (tùy thuộc vào loại bộ nhớ được cài đặt, không được mô tả trong thông số kỹ thuật) và RAM 3.9TiB. Nó có đủ bộ nhớ cache để chạy hầu hết các ứng dụng mà không cần chạm vào RAM chính. Ngay cả khi không có GPU, nó vẫn mạnh như cụm siêu máy tính 500 nút từ năm 2000.
Jules

0

Chúng tôi đang làm việc ở đây trên một ứng dụng máy chủ, ngày càng lớn hơn, thậm chí tại thời điểm chúng tôi đang xem xét chia nó thành các phần khác nhau (tệp DLL), tải động khi cần và dỡ tải sau đó, để có thể xử lý các vấn đề hiệu suất.

Phần đầu tiên có ý nghĩa (chia ứng dụng cho các máy khác nhau, vì lý do hiệu suất).

Phần thứ hai (tải và dỡ thư viện) không có ý nghĩa gì, vì đây là nỗ lực thêm để thực hiện và nó sẽ không (thực sự) cải thiện mọi thứ.

Vấn đề bạn đang mô tả được giải quyết tốt hơn với các máy tính chuyên dụng, nhưng chúng sẽ không hoạt động với cùng một ứng dụng (chính).

Giải pháp cổ điển trông như thế này:

[user] [front-end] [machine1] [common resources]
                   [machine2]
                   [machine3]

Giữa các máy tính đầu cuối và máy tính, bạn có thể có những thứ bổ sung, chẳng hạn như bộ cân bằng tải và giám sát hiệu suất, và xử lý chuyên dụng trên các máy chuyên dụng rất tốt cho việc tối ưu hóa bộ nhớ cache và thông lượng.

Điều này không có nghĩa là tải / dỡ tải thêm DLL, cũng như không có gì để làm với STL.

Đó là, sử dụng STL trong nội bộ theo yêu cầu và tuần tự hóa dữ liệu của bạn giữa các yếu tố (xem grpc và bộ đệm giao thức và loại vấn đề chúng giải quyết).

Điều này cho biết, với thông tin hạn chế mà bạn cung cấp, điều này trông giống như vấn đề xy cổ điển (như @Graham đã nói).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.