Có lập trình mô-đun ảnh hưởng đến thời gian tính toán?


19

Mọi người nói rằng tôi nên biến mã của mình thành mô-đun, nhưng không hiệu quả hơn nếu tôi sử dụng nhiều cuộc gọi phương thức hơn là phương thức ít hơn, nhưng lớn hơn? Sự khác biệt trong Java, C hoặc C ++ cho vấn đề đó là gì?

Tôi hiểu rằng việc chỉnh sửa, đọc và hiểu dễ dàng hơn, đặc biệt là trong một nhóm. Vì vậy, mất thời gian tính toán không đáng kể so với các lợi ích gọn gàng mã?


2
Câu hỏi đặt ra là sẽ mất bao lâu cho thời gian xử lý mà bạn tiết kiệm vượt qua thời gian dành cho việc bảo trì khó khăn hơn. Câu trả lời phụ thuộc hoàn toàn vào ứng dụng của bạn.
Blrfl

2
Nhiều câu hỏi hay tạo ra một số mức độ ý kiến ​​dựa trên kinh nghiệm của chuyên gia, nhưng câu trả lời cho câu hỏi này sẽ có xu hướng gần như hoàn toàn dựa trên ý kiến, thay vì sự kiện, tài liệu tham khảo hoặc chuyên môn cụ thể.
gnat

10
Cũng đáng chỉ ra rằng hình phạt tính toán cho một lệnh gọi hàm hoặc phương thức rất nhỏ đến mức ngay cả trong một chương trình rất lớn có nhiều hàm và các lệnh gọi phương thức, làm cho các cuộc gọi đó thậm chí không được xếp hạng trên biểu đồ.
greyfade

1
@greyfade: Điều đó đúng với các bước nhảy trực tiếp nhưng bước nhảy dự đoán gián tiếp bổ sung có thể có giá ~ 3% tổng thời gian chạy chương trình (chỉ là một số từ chương trình tôi đã kiểm tra gần đây - mặc dù nó có thể không phải là đại diện). Tùy thuộc vào khu vực của bạn, bạn có thể hoặc có thể không coi nó là đáng kể nhưng nó đã đăng ký trên biểu đồ (và tất nhiên nó ít nhất là một phần trực giao với mô đun).
Maciej Piechotka

4
Tối ưu hóa sớm là gốc rễ của mọi tội lỗi. Mã tuyến tính nhanh hơn một chút so với mã mô-đun. Mã mô-đun nhanh hơn rất nhiều so với mã spaghetti. Nếu bạn nhắm đến mã tuyến tính mà không có một dự án rất (RẤT) toàn bộ, bạn sẽ kết thúc với mã spaghetti, tôi đảm bảo điều đó.
SF.

Câu trả lời:


46

Vâng, nó không liên quan.

Máy tính là động cơ thực thi không mệt mỏi, gần như hoàn hảo làm việc ở tốc độ hoàn toàn không thể so sánh với bộ não. Mặc dù có một lượng thời gian có thể đo được mà một lệnh gọi hàm thêm vào thời gian thực hiện của chương trình, nhưng điều này không là gì so với thời gian bổ sung cần thiết cho bộ não của người tiếp theo liên quan đến mã khi họ phải gỡ bỏ thói quen không thể đọc được để thậm chí bắt đầu hiểu làm thế nào để làm việc với nó. Bạn có thể thử tính toán cho một trò đùa - giả sử rằng mã của bạn chỉ được duy trì một lần và nó chỉ thêm nửa giờ cho thời gian cần thiết để ai đó đi đến thỏa thuận với mã. Lấy tốc độ xung nhịp bộ xử lý của bạn và tính toán: mã sẽ phải chạy bao nhiêu lần để thậm chí mơ ước bù đắp điều đó?

Nói tóm lại, việc thương hại CPU là hoàn toàn, hoàn toàn sai lầm 99,99% thời gian. Đối với các trường hợp hiếm còn lại, sử dụng hồ sơ. Đừng không cho rằng bạn có thể nhận ra những trường hợp đó - bạn không thể.


2
Mặc dù tôi đồng ý rằng hầu hết thời gian là tối ưu hóa sớm, tôi nghĩ rằng lập luận của bạn với thời gian là xấu. Thời gian là tương đối trong các tình huống khác nhau và bạn không thể tính toán đơn giản như bạn đã làm.
Honza Brabec

28
+1 chỉ cho biểu thức "lấy sự thương hại cho CPU", bởi vì nó rất phù hợp với sự sai lầm trong tối ưu hóa sớm.
Michael Borgwardt

4
Liên quan cao: Máy tính trong các điều khoản hàng ngày nhanh như thế nào? Tránh ra để tránh một vài chức năng gọi "tốc độ" giống như đi ra ngoài để cứu ai đó tổng cộng một phút trong suốt cuộc đời của họ . Nói tóm lại: nó thậm chí không xứng đáng với thời gian cần thiết để xem xét.
BlueRaja - Daniel Pflughoeft

7
+1 để bán một cách hùng hồn những gì nhiều người đang thiếu, nhưng bạn đã quên thêm trọng lượng quan trọng nhất vào số dư khiến cho việc thương hại CPU thậm chí còn nghiêm trọng hơn: Bỏ qua số tiền bị mất và thời gian dành cho việc bảo trì một lần đó; nếu mất 1 giây, vẫn còn một vụ chìm sâu hơn nữa. Rủi ro lỗi . Việc bảo trì càng thay đổi mã của bạn càng khó khăn và khó khăn hơn, thì nguy cơ anh ta thực hiện các lỗi trong đó càng cao, điều này có thể gây ra chi phí lớn không thể tránh khỏi từ rủi ro cho người dùng và bất kỳ lỗi nào gây ra bảo trì tiếp theo (có thể gây ra lỗi ...)
Jimmy Hoffa

Một giáo viên cũ của tôi từng nói "Nếu bạn đang suy nghĩ liệu bạn có tối ưu hóa sớm hay không, hãy dừng lại ngay tại đây. Nếu đây là lựa chọn đúng đắn, bạn sẽ biết điều đó".
Ven

22

Nó phụ thuộc.

Trong thế giới chậm chạp băng giá đó là lập trình Web, nơi mọi thứ xảy ra ở tốc độ của con người, lập trình nặng về phương pháp, trong đó chi phí của cuộc gọi phương thức tương đương hoặc vượt quá chi phí xử lý được thực hiện bởi phương thức, có lẽ không thành vấn đề .

Trong thế giới của lập trình hệ thống nhúng và xử lý ngắt cho các ngắt tốc độ cao, điều đó chắc chắn là có vấn đề. Trong môi trường đó, các mô hình thông thường về "truy cập bộ nhớ là rẻ" và "bộ xử lý cực kỳ nhanh" bị hỏng. Tôi đã thấy những gì xảy ra khi một lập trình viên hướng đối tượng máy tính lớn viết trình xử lý ngắt tốc độ cao đầu tiên của mình. Nó không đẹp

Cách đây vài năm, tôi đã thực hiện tô màu blob kết nối 8 chiều không đáng có trên hình ảnh FLIR thời gian thực, vào thời điểm đó là một bộ xử lý tốt. Nỗ lực đầu tiên đã sử dụng một cuộc gọi chương trình con và cuộc gọi chương trình con trên đầu đã ăn bộ xử lý còn sống. (4 cuộc gọi PER PIXEL x 64K pixel mỗi khung hình x 30 khung hình mỗi giây = bạn tìm ra nó). Nỗ lực thứ hai đã thay đổi chương trình con thành macro C, không mất khả năng đọc và mọi thứ đều là hoa hồng.

Bạn phải nhìn CỨNG vào những gì bạn đang làm và ở môi trường mà bạn sẽ làm việc đó.


Hãy nhớ rằng phần lớn các vấn đề lập trình hiện đại được giải quyết tốt nhất với rất nhiều mã hướng đối tượng, hướng hạnh phúc. Bạn sẽ biết khi bạn ở trong một môi trường hệ thống nhúng.
Kevin - Tái lập Monica

4
+1, nhưng có một cảnh báo: thông thường, một trình biên dịch tối ưu hóa thường sẽ thực hiện công việc nội tuyến tốt hơn, không kiểm soát vòng lặp và thực hiện tối ưu hóa vô hướng và vectơ so với một người. Lập trình viên vẫn cần biết ngôn ngữ, trình biên dịch và máy rất tốt để tận dụng lợi thế này, vì vậy nó không phải là phép thuật.
gièm pha

2
Điều đó đúng nhưng bạn đã tối ưu hóa mã của mình sau khi bạn viết logic và định hình nó để tìm phần có vấn đề trong thuật toán của bạn. Bạn đã không bắt đầu viết mã cho hiệu suất nhưng để dễ đọc. Và các macro sẽ giúp bạn giữ mã của bạn có thể đọc được.
Uwe Plonus

2
Ngay cả trong lập trình hệ thống nhúng, hãy bắt đầu bằng cách viết mã chính xác rõ ràng và chỉ bắt đầu tối ưu hóa sau đó nếu cần thiếttheo hướng dẫn bằng cách định hình . Mặt khác, quá dễ để đặt nhiều công việc vào đó không có ảnh hưởng thực sự đến hiệu suất / kích thước mã và điều này chỉ làm cho nguồn khó hiểu.
Donal Fellows

1
@detly: Có và không. Trình biên dịch hiện đại có thể CHUNG làm một công việc tốt hơn TRONG VẤN ĐỀ TÁC ĐỘNG CỦA NGÔN NGỮ. Các lập trình viên con người biết liệu các giới hạn áp đặt bởi ngôn ngữ có thể áp dụng trong trường hợp cụ thể trong tay hay không. Ví dụ, các trình biên dịch Cn và C ++ được yêu cầu bởi các tiêu chuẩn ngôn ngữ để cho phép lập trình viên thực hiện một số điều rất bệnh hoạn và tạo mã hoạt động theo mọi cách. Lập trình viên có thể biết anh ta không điên hoặc ngu ngốc hay mạo hiểm đủ để làm những việc đó và thực hiện tối ưu hóa nếu không an toàn, bởi vì anh ta biết rằng họ an toàn trong trường hợp này.
John R. Strohm

11

Trước hết: Các chương trình với ngôn ngữ cao hơn là để con người đọc chứ không phải bằng máy móc.

Vì vậy, viết các chương trình để bạn hiểu chúng. Đừng suy nghĩ về hiệu suất (nếu bạn nghiêm túc gặp vấn đề về nước hoa thì hãy lập hồ sơ cho ứng dụng của bạn và nâng cao hiệu suất khi cần thiết).

Ngay cả khi đúng là việc gọi một phương thức hoặc hàm mất một số chi phí thì điều này không thành vấn đề. Ngày nay các trình biên dịch sẽ có thể biên dịch mã của bạn thành ngôn ngữ máy hiệu quả để mã được tạo có hiệu quả cho kiến ​​trúc đích. Sử dụng các công tắc tối ưu hóa của trình biên dịch của bạn để có được mã hiệu quả.


5
Tôi muốn nói rằng chúng ta nên viết chương trình theo cách mà người khác sẽ hiểu chúng.
Bartlomiej Lewandowski

Người đầu tiên phải đọc một chương trình là chính nhà phát triển. Đó là lý do tại sao tôi viết cho bạn . Nếu những người khác có thể đọc chương trình thì cũng tốt nhưng (trong mắt tôi) không cần thiết (ở nơi đầu tiên). Nếu bạn làm việc trong một nhóm thì những người khác cũng nên hiểu chương trình nhưng ý định của tôi là người đó phải đọc một chương trình chứ không phải máy tính.
Uwe Plonus

5

Thông thường, khi bạn có một hàm lớn và bạn chia nó thành nhiều hàm nhỏ hơn, những hàm nhỏ hơn này sẽ được nội tuyến vì nhược điểm duy nhất của nội tuyến (lặp lại cùng một hướng dẫn quá nhiều) không liên quan trong trường hợp này. Điều đó có nghĩa là mã của bạn sẽ hoạt động như thể bạn đã viết một hàm lớn.

Nếu chúng không được nội tuyến vì một số lý do và điều này trở thành một vấn đề về hiệu suất, thì bạn nên xem xét nội tuyến thủ công. Không phải tất cả các ứng dụng đều là các hình thức CRUD được nối mạng với độ trễ nội tại rất lớn.


2

Có lẽ không có chi phí tính toán. Thông thường các trình biên dịch / JIT trong 10-20 năm qua hoặc liên quan đến chức năng nội tuyến hoàn toàn tốt. Đối với C / C ++ thông thường, nó bị giới hạn ở các hàm 'inlinable' (nghĩa là định nghĩa của hàm có sẵn cho trình biên dịch trong quá trình biên dịch - đó là trong tiêu đề của cùng một tệp) nhưng các kỹ thuật hiện tại của LTO đã khắc phục điều này.

Nếu bạn nên dành thời gian cho việc tối ưu hóa tùy thuộc vào khu vực bạn đang làm việc. Nếu bạn đối phó với ứng dụng 'bình thường' dành phần lớn thời gian chờ đợi đầu vào - có lẽ bạn không nên lo lắng về việc tối ưu hóa trừ khi ứng dụng 'cảm thấy' chậm.

Ngay cả trong những trường hợp như vậy, bạn nên tập trung vào nhiều thứ trước khi thực hiện tối ưu hóa vi mô:

  • Vấn đề ở đâu? Thông thường mọi người khó có thể tìm thấy các điểm nóng khi chúng ta đọc mã nguồn khác nhau. Chúng tôi có tỷ lệ thời gian hoạt động khác nhau và chúng tôi thực hiện chúng tuần tự trong khi các bộ xử lý hiện đại thì không .
  • Là làm một số tính toán cần thiết mỗi lần? Ví dụ: nếu bạn thay đổi một tham số trong số hàng ngàn, bạn có thể muốn tính toán chỉ một phần bị ảnh hưởng thay vì toàn bộ mô hình.
  • Bạn có sử dụng thuật toán tối ưu? Thay đổi từ O(n)để O(log n)có thể có tác động lớn hơn nhiều sau đó bất cứ điều gì bạn có thể đạt được bằng cách vi tối ưu hóa.
  • Bạn có sử dụng cấu trúc thích hợp? Giả sử bạn đang sử dụng Listkhi bạn cần HashSetđể bạn có O(n)tra cứu khi bạn có thể có O(1).
  • Bạn có sử dụng song song hiệu quả? Hiện tại, ngay cả điện thoại di động cũng có thể có 4 lõi hoặc nhiều hơn, nó có thể hấp dẫn khi sử dụng các luồng. Tuy nhiên, chúng không phải là viên đạn bạc vì chúng có chi phí đồng bộ hóa (không đề cập đến việc nếu vấn đề bị ràng buộc về bộ nhớ thì dù sao chúng cũng không có ý nghĩa gì).

Ngay cả khi bạn quyết định rằng bạn cần thực hiện tối ưu hóa vi mô (thực tế có nghĩa là phần mềm của bạn được sử dụng trong HPC, được nhúng hoặc chỉ được sử dụng bởi số lượng người rất lớn - nếu không, chi phí bảo trì bổ sung sẽ vượt qua chi phí thời gian của máy tính) bạn cần để xác định các điểm nóng (hạt nhân) mà bạn muốn tăng tốc. Nhưng sau đó có lẽ bạn nên:

  1. Biết chính xác nền tảng bạn đang làm việc
  2. Biết chính xác trình biên dịch bạn đang làm việc và khả năng tối ưu hóa nào có khả năng thực hiện và cách viết mã thành ngữ để kích hoạt các tối ưu hóa đó
  3. Hãy suy nghĩ về các mẫu truy cập bộ nhớ và mức độ bạn có thể phù hợp với bộ đệm (kích thước chính xác mà bạn biết từ điểm 1).
  4. Sau đó, nếu bạn bị ràng buộc tính toán, hãy suy nghĩ về việc sắp xếp lại tính toán để lưu tính toán

Như một nhận xét cuối cùng. Thông thường, vấn đề duy nhất bạn gặp phải với các cuộc gọi phương thức là các bước nhảy gián tiếp (phương thức ảo) không được dự đoán bởi bộ dự báo nhánh (không may là bước nhảy gián tiếp là trường hợp khó đối với nó). Tuy nhiên:

  • Java có JIT mà trong nhiều trường hợp có thể dự đoán loại lớp và do đó, mục tiêu nhảy trước và do đó trong các điểm nóng, bạn không nên gặp nhiều vấn đề.
  • Trình biên dịch C ++ thường thực hiện phân tích chương trình và ít nhất trong một số trường hợp có thể dự đoán mục tiêu tại thời điểm biên dịch.
  • Trong cả hai trường hợp khi mục tiêu đã được dự đoán, nội tuyến sẽ hoạt động. Nếu trình biên dịch không thể thực hiện các cơ hội nội tuyến thì chúng ta cũng không thể.

0

Câu trả lời của tôi có lẽ sẽ không mở rộng quá nhiều vào các câu trả lời hiện có, nhưng tôi cảm thấy hai xu của mình có thể hữu ích.

Trước hết; vâng, đối với tính mô đun, bạn thường bỏ một số mức thời gian thực hiện. Viết tất cả mọi thứ trong mã lắp ráp sẽ cung cấp cho bạn tốc độ tốt nhất. Mà nói...

Bạn biết YouTube? Có khả năng là trang web có băng thông cao nhất đang tồn tại hoặc đứng thứ hai sau Netflix? Họ viết một phần lớn mã của họ bằng Python, đây là một ngôn ngữ có tính mô-đun cao không được xây dựng cho hiệu năng vượt trội.

Vấn đề là, khi có sự cố xảy ra và người dùng phàn nàn về việc video tải chậm, không có nhiều tình huống mà sự chậm chạp đó cuối cùng sẽ được quy cho tốc độ thực thi chậm của Python. Tuy nhiên, việc biên dịch lại nhanh chóng của Python và khả năng mô đun của nó để thử những thứ mới mà không cần kiểm tra kiểu có thể sẽ cho phép các kỹ sư gỡ lỗi những gì sai khá nhanh ("Wow. Thực tập sinh mới của chúng tôi đã viết một vòng lặp thực hiện truy vấn phụ SQL mới cho MỌI kết quả. ") hoặc (" Ồ, Firefox đã từ chối định dạng tiêu đề bộ đệm cũ đó và họ đã tạo một thư viện Python để dễ dàng thiết lập cái mới ")

Theo nghĩa đó, ngay cả về thời gian thực hiện, một ngôn ngữ mô-đun có thể được coi là nhanh hơn bởi vì một khi bạn tìm thấy nút thắt của bạn là gì, có thể sẽ dễ dàng tổ chức lại mã của bạn để làm cho nó hoạt động theo cách tốt nhất. Vì vậy, nhiều kỹ sư sẽ nói với bạn rằng các cú đánh hiệu suất nặng không phải là nơi họ nghĩ (và thực tế những điều họ DID tối ưu hóa hầu như không cần thiết; hoặc, thậm chí không hoạt động theo cách họ mong đợi!)


0

Có và không. Như các chương trình khác đã lưu ý về khả năng đọc trước, sau đó cho hiệu quả. Tuy nhiên, có những thực hành tiêu chuẩn vừa dễ đọc vừa hiệu quả. Hầu hết các mã được chạy khá ít khi và bạn sẽ không đạt được nhiều lợi thế từ việc tối ưu hóa nó.

Java có thể thực hiện các cuộc gọi hàm nhỏ hơn, vì vậy có rất ít lý do để tránh viết các hàm. Trình tối ưu hóa có xu hướng hoạt động tốt hơn với mã đơn giản dễ đọc hơn. Có những nghiên cứu cho thấy các phím tắt về lý thuyết nên chạy nhanh hơn, thực sự mất nhiều thời gian hơn. Trình biên dịch JIT có khả năng hoạt động tốt hơn, mã nhỏ hơn và các phần chạy thường xuyên có thể được xác định và tối ưu hóa. Tôi đã không thử nó nhưng tôi sẽ mong đợi một chức năng lớn mà hiếm khi được gọi là không được biên dịch.

Điều này có thể không áp dụng cho Java, nhưng một nghiên cứu cho thấy các hàm lớn hơn thực sự chạy chậm hơn do yêu cầu một mô hình tham chiếu bộ nhớ khác. Đây là phần cứng và tối ưu hóa cụ thể. Đối với các hướng dẫn mô-đun nhỏ hơn hoạt động trong một trang bộ nhớ đã được sử dụng. Chúng nhanh hơn và nhỏ hơn các hướng dẫn cần thiết khi chức năng không vừa với một trang.

Có những trường hợp đáng để tối ưu hóa mã, nhưng nhìn chung bạn cần lập hồ sơ mã để xác định đó là đâu. Tôi thấy nó thường không phải là mã tôi mong đợ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.