Viết bằng C cho hiệu suất? [đóng cửa]


32

Tôi biết rằng tôi thường xuyên nghe nói rằng C thường có lợi thế về hiệu năng so với C ++. Tôi thực sự không nghĩ gì khác về nó cho đến khi tôi nhận ra rằng MSVC thậm chí dường như không hỗ trợ tiêu chuẩn C mới nhất, nhưng mới nhất là nó hỗ trợ C99 (theo như tôi biết).

Tôi đã lên kế hoạch viết một thư viện với một số mã để kết xuất trong OpenGL để tôi có thể sử dụng lại nó. Tôi đã dự định viết thư viện bằng C vì bất kỳ sự gia tăng hiệu suất nào đều được hoan nghênh khi nói đến đồ họa.

Nhưng nó sẽ thực sự có giá trị nó? Mã sử ​​dụng thư viện có thể sẽ được viết bằng C ++ và tôi thích viết mã trong C ++ hơn.

Tuy nhiên, nếu nó tạo ra một sự khác biệt nhỏ trong hiệu suất, tôi có thể sẽ đi với C.

Cũng có thể lưu ý rằng thư viện này sẽ là thứ mà tôi sẽ làm để hoạt động trên Windows / OS X / Linux và tôi có thể sẽ biên dịch mọi thứ nguyên bản (MSVC cho Windows, Clang hoặc GCC cho OS X và GCC cho Linux .. .hoặc có thể là trình biên dịch của Intel cho mọi thứ).

Tôi đã nhìn xung quanh và tôi đã tìm thấy một số điểm chuẩn và như vậy, nhưng tất cả mọi thứ tôi thấy đã xử lý GCC thay vì MSVC và Clang. Ngoài ra, điểm chuẩn không đề cập đến các tiêu chuẩn của ngôn ngữ được sử dụng. Có ai có suy nghĩ nào về vấn đề này nữa ko?

CHỈNH SỬA:Tôi chỉ muốn chia sẻ quan điểm của mình về câu hỏi này sau một vài năm kinh nghiệm. Tôi đã kết thúc việc viết dự án mà tôi đã hỏi câu hỏi này trong C ++. Tôi đã bắt đầu một dự án khác cùng thời điểm tại C khi chúng tôi đang tìm cách đạt được bất kỳ hiệu suất nhỏ nào chúng tôi có thể và cần dự án có thể liên kết được trong C. Một vài tháng trước, tôi đã đạt đến điểm mà tôi thực sự cần bản đồ và nâng cao thao tác chuỗi. Tôi biết các khả năng này trong thư viện chuẩn C ++ và cuối cùng đi đến kết luận rằng các cấu trúc đó trong thư viện chuẩn có thể sẽ hoạt động tốt hơn và ổn định hơn các bản đồ và chuỗi tôi có thể thực hiện trong C trong một khoảng thời gian hợp lý. Yêu cầu có thể liên kết trong C dễ dàng được thỏa mãn bằng cách viết giao diện C sang mã C ++, được thực hiện nhanh chóng với các loại mờ. Viết lại thư viện trong C ++ dường như nhanh hơn nhiều so với khi viết nó bằng C và ít bị lỗi hơn, đặc biệt là rò rỉ bộ nhớ. Tôi cũng có thể sử dụng thư viện luồng thư viện tiêu chuẩn, việc này dễ dàng hơn nhiều so với việc sử dụng các triển khai dành riêng cho nền tảng. Cuối cùng, tôi tin rằng việc viết thư viện bằng C ++ đã mang lại lợi ích lớn với chi phí hiệu năng nhỏ. Tôi chưa điểm chuẩn phiên bản C ++, nhưng tôi tin rằng thậm chí có thể tôi đã đạt được một số hiệu suất bằng cách sử dụng các cấu trúc dữ liệu thư viện tiêu chuẩn so với các cấu trúc tôi đã viết. Tôi tin rằng việc viết thư viện bằng C ++ đã mang lại lợi ích lớn với chi phí hiệu năng nhỏ. Tôi chưa điểm chuẩn phiên bản C ++, nhưng tôi tin rằng thậm chí có thể tôi đã đạt được một số hiệu suất bằng cách sử dụng các cấu trúc dữ liệu thư viện tiêu chuẩn so với các cấu trúc tôi đã viết. Tôi tin rằng việc viết thư viện bằng C ++ đã mang lại lợi ích lớn với chi phí hiệu năng nhỏ. Tôi chưa điểm chuẩn phiên bản C ++, nhưng tôi tin rằng thậm chí có thể tôi đã đạt được một số hiệu suất bằng cách sử dụng các cấu trúc dữ liệu thư viện tiêu chuẩn so với các cấu trúc tôi đã viết.


9
Các hỗ trợ MSVC mới nhất thực sự là C89.
gièm pha

4
@detly Trong Visual Studio 2013, phần lớn các tính năng của C99 được hỗ trợ . Nó không hỗ trợ đầy đủ, nhưng tôi thực tế muốn sử dụng nó để viết C99.
congusbongus

4
@ danielu13 - Chính xác thì bạn đã nghe nói rằng C có lợi thế về hiệu suất so với C ++ ở đâu?
Ramhound

1
@ Sebastian-LaurenţiuPlesciuc Tôi không nghĩ những liên kết đó thực sự hữu ích. Câu hỏi đầu tiên có thể được phản hồi tốt với gần như cùng một lập trình viên câu hỏi.stackexchange.com/q/113295/76444 nhưng ủng hộ c ++ thay vì c như trong liên kết của bạn. Đối với liên kết thứ 2 của bạn, nó chỉ là một cơn thịnh nộ của linus torvalds. Tôi hy vọng mọi người đều biết rằng anh ấy thực sự thích ghét c ++ và sẽ không chạm vào nó bằng gậy nhưng những lời ca ngợi của anh ấy về c ++ hầu như không khách quan, họ đầy quan điểm cá nhân và thiên vị và không thực sự phản ánh thực tế của ngôn ngữ . Ít nhất đó là ý kiến của tôi .
dùng1942027

1
@RaphaelMiedl Ngoài ra tôi đã đề cập đến điều này được viết vào năm 2007 cách đây khá lâu, trình biên dịch C ++ và ngôn ngữ C ++ đã phát triển kể từ đó. Bất kể, tùy thuộc vào lập trình viên để chọn ngôn ngữ để sử dụng.
Sebastian-Laurenţiu Plesciuc

Câu trả lời:


89

Tôi đoán mọi người thường cho rằng C nhanh hơn C ++ vì lý do hiệu suất trong C. C ++ không phải là chậm hơn hoặc nhanh hơn, nhưng mã C ++ nhất định có thể che khuất các hình phạt hiệu suất ẩn. Ví dụ: có thể có các bản sao và chuyển đổi ngầm định không thể nhìn thấy ngay lập tức khi xem một số đoạn mã C ++.

Hãy đưa ra tuyên bố sau:

foo->doSomething(a + 5, *c);

Chúng ta hãy giả sử rằng doSomethingcó chữ ký sau:

void doSomething(int a, long b);

Bây giờ, hãy thử phân tích tác động hiệu suất có thể có của tuyên bố cụ thể này.

Trong C, các hàm ý khá rõ ràng. foochỉ có thể là một con trỏ tới một cấu trúc và doSomethingphải là một con trỏ tới một hàm. *cdereferences một dài, và a + 5là bổ sung số nguyên. Sự không chắc chắn duy nhất đến từ loại a: Nếu không phải là int, sẽ có một số chuyển đổi. nhưng ngoài ra, thật dễ dàng để định lượng tác động hiệu suất của tuyên bố này.

Bây giờ hãy chuyển sang C ++. Tuyên bố tương tự bây giờ có thể có các đặc tính hiệu suất rất khác nhau:

  1. doSomethingcó thể là một hàm thành viên không ảo (giá rẻ), hàm thành viên ảo (đắt hơn một chút) std::function, lambda ... vv Điều tồi tệ hơn, foocó thể là một loại lớp quá tải operator->với một số hoạt động phức tạp không rõ ràng. Vì vậy, để định lượng chi phí gọi điện doSomething, giờ đây cần phải biết chính xác bản chất của foodoSomething.
  2. acó thể là một số nguyên hoặc một tham chiếu đến một số nguyên (bổ sung bổ sung) hoặc một loại lớp thực hiện operator+(int). Toán tử thậm chí có thể trả về một loại lớp khác có thể chuyển đổi hoàn toàn thành int. Một lần nữa, chi phí hiệu suất không rõ ràng từ tuyên bố một mình.
  3. ccó thể là một loại thực hiện lớp operator*(). Nó cũng có thể là một tài liệu tham khảo cho một long*vv

Bạn nhận được hình ảnh. Do các tính năng ngôn ngữ của C ++, việc định lượng chi phí hiệu năng của một câu lệnh khó hơn nhiều so với C. Bây giờ, ngoài ra, các khái niệm trừu tượng như std::vector, std::stringthường được sử dụng trong C ++, có đặc tính hiệu năng của riêng chúng và ẩn phân bổ bộ nhớ động ( cũng xem câu trả lời của @ Ian).

Vì vậy, điểm mấu chốt là: Nói chung, không có sự khác biệt nào về hiệu suất có thể đạt được bằng cách sử dụng C hoặc C ++. Nhưng đối với mã thực sự quan trọng về hiệu năng, mọi người thường thích sử dụng C vì có nhiều hình phạt hiệu năng ẩn ít khả thi hơn.


1
Câu trả lời tuyệt vời. Đây là những gì tôi đã ám chỉ trong câu trả lời của tôi, nhưng bạn đã giải thích nó tốt hơn nhiều.
Ian Goldby

4
Đây thực sự nên là câu trả lời được chấp nhận. Nó giải thích tại sao các câu như "C nhanh hơn C ++" tồn tại. C có thể nhanh hơn hoặc chậm hơn C ++, nhưng thường thì dễ hiểu hơn tại sao một đoạn mã C cụ thể lại nhanh / chậm, điều này cũng thường giúp dễ dàng tối ưu hóa hơn.
Leo

Không lấy bất cứ điều gì từ câu trả lời xuất sắc này (trong đó +1 từ tôi), nhưng (các) trình biên dịch có thể là táo và cam trong so sánh này. Nó / họ có thể tạo mã giống hệt nhau cho C so với C ++ hoặc không. Tất nhiên điều tương tự có thể được nói cho bất kỳ hai trình biên dịch hoặc tùy chọn trình biên dịch, thậm chí khi biên dịch chương trình vật lý giống nhau với các giả định ngôn ngữ nguồn giống nhau.
JRobert

4
Tôi sẽ nói thêm rằng hầu hết thời gian chạy của C ++ là rất lớn so với thời gian chạy C tương đương, sẽ rất quan trọng nếu bạn bị hạn chế về bộ nhớ.
James Anderson

@JamesAnderson Nếu bạn thực sự bộ nhớ hạn chế, có thể bạn không cần một thời gian chạy ở tất cả :)
Navin

30

Mã được viết bằng C ++ có thể nhanh hơn trong C, đối với một số loại tác vụ nhất định.

Nếu bạn thích C ++, hãy sử dụng C ++. Bất kỳ vấn đề hiệu suất nào cũng sẽ không đáng kể so với các quyết định thuật toán của phần mềm của bạn.


6
Nó có thể nhanh hơn nhưng nó có thể không nhanh hơn vì cùng một lý do.
Cướp

Bạn có thể đưa ra một số ví dụ về mã tối ưu hóa được viết bằng C ++ nhanh hơn C được tối ưu hóa không?

1
@TomDworzanski: một ví dụ là bằng cách sử dụng các mẫu, quyết định về đường dẫn mã có thể được xác định tại thời điểm biên dịch và kết thúc mã hóa cứng trong nhị phân cuối cùng, thay vì điều kiện và phân nhánh như được yêu cầu nếu nó được viết bằng c, cũng như khả năng để tránh các cuộc gọi chức năng thông qua nội tuyến.
whatsisname

23

Một trong những nguyên tắc thiết kế của C ++ là bạn không trả tiền cho các tính năng bạn không sử dụng. Vì vậy, nếu bạn viết mã bằng C ++ và tránh các tính năng không tồn tại trong C, thì mã được biên dịch kết quả sẽ tương đương về hiệu suất (mặc dù bạn sẽ phải đo lường điều này).

Có chi phí không đáng kể để sử dụng các lớp, ví dụ, so với các cấu trúc và một loạt các chức năng liên quan. Các chức năng ảo sẽ có giá cao hơn một chút và bạn phải đo hiệu suất để xem liệu nó có quan trọng với ứng dụng của bạn hay không. Điều tương tự cũng xảy ra với bất kỳ tính năng ngôn ngữ C ++ nào khác.


3
Chi phí gửi chức năng ảo chỉ là không đáng kể, trừ khi bạn đã quá mức trong việc phân tách và biến mọi thứ thành ảo. Các vtables sẽ nhỏ so với phần còn lại của mã và dữ liệu của bạn và nhánh được lập chỉ mục thông qua vtable sẽ thêm một vài đồng hồ vào mỗi lệnh gọi thông thường. Cho rằng các yêu cầu thông thường, tất cả, gọi để trở về, sẽ ở bất cứ đâu từ vài trăm đến vài triệu đồng hồ, chi nhánh vtable sẽ bị chôn vùi trong sàn tiếng ồn.
John R. Strohm

6
Cấu trúc là các lớp trong C ++.
đúng vào

2
@rightprint: Chắc chắn, nhưng bạn vẫn có thể viết mã C ++ đi xung quanh các con trỏ tới các cấu trúc mà không cần sử dụng thistính năng ngôn ngữ con trỏ. Đó là tất cả những gì tôi đã nói.
Greg Hewgill

4
@John Chi phí thực tế không phải là sự quyết định (mặc dù tôi khá chắc chắn rằng điều này cũng sẽ hơi khó khăn với một số bộ xử lý tìm nạp trước), nhưng thực tế là bạn không thể thực hiện các chức năng ảo (ít nhất là trong C ++), điều này không cho phép nhiều tối ưu hóa. Và vâng, điều đó có thể có ảnh hưởng rất lớn đến hiệu suất.
Voo

2
@Voo Để công bằng, có thể nói tương tự về mã C tương đương (mã mô phỏng đa hình thời gian chạy thủ công). Sự khác biệt lớn nhất là tôi tin rằng trình biên dịch sẽ dễ dàng hơn để xác định xem hàm nói có thể được nội tuyến trong C ++ hay không.
Thomas Eding

14

Một lý do mà các ngôn ngữ cấp cao hơn đôi khi chậm hơn là chúng có thể ẩn đằng sau hậu trường quản lý bộ nhớ nhiều hơn so với các ngôn ngữ cấp thấp hơn.

Bất kỳ ngôn ngữ nào (hoặc thư viện, API, v.v.) trừu tượng hóa chi tiết cấp thấp đều có khả năng che giấu các hoạt động tốn kém. Ví dụ, trong một số ngôn ngữ, chỉ cần cắt bớt khoảng trắng theo dõi từ một chuỗi dẫn đến việc cấp phát bộ nhớ và bản sao của chuỗi. Phân bổ bộ nhớ và sao chép đặc biệt có thể trở nên đắt đỏ nếu chúng xảy ra lặp đi lặp lại trong một vòng lặp chặt chẽ.

Nếu bạn đã viết loại mã này trong C, nó sẽ rõ ràng rõ ràng. Trong C ++ có thể ít hơn như vậy, bởi vì việc phân bổ và sao chép có thể được trừu tượng hóa thành một lớp ở đâu đó. Chúng thậm chí có thể được ẩn đằng sau một toán tử quá tải trông có vẻ vô tội hoặc hàm tạo sao chép.

Vì vậy, sử dụng C ++ nếu bạn muốn. Nhưng đừng bao biện bởi sự tiện lợi có vẻ trừu tượng khi bạn không biết những gì nằm bên dưới chúng.

Tất nhiên, sử dụng một trình lược tả để tìm hiểu điều gì thực sự làm chậm mã của bạn.


5

Để biết giá trị của nó, tôi có xu hướng viết các thư viện của mình trong C ++ 11 cho bộ tính năng nâng cao. Tôi thích có thể tận dụng những thứ như con trỏ chia sẻ, ngoại lệ, lập trình chung và các tính năng chỉ C ++ khác. Tôi thích C ++ 11 vì tôi thấy rằng một chút tốt của nó được hỗ trợ trên tất cả các nền tảng tôi quan tâm. Visual Studio 2013 có rất nhiều tính năng ngôn ngữ cốt lõi và triển khai thư viện đã sẵn sàng và được cho là đang làm việc để thêm phần còn lại. Như bạn đã biết, Clang và GCC đều hỗ trợ toàn bộ tính năng.

Như đã nói, gần đây tôi đã đọc về một chiến lược thực sự tuyệt vời liên quan đến phát triển thư viện mà tôi nghĩ là có liên quan trực tiếp đến truy vấn của bạn. Bài viết có tiêu đề "Phong cách xử lý lỗi AC hoạt động tốt với các ngoại lệ C ++" Stefanu Du Toit đề cập đến chiến lược này như một mô hình "đồng hồ cát". Đoạn đầu tiên của bài viết:

Tôi đã viết rất nhiều mã thư viện bằng cách sử dụng mẫu mà tôi gọi là mẫu "đồng hồ cát": Tôi triển khai một thư viện (trong trường hợp của tôi, thông thường, sử dụng C ++), bọc nó trong API C trở thành điểm truy cập duy nhất vào thư viện, sau đó bọc API C đó trong C ++ hoặc một số ngôn ngữ khác để cung cấp một cú pháp trừu tượng và tiện lợi. Khi nói đến mã đa nền tảng riêng, API C cung cấp độ ổn định ABI vô song và tính di động cho các ngôn ngữ khác thông qua FFI. Tôi thậm chí còn giới hạn API ở một tập hợp con C mà tôi biết là có thể mang theo nhiều loại FFI và bảo vệ thư viện khỏi rò rỉ các thay đổi trong cấu trúc dữ liệu nội bộ - mong đợi nhiều hơn về các bài đăng trên blog trong tương lai.


Bây giờ để giải quyết mối quan tâm chính của bạn: hiệu suất.

Tôi muốn đề xuất, giống như nhiều câu trả lời khác ở đây, viết mã bằng một trong hai ngôn ngữ sẽ hoạt động tốt trên quan điểm hiệu suất. Từ quan điểm cá nhân, tôi thấy việc viết mã chính xác trong C ++ sẽ dễ dàng hơn nhờ các tính năng ngôn ngữ, nhưng tôi nghĩ đó là một sở thích cá nhân. Dù bằng cách nào, trình biên dịch thực sự thông minh và có xu hướng viết mã tốt hơn bạn. Điều đó để nói rằng trình biên dịch sẽ có khả năng tối ưu hóa mã của bạn tốt hơn bạn có thể.

Tôi biết rất nhiều lập trình viên nói điều này, nhưng điều đầu tiên bạn nên làm là viết mã của mình, sau đó lập hồ sơ và thực hiện tối ưu hóa nơi trình hồ sơ của bạn gợi ý bạn làm. Thời gian của bạn sẽ được dành nhiều hơn để sản xuất các tính năng và sau đó tối ưu hóa nó một khi bạn có thể thấy các nút thắt của bạn đang ở đâu.


Bây giờ cho một số bài đọc thú vị về cách các tính năng ngôn ngữ và tối ưu hóa có thể thực sự hoạt động có lợi cho bạn:

std :: unique_ptr không có chi phí

constEx cho phép tính toán thời gian biên dịch

di chuyển ngữ nghĩa ngăn chặn các đối tượng tạm thời không cần thiết


std::unique_ptr has zero overheadĐiều này có thể không đúng (nói về mặt kỹ thuật) bởi vì nó phải có hàm tạo của nó được gọi nếu ngăn xếp thư giãn do một ngoại lệ. Một con trỏ thô không có chi phí này và vẫn đúng nếu mã của bạn có thể sẽ không ném. Một trình biên dịch sẽ không thể chứng minh điều này trong trường hợp chung.
Thomas Eding

2
@ThomasEding Tôi đã đề cập đến kích thước và chi phí thời gian chạy liên quan đến mã không có ngoại lệ. Sửa lỗi cho tôi nếu tôi sai, nhưng có những mô hình thực thi không có chi phí thời gian chạy bằng 0 khi ngoại lệ không bị ném mà vẫn cho phép ngoại lệ được lan truyền khi cần thiết. Mặc dù vậy, khi nào một ngoại lệ có thể được ném vào hàm tạo của unique_ptr? Nó được tuyên bố noexcept, và ít nhất là nó xử lý tất cả các ngoại lệ, nhưng tôi không thể tưởng tượng loại ngoại lệ nào thậm chí có thể được ném ở vị trí đầu tiên.
vmrob

vmrob: Xin lỗi tôi ... Tôi có nghĩa là viết "hàm hủy" thay vì "hàm tạo". Tôi cũng có nghĩa là viết "chắc chắn sẽ không ném" quá. Eeek!
Thomas Eding

2
@ThomasEding Bạn biết đấy, tôi không nghĩ nó thậm chí còn quan trọng nếu kẻ hủy diệt ném ngoại lệ. Chừng nào kẻ hủy diệt không đưa ra bất kỳ ngoại lệ mới nào, thì nó vẫn là sự hủy diệt trên không. Hơn nữa, tôi tin rằng toàn bộ hàm hủy được gắn vào một cuộc gọi xóa / miễn phí duy nhất với tối ưu hóa.
vmrob

4

Sự khác biệt về hiệu năng giữa C ++ và C không phải do bất kỳ thứ gì trong ngôn ngữ, nói đúng ra, mà là ở những gì nó cám dỗ bạn làm. Nó giống như một thẻ tín dụng so với tiền mặt. Nó không khiến bạn chi tiêu nhiều hơn, nhưng dù sao thì bạn cũng vậy, trừ khi bạn rất kỷ luật.

Đây là một ví dụ về một chương trình được viết bằng C ++, sau đó được điều chỉnh hiệu năng mạnh mẽ. Bạn cần biết làm thế nào để điều chỉnh hiệu suất tích cực, bất kể ngôn ngữ. Phương pháp tôi sử dụng là tạm dừng ngẫu nhiên, như được hiển thị trong video này .

Các loại công việc tốn kém mà C ++ cám dỗ bạn phải làm là quản lý bộ nhớ quá mức, lập trình kiểu thông báo, tin tưởng chương trình của bạn vào các thư viện trừu tượng nhiều lớp (như @Ian đã nói), ẩn chậm, v.v.


2

C không có bất kỳ lợi thế về hiệu suất so với C ++ nếu bạn làm những điều tương tự ở cả hai ngôn ngữ. Bạn có thể lấy bất kỳ mã C cũ nào được viết bởi bất kỳ lập trình viên C giỏi nào và biến nó thành mã C ++ hợp lệ và tương đương, mã này sẽ chạy nhanh như vậy (trừ khi cả bạn và trình biên dịch của bạn đều biết từ khóa "hạn chế" làm gì và bạn sử dụng nó một cách hiệu quả, nhưng hầu hết mọi người không).

C ++ có thể có hiệu năng cực kỳ khác nhau, chậm hơn hoặc nhanh hơn, nếu (1) bạn sử dụng thư viện C ++ tiêu chuẩn để thực hiện những việc có thể được thực hiện nhanh hơn và dễ dàng hơn mà không cần sử dụng thư viện hoặc (2) nếu bạn sử dụng thư viện C ++ tiêu chuẩn để làm mọi thứ dễ dàng hơn và nhanh hơn bằng cách thực hiện lại thư viện trong C.


1
điều này dường như không cung cấp bất cứ điều gì đáng kể so với những gì đã được giải thích trong 6 câu trả lời trước
gnat

Tôi nghĩ rằng câu trả lời này đề cập đến một điểm quan trọng mà không ai khác đã đề cập. Thoạt nhìn có vẻ như C ++ là một superset của C, vì vậy nếu bạn có thể viết một triển khai C nhanh thì bạn sẽ có thể viết một triển khai C ++ tương đương. Tuy nhiên, C99 hỗ trợ từ khóa hạn chế cho phép tránh răng cưa con trỏ ngoài ý muốn. C ++ không có hỗ trợ như vậy. Khả năng tránh răng cưa con trỏ là một tính năng quan trọng của Fortran giúp nó hữu ích cho các ứng dụng hiệu suất cao. Tôi hy vọng cũng có thể đạt được hiệu suất tốt hơn so với C99 so với C ++ trong các miền tương tự.
dùng27539
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.