Có phải mã độ trễ thấp đôi khi phải là mã xấu xí không?


21

(Điều này chủ yếu nhắm vào những người có kiến ​​thức cụ thể về hệ thống độ trễ thấp, để tránh mọi người chỉ trả lời với những ý kiến ​​không có căn cứ).

Bạn có cảm thấy có sự đánh đổi giữa việc viết mã định hướng đối tượng "đẹp" và viết mã độ trễ thấp rất nhanh không? Ví dụ, tránh các chức năng ảo trong C ++ / chi phí hoạt động của đa hình, v.v., việc viết lại mã có vẻ khó chịu, nhưng rất nhanh, v.v.?

Lý do là ai quan tâm nếu nó trông xấu xí (miễn là nó có thể duy trì được) - nếu bạn cần tốc độ, bạn cần tốc độ?

Tôi rất muốn nghe từ những người đã làm việc trong các lĩnh vực như vậy.


1
@ user997112: Lý do gần gũi là tự giải thích. Nó nói: "Chúng tôi hy vọng câu trả lời sẽ được hỗ trợ bởi các sự kiện, tài liệu tham khảo hoặc chuyên môn cụ thể, nhưng câu hỏi này có thể sẽ thu hút tranh luận, tranh luận, bỏ phiếu hoặc thảo luận mở rộng. Không nhất thiết là chúng đúng, nhưng đó là kết thúc lý do được lựa chọn bởi cả ba cử tri thân thiết.
Robert Harvey

Thông thường, tôi nói rằng lý do câu hỏi này thu hút được số phiếu gần là vì nó có thể được coi là một câu nói mỏng manh (mặc dù tôi không nghĩ vậy).
Robert Harvey

8
Tôi sẽ ló đầu ra: Tôi bỏ phiếu thứ ba để đóng là "không mang tính xây dựng" bởi vì tôi nghĩ người hỏi trả lời khá nhiều câu hỏi của chính mình. Mã "đẹp" không chạy đủ nhanh để thực hiện công việc đã không đáp ứng yêu cầu độ trễ. Mã "Xấu xí" chạy đủ nhanh có thể được bảo trì nhiều hơn thông qua tài liệu tốt. Làm thế nào bạn đo lường vẻ đẹp hay sự xấu xí là một chủ đề cho một câu hỏi khác.
Blrfl

1
Mã nguồn cho Disruptor của LMAX không quá xấu. Có một số phần 'mô hình bảo mật' của Java (lớp không an toàn) và một số sửa đổi cụ thể về phần cứng (biến đệm được đệm dòng bộ đệm) nhưng IMO rất dễ đọc.
James

5
@ Carson63000, user1598390 và bất cứ ai quan tâm: Nếu câu hỏi kết thúc, hãy hỏi về việc đóng cửa trên trang Meta của chúng tôi , có rất ít điểm để thảo luận về việc đóng cửa trong các bình luận, đặc biệt là việc đóng cửa đã không xảy ra . Ngoài ra, hãy nhớ rằng mọi câu hỏi đóng có thể được mở lại, đó không phải là ngày tận thế. Tất nhiên ngoại trừ nếu người Maya đã đúng, trong trường hợp đó thật tốt khi biết tất cả các bạn!
yannis

Câu trả lời:


31

Bạn có cảm thấy có sự đánh đổi giữa việc viết mã định hướng đối tượng "đẹp" và viết mã có độ trễ rất thấp không?

Vâng.

Đó là lý do tại sao cụm từ "tối ưu hóa sớm" tồn tại. Nó tồn tại để buộc các nhà phát triển đo lường hiệu suất của họ và chỉ tối ưu hóa mã đó sẽ tạo ra sự khác biệt về hiệu suất, trong khi thiết kế hợp lý kiến ​​trúc ứng dụng của họ ngay từ đầu để nó không bị rơi xuống dưới tải nặng.

Bằng cách đó, đến mức tối đa có thể, bạn có thể giữ mã đẹp, được kiến ​​trúc tốt, hướng đối tượng và chỉ tối ưu hóa với mã xấu mà những phần nhỏ đó quan trọng.


15
"Làm cho nó hoạt động, sau đó làm cho nó nhanh". Câu trả lời này bao gồm khá nhiều điều tôi nghĩ sẽ nói khi đọc câu hỏi.
Carson63000

13
Tôi sẽ thêm "Biện pháp, đừng đoán"
Martijn Verburg

1
Tôi nghĩ rằng việc đưa một số mặc dù vào công việc cơ bản khi bạn đi là đáng giá miễn là nó không phải trả giá cho mức độ dễ đọc. Giữ mọi thứ ngắn gọn, dễ đọc và chỉ làm những việc rõ ràng họ cần làm dẫn đến nhiều chiến thắng hoàn hảo lâu dài gián tiếp như các nhà phát triển khác biết cách tạo ra mã của bạn để họ không nhân đôi nỗ lực hoặc đưa ra các giả định xấu về cách thức hoạt động.
Erik Reppen

1
Trên "tối ưu hóa sớm" - vẫn áp dụng ngay cả khi mã được tối ưu hóa sẽ "đẹp" như mã không được tối ưu hóa. Vấn đề là không lãng phí thời gian để nhắm đến tốc độ / bất cứ điều gì bạn không cần phải đạt được. Trong thực tế, tối ưu hóa không phải lúc nào cũng là về tốc độ, và có thể nói rằng có một thứ như tối ưu hóa không cần thiết cho "vẻ đẹp". Mã của bạn không cần phải là một tác phẩm nghệ thuật tuyệt vời để có thể đọc và duy trì được.
Steve314

Tôi thứ hai @ Steve314. Tôi là người dẫn đầu về hiệu suất trên một sản phẩm và thường tìm thấy mã quá phức tạp có nguồn gốc mà tôi có thể truy ngược lại một số loại tối ưu hóa hiệu suất. Đơn giản hóa mã đó thường cho thấy một cải tiến hiệu suất đáng kể. Một ví dụ như vậy đã biến thành một cải tiến hiệu suất 5x khi tôi đơn giản hóa nó (giảm hàng ngàn dòng mã). Rõ ràng, không ai dành thời gian để thực sự đo lường và chỉ đơn giản là tối ưu hóa sớm những gì họ nghĩ có thể là mã chậm .
Brandon

5

Vâng, ví dụ tôi đưa ra không phải là C ++ so với Java mà là hội so với COBOL vì đó là những gì tôi biết.

Cả hai ngôn ngữ đều rất nhanh, nhưng, ngay cả COBOL khi được biên dịch cũng có nhiều hướng dẫn khác được đặt vào tập lệnh mà không nhất thiết phải có ở đó so với việc tự viết các hướng dẫn đó trong hội.

Ý tưởng tương tự có thể được áp dụng trực tiếp cho câu hỏi của bạn về việc viết "mã trông xấu xí" so với sử dụng tính kế thừa / đa hình trong C ++. Tôi tin rằng cần phải viết mã trông xấu xí, nếu người dùng cuối cần khung thời gian giao dịch thứ hai thì công việc của chúng tôi là lập trình viên để cung cấp cho họ bất kể điều đó xảy ra như thế nào.

Điều đó đang được nói, việc sử dụng bình luận tự do làm tăng đáng kể chức năng và khả năng bảo trì của lập trình viên, bất kể mã xấu đến mức nào.


3

Vâng, một sự đánh đổi tồn tại. Bằng cách này, tôi có nghĩa là mã nhanh hơn và xấu hơn là không cần thiết tốt hơn - lợi ích định lượng từ "mã nhanh" cần phải được cân nhắc với độ phức tạp bảo trì của các thay đổi mã cần thiết để đạt được tốc độ đó.

Sự đánh đổi đến từ chi phí kinh doanh. Mã phức tạp hơn đòi hỏi nhiều lập trình viên lành nghề hơn (và lập trình viên có bộ kỹ năng tập trung hơn, chẳng hạn như mã có kiến ​​thức thiết kế và kiến ​​trúc CPU), mất nhiều thời gian hơn để đọc và hiểu mã và sửa lỗi. Chi phí kinh doanh để phát triển và duy trì mã như vậy có thể nằm trong phạm vi gấp 10 - 100 lần so với mã được viết thông thường.

Chi phí bảo trì này là hợp lý trong một số ngành , trong đó khách hàng sẵn sàng trả phí bảo hiểm rất cao cho phần mềm rất nhanh.

Một số tối ưu hóa tốc độ làm cho lợi tức đầu tư (ROI) tốt hơn so với những cái khác. Cụ thể, một số kỹ thuật tối ưu hóa có thể được áp dụng với tác động ít hơn đến khả năng duy trì mã (bảo tồn cấu trúc cấp cao hơn và khả năng đọc cấp thấp hơn) so với mã được viết thông thường.

Vì vậy, một chủ doanh nghiệp nên:

  • Nhìn vào chi phí và lợi ích,
  • Thực hiện các phép đo và tính toán
    • Yêu cầu lập trình viên đo tốc độ chương trình
    • Yêu cầu lập trình viên ước tính thời gian phát triển cần thiết để tối ưu hóa
    • Ước tính riêng về doanh thu tăng từ phần mềm nhanh hơn
    • Yêu cầu các kiến ​​trúc sư phần mềm hoặc người quản lý QA đánh giá một cách định tính các nhược điểm do giảm tính trực giác và khả năng đọc của mã nguồn
  • Và ưu tiên các loại trái cây treo tối ưu hóa phần mềm.

Những sự đánh đổi này rất cụ thể cho hoàn cảnh.

Chúng không thể được quyết định một cách tối ưu nếu không có sự tham gia của các nhà quản lý và chủ sở hữu sản phẩm.

Đây là rất cụ thể cho các nền tảng. Ví dụ, CPU máy tính để bàn và CPU di động có những cân nhắc khác nhau. Các ứng dụng máy chủ và máy khách cũng có những cân nhắc khác nhau.


Vâng, nói chung đúng là mã nhanh hơn trông khác với mã được viết thông thường. Bất kỳ mã nào khác nhau sẽ mất nhiều thời gian hơn để đọc. Cho dù điều đó ngụ ý sự xấu xí là trong mắt của kẻ si tình.

Các kỹ thuật mà tôi có một số tiếp xúc là: (không cố gắng yêu cầu bất kỳ mức độ chuyên môn nào) tối ưu hóa vectơ ngắn (SIMD), song song hóa nhiệm vụ chi tiết, phân bổ trước bộ nhớ và tái sử dụng đối tượng.

SIMD thường có tác động nghiêm trọng đến khả năng đọc ở mức độ thấp, mặc dù nó thường không yêu cầu thay đổi cấu trúc ở cấp độ cao hơn (với điều kiện API được thiết kế với mục đích ngăn chặn tắc nghẽn).

Một số thuật toán có thể được chuyển đổi thành SIMD một cách dễ dàng (có thể thay đổi một cách rõ ràng). Một số thuật toán yêu cầu sắp xếp lại tính toán nhiều hơn để sử dụng SIMD. Trong các trường hợp cực đoan như song song SIMD sóng, các thuật toán hoàn toàn mới (và triển khai bằng sáng chế) phải được viết để tận dụng lợi thế.

Song song hóa nhiệm vụ chi tiết đòi hỏi phải sắp xếp lại các thuật toán vào các biểu đồ luồng dữ liệu và liên tục áp dụng phân rã chức năng (tính toán) cho thuật toán cho đến khi không thể đạt được lợi ích biên nữa. Các giai đoạn phân tách thường được xâu chuỗi với kiểu tiếp tục, một khái niệm mượn từ lập trình chức năng.

Bằng cách phân rã chức năng (tính toán), các thuật toán có thể được viết bình thường theo trình tự tuyến tính và rõ ràng về mặt khái niệm (các dòng mã có thể thực thi theo cùng thứ tự chúng được viết) phải được chia thành các đoạn và phân phối thành nhiều hàm hoặc các lớp học. (Xem phần đối tượng hóa thuật toán, bên dưới.) Thay đổi này sẽ cản trở rất nhiều các lập trình viên không quen với quy trình thiết kế phân rã đã tạo ra mã như vậy.

Để làm cho mã này có thể duy trì được, các tác giả của mã đó phải viết các tài liệu phức tạp về thuật toán - vượt xa các loại bình luận mã hoặc sơ đồ UML được thực hiện cho mã được viết thông thường. Điều này tương tự như cách các nhà nghiên cứu viết bài báo học thuật của họ.


Không, mã nhanh không cần phải mâu thuẫn với hướng đối tượng.

Nói cách khác, có thể thực hiện phần mềm rất nhanh mà vẫn hướng đối tượng. Tuy nhiên, đối với cấp thấp hơn của việc triển khai đó (ở cấp độ hạt và bu lông trong đó phần lớn tính toán xảy ra), thiết kế đối tượng có thể sai lệch đáng kể so với các thiết kế thu được từ thiết kế hướng đối tượng (OOD). Thiết kế cấp thấp hơn hướng đến sự khách quan hóa thuật toán.

Một vài lợi ích của lập trình hướng đối tượng (OOP), như đóng gói, đa hình và thành phần, vẫn có thể được gặt hái từ đối tượng hóa thuật toán cấp thấp. Đây là lý do chính để sử dụng OOP ở cấp độ này.

Hầu hết các lợi ích của thiết kế hướng đối tượng (OOD) bị mất. Quan trọng nhất, không có trực giác trong thiết kế cấp thấp. Một lập trình viên đồng nghiệp không thể học cách làm việc với mã cấp thấp hơn mà không hiểu đầy đủ về cách thuật toán đã được chuyển đổi và phân tách ngay từ đầu, và sự hiểu biết này không thể có được từ mã kết quả.


2

Có đôi khi mã phải "xấu" để làm cho nó hoạt động trong thời gian cần thiết, tất cả các mã không phải là xấu mặc dù. Hiệu suất nên được kiểm tra và định hình trước để tìm ra các đoạn mã cần phải "xấu xí" và những phần đó cần được ghi chú bằng một nhận xét để các nhà phát triển trong tương lai biết điều gì là xấu xí và điều gì chỉ là sự lười biếng. Nếu ai đó đang viết rất nhiều mã được thiết kế kém tuyên bố lý do hiệu suất, hãy làm cho họ chứng minh điều đó.

Tốc độ cũng quan trọng như bất kỳ yêu cầu nào khác của chương trình, đưa ra các chỉnh sửa sai cho tên lửa dẫn đường tương đương với việc cung cấp các hiệu chỉnh đúng sau khi va chạm. Khả năng bảo trì luôn là mối quan tâm thứ yếu đối với mã làm việc.


2

Một số nghiên cứu tôi đã thấy các trích đoạn chỉ ra rằng mã dễ đọc sạch thường nhanh hơn mã khó đọc phức tạp hơn. Một phần, điều này là do cách tối ưu hóa được thiết kế. Họ có xu hướng tốt hơn nhiều trong việc tối ưu hóa một biến thành một thanh ghi, hơn là làm tương tự với kết quả trung gian của một phép tính. Chuỗi bài tập dài sử dụng một toán tử duy nhất dẫn đến kết quả cuối cùng có thể được tối ưu hóa tốt hơn một phương trình phức tạp dài. Các trình tối ưu hóa mới hơn có thể đã làm giảm sự khác biệt giữa mã sạch và phức tạp, nhưng tôi nghi ngờ họ đã loại bỏ nó.

Các tối ưu hóa khác như unrolling có thể được thêm vào một cách sạch sẽ khi được yêu cầu.

Bất kỳ tối ưu hóa nào được thêm vào để cải thiện hiệu suất nên được kèm theo một nhận xét thích hợp. Điều này nên bao gồm một tuyên bố rằng nó đã được thêm vào như một tối ưu hóa, tốt nhất là với các biện pháp hiệu suất trước và sau.

Tôi đã tìm thấy quy tắc 80/20 áp dụng cho mã tôi đã tối ưu hóa. Theo nguyên tắc thông thường, tôi không tối ưu hóa bất cứ điều gì không chiếm ít nhất 80% thời gian. Sau đó tôi nhắm đến (và thường đạt được) tăng hiệu suất gấp 10 lần. Điều này cải thiện hiệu suất khoảng 4 lần. Hầu hết các tối ưu hóa tôi đã thực hiện chưa làm cho mã trở nên "đẹp" hơn đáng kể. Số dặm của bạn có thể thay đổi.


2

Nếu xấu, bạn có nghĩa là khó đọc / hiểu ở cấp độ mà các nhà phát triển khác sẽ sử dụng lại hoặc cần phải hiểu nó, thì tôi sẽ nói, mã thanh lịch, dễ đọc hầu như sẽ luôn khiến bạn thất vọng tăng hiệu suất trong thời gian dài trong một ứng dụng mà bạn phải duy trì.

Mặt khác, đôi khi có đủ một chiến thắng hiệu suất để làm cho nó đáng để đặt xấu vào một chiếc hộp đẹp với giao diện sát thủ trên đó nhưng theo kinh nghiệm của tôi, đây là một vấn đề khá khó xử.

Hãy suy nghĩ về việc tránh công việc cơ bản khi bạn đi. Lưu các thủ thuật phức tạp khi một vấn đề hiệu suất thực sự xuất hiện. Và nếu bạn phải viết một cái gì đó mà ai đó chỉ có thể hiểu được thông qua việc làm quen với tối ưu hóa cụ thể, hãy làm những gì bạn có thể để làm cho sự xấu xí trở nên dễ hiểu từ việc sử dụng lại quan điểm mã của bạn. Mã thực hiện rất hiếm khi xảy ra bởi vì các nhà phát triển đã suy nghĩ quá nhiều về những gì anh chàng tiếp theo sẽ thừa hưởng, nhưng nếu thay đổi thường xuyên là hằng số duy nhất của một ứng dụng (hầu hết các ứng dụng web theo kinh nghiệm của tôi), mã cứng nhắc / không linh hoạt đó là khó sửa đổi trên thực tế là cầu xin cho những mớ hỗn độn hoảng loạn bắt đầu xuất hiện trên khắp cơ sở mã của bạn. Sạch và nạc là tốt hơn cho hiệu suất trong thời gian dài.


Tôi muốn đề xuất hai thay đổi: (1) Có những nơi cần tốc độ. Ở những nơi đó, tôi nghĩ rằng đáng để làm cho giao diện dễ hiểu hơn là làm cho việc thực hiện dễ hiểu, bởi vì điều sau có thể khó khăn hơn rất nhiều. (2) "Mã thực hiện khốn khổ hiếm khi làm như vậy ...", mà tôi muốn viết lại là "Sự nhấn mạnh mạnh vào sự thanh lịch và đơn giản của mã hiếm khi là nguyên nhân của hiệu suất khốn khổ. được dự đoán trước, ... "
rwong

Thực hiện là một lựa chọn từ ngữ kém trong một cuộc trò chuyện OOPish. Tôi có nghĩa là nó dễ dàng sử dụng lại và chỉnh sửa. # 2, tôi vừa thêm một câu để xác định rằng 2 về cơ bản là điểm tôi đang thực hiện.
Erik Reppen

1

Phức tạpxấu xí không giống nhau. Mã có nhiều trường hợp đặc biệt, được tối ưu hóa để phát huy từng giọt hiệu suất cuối cùng và thoạt nhìn trông giống như một mớ kết nối và phụ thuộc trên thực tế có thể được thiết kế rất cẩn thận và khá đẹp khi bạn hiểu nó. Thật vậy, nếu hiệu suất (cho dù được đo bằng độ trễ hoặc thứ gì khác) đủ quan trọng để biện minh cho mã rất phức tạp, thì mã phải được thiết kế tốt. Nếu không, thì bạn không thể chắc chắn rằng tất cả sự phức tạp đó thực sự tốt hơn một giải pháp đơn giản hơn.

Mã xấu, với tôi, là mã cẩu thả, được xem xét kém và / hoặc phức tạp không cần thiết . Tôi không nghĩ rằng bạn muốn bất kỳ tính năng nào trong mã phải thực hiện.


1

Bạn có cảm thấy có sự đánh đổi giữa việc viết mã định hướng đối tượng "đẹp" và viết mã độ trễ thấp rất nhanh không? Ví dụ, tránh các chức năng ảo trong C ++ / chi phí hoạt động của đa hình, v.v., việc viết lại mã có vẻ khó chịu, nhưng rất nhanh, v.v.?

Tôi làm việc trong một lĩnh vực tập trung nhiều hơn vào thông lượng hơn độ trễ, nhưng nó rất quan trọng về hiệu suất và tôi sẽ nói "sorta" .

Tuy nhiên, một vấn đề là rất nhiều người hiểu sai về hiệu suất của họ. Người mới thường hiểu sai về mọi thứ và toàn bộ mô hình khái niệm "chi phí tính toán" của họ cần phải làm lại, chỉ có sự phức tạp về thuật toán là điều duy nhất họ có thể làm đúng. Trung gian nhận được rất nhiều điều sai. Các chuyên gia nhận được một số điều sai.

Đo lường bằng các công cụ chính xác có thể cung cấp các số liệu như lỗi bộ nhớ cache và dự đoán sai chi nhánh là điều giúp mọi người ở bất kỳ cấp độ chuyên môn nào trong lĩnh vực này kiểm tra.

Đo lường cũng là những gì chỉ ra những gì không tối ưu hóa . Các chuyên gia thường dành ít thời gian để tối ưu hóa hơn người mới, vì họ tối ưu hóa các điểm nóng đo được thực sự và không cố gắng tối ưu hóa các cú đâm hoang dã trong bóng tối dựa trên linh cảm về những gì có thể chậm (ở dạng cực đoan, có thể cám dỗ một người để tối ưu hóa vi mô về mọi dòng khác trong cơ sở mã).

Thiết kế cho hiệu suất

Bên cạnh đó, chìa khóa để thiết kế cho hiệu suất đến từ phần thiết kế , như trong thiết kế giao diện. Một trong những vấn đề với thiếu kinh nghiệm là có xu hướng thay đổi sớm các số liệu thực hiện tuyệt đối, như chi phí của một cuộc gọi hàm gián tiếp trong một số bối cảnh tổng quát, như thể chi phí (được hiểu rõ hơn theo nghĩa tức thời từ điểm tối ưu hóa của quan điểm chứ không phải là một quan điểm phân nhánh) là một lý do để tránh nó trong toàn bộ cơ sở mã.

Chi phí tương đối . Mặc dù có một chi phí cho một cuộc gọi chức năng gián tiếp, ví dụ, tất cả các chi phí là tương đối. Nếu bạn trả một lần chi phí đó để gọi một chức năng lặp qua hàng triệu yếu tố, thì việc lo lắng về chi phí này cũng giống như việc bạn mất hàng giờ đồng hồ để mua một sản phẩm tỷ đô, chỉ để kết luận không mua sản phẩm đó vì nó là một xu quá đắt

Thiết kế giao diện Coarser

Khía cạnh thiết kế giao diện của hiệu suất thường tìm kiếm sớm hơn để đẩy các chi phí này đến mức thô hơn. Ví dụ, thay vì trả chi phí trừu tượng thời gian chạy cho một hạt, chúng ta có thể đẩy chi phí đó đến mức của hệ thống / bộ phát hạt, biến hiệu quả của hạt thành một chi tiết thực hiện và / hoặc đơn giản là dữ liệu thô của bộ sưu tập hạt này.

Vì vậy, thiết kế hướng đối tượng không phải không tương thích với thiết kế cho hiệu suất (dù là độ trễ hay thông lượng), nhưng có thể có những cám dỗ trong ngôn ngữ tập trung vào nó để mô hình hóa các đối tượng dạng hạt ngày càng trẻ hơn và ở đó trình tối ưu hóa mới nhất không thể Cứu giúp. Nó không thể thực hiện những việc như kết hợp một lớp đại diện cho một điểm duy nhất theo cách mang lại một biểu diễn SoA hiệu quả cho các mẫu truy cập bộ nhớ của phần mềm. Một tập hợp các điểm với thiết kế giao diện được mô hình hóa ở mức độ thô mang lại cơ hội đó và cho phép lặp lại theo hướng ngày càng nhiều giải pháp tối ưu hơn khi cần. Thiết kế như vậy được thiết kế cho bộ nhớ số lượng lớn *.

* Lưu ý tập trung vào bộ nhớ ở đây và không phải dữ liệu , vì làm việc trong các khu vực quan trọng về hiệu năng trong một thời gian dài sẽ có xu hướng thay đổi cách nhìn của bạn về các loại dữ liệu và cấu trúc dữ liệu và xem cách chúng kết nối với bộ nhớ. Cây tìm kiếm nhị phân không còn trở nên đơn thuần về độ phức tạp logarit trong các trường hợp như các khối bộ nhớ không thân thiện và bộ nhớ cache không thân thiện cho các nút cây trừ khi được hỗ trợ bởi bộ cấp phát cố định. Khung nhìn không loại bỏ độ phức tạp thuật toán, nhưng nó thấy nó không còn độc lập với bố cục bộ nhớ. Người ta cũng bắt đầu thấy các công việc lặp đi lặp lại nhiều hơn về các lần lặp truy cập bộ nhớ. *

Rất nhiều thiết kế quan trọng về hiệu năng thực sự có thể rất tương thích với khái niệm thiết kế giao diện cấp cao, dễ hiểu cho con người và sử dụng. Sự khác biệt là "mức cao" trong ngữ cảnh này sẽ là tập hợp số lượng lớn bộ nhớ, một giao diện được mô hình hóa cho các bộ sưu tập dữ liệu có khả năng lớn và với việc triển khai dưới mui xe có thể ở mức khá thấp. Một sự tương tự trực quan có thể là một chiếc xe thực sự thoải mái, dễ lái và dễ điều khiển và rất an toàn khi đi ở tốc độ âm thanh, nhưng nếu bạn bật mui xe, có rất ít con quỷ thở lửa bên trong.

Với thiết kế thô hơn cũng có xu hướng trở thành một cách dễ dàng hơn để cung cấp các kiểu khóa hiệu quả hơn và khai thác tính song song trong mã (đa luồng là một chủ đề toàn diện mà tôi sẽ bỏ qua ở đây).

Bộ nhớ

Một khía cạnh quan trọng của lập trình độ trễ thấp có lẽ sẽ là một sự kiểm soát rất rõ ràng đối với bộ nhớ để cải thiện tính cục bộ của tham chiếu cũng như tốc độ chung của việc phân bổ và giải phóng bộ nhớ. Một bộ nhớ gộp cấp phát tùy chỉnh thực sự lặp lại cùng một kiểu tư duy thiết kế mà chúng tôi đã mô tả. Nó được thiết kế cho số lượng lớn ; nó được thiết kế ở mức độ thô. Nó phân bổ bộ nhớ trong các khối lớn và gộp bộ nhớ đã được phân bổ thành các khối nhỏ.

Ý tưởng này hoàn toàn giống với việc đẩy những thứ tốn kém (phân bổ một đoạn bộ nhớ theo phân bổ mục đích chung, ví dụ) đến mức thô hơn và thô hơn. Nhóm bộ nhớ được thiết kế để xử lý hàng loạt bộ nhớ .

Loại hệ thống Bộ nhớ tách riêng

Một trong những khó khăn với thiết kế hướng đối tượng chi tiết trong bất kỳ ngôn ngữ nào là nó thường muốn giới thiệu rất nhiều kiểu và cấu trúc dữ liệu do người dùng định nghĩa. Những loại đó sau đó có thể muốn được phân bổ theo từng phần nhỏ nếu chúng được phân bổ động.

Một ví dụ phổ biến trong C ++ sẽ là cho các trường hợp bắt buộc phải có tính đa hình, trong đó sự cám dỗ tự nhiên là phân bổ từng thể hiện của một lớp con so với cấp phát bộ nhớ cho mục đích chung.

Điều này cuối cùng phá vỡ các bố cục bộ nhớ liền kề có thể thành các bit và mảnh nhỏ của nó nằm rải rác trong phạm vi địa chỉ, điều này dẫn đến nhiều lỗi trang hơn và lỗi bộ nhớ cache.

Các lĩnh vực đòi hỏi độ trễ thấp nhất, không nói lắp, đáp ứng xác định có lẽ là một nơi mà các điểm nóng không phải lúc nào cũng sôi sục đến một nút cổ chai, nơi mà sự thiếu hiệu quả thực sự có thể thực sự là "tích lũy" (điều mà nhiều người tưởng tượng xảy ra không chính xác với một trình lược tả để giữ chúng trong tầm kiểm soát, nhưng trong các trường điều khiển độ trễ, thực sự có thể có một số trường hợp hiếm hoi khi thiếu hiệu quả tích lũy nhỏ). Và rất nhiều lý do phổ biến nhất cho sự tích lũy như vậy có thể là đây: sự phân bổ quá mức của các khối bộ nhớ tuổi teen ở khắp mọi nơi.

Trong các ngôn ngữ như Java, có thể hữu ích khi sử dụng nhiều mảng dữ liệu cũ đơn giản hơn khi có thể cho các khu vực tắc nghẽn (các khu vực được xử lý trong các vòng lặp chặt chẽ) như một mảng int(nhưng vẫn nằm sau giao diện cấp cao cồng kềnh) thay vì nói , một đối tượng do ArrayListngười dùng định nghĩa Integer. Điều này tránh sự phân biệt bộ nhớ thường đi kèm với cái sau. Trong C ++, chúng ta không phải làm suy giảm cấu trúc nhiều như vậy nếu các mẫu phân bổ bộ nhớ của chúng ta hiệu quả, vì các kiểu do người dùng xác định có thể được phân bổ liền kề ở đó và ngay cả trong bối cảnh của một thùng chứa chung.

Bộ nhớ hợp nhất trở lại với nhau

Một giải pháp ở đây là tiếp cận một công cụ cấp phát tùy chỉnh cho các loại dữ liệu đồng nhất và thậm chí có thể trên các loại dữ liệu đồng nhất. Khi các kiểu dữ liệu nhỏ và cấu trúc dữ liệu được làm phẳng thành bit và byte trong bộ nhớ, chúng có tính chất đồng nhất (mặc dù có một số yêu cầu căn chỉnh khác nhau). Khi chúng ta không nhìn chúng từ một tư duy tập trung vào bộ nhớ, hệ thống các ngôn ngữ lập trình "muốn" phân tách / tách biệt các vùng bộ nhớ có khả năng tiếp giáp nhau thành các khối nhỏ rải rác.

Ngăn xếp sử dụng tiêu điểm tập trung vào bộ nhớ này để tránh điều này và có khả năng lưu trữ bất kỳ kết hợp hỗn hợp nào có thể có của các thể hiện loại do người dùng xác định bên trong nó. Sử dụng ngăn xếp nhiều hơn là một ý tưởng tuyệt vời khi có thể vì phần trên của nó hầu như luôn luôn nằm trong một dòng bộ đệm, nhưng chúng ta cũng có thể thiết kế các bộ cấp phát bộ nhớ bắt chước một số đặc điểm này mà không có mẫu LIFO, kết hợp bộ nhớ giữa các loại dữ liệu khác nhau chunk thậm chí cho các mô hình phân bổ bộ nhớ và phân bổ bộ nhớ phức tạp hơn.

Phần cứng hiện đại được thiết kế để đạt đến đỉnh cao khi xử lý các khối bộ nhớ liền kề (liên tục truy cập vào cùng một dòng bộ đệm, cùng một trang, ví dụ). Từ khóa có sự liên tục, vì điều này chỉ có lợi nếu có dữ liệu quan tâm xung quanh. Vì vậy, rất nhiều chìa khóa (nhưng cũng khó khăn) để thực hiện là kết hợp các khối bộ nhớ tách biệt lại với nhau thành các khối liền kề được truy cập toàn bộ (tất cả dữ liệu xung quanh có liên quan) trước khi bị trục xuất. Hệ thống loại phong phú gồm các loại đặc biệt do người dùng xác định trong ngôn ngữ lập trình có thể là trở ngại lớn nhất ở đây, nhưng chúng ta luôn có thể tiếp cận và giải quyết vấn đề thông qua bộ cấp phát tùy chỉnh và / hoặc thiết kế cồng kềnh khi thích hợp.

Xấu xí

"Xấu xí" thật khó nói. Đó là một số liệu chủ quan và ai đó làm việc trong một lĩnh vực rất quan trọng về hiệu suất sẽ bắt đầu thay đổi ý tưởng về "vẻ đẹp" của họ thành một định hướng dữ liệu nhiều hơn và tập trung vào các giao diện xử lý hàng loạt.

Nguy hiểm

"Nguy hiểm" có thể dễ dàng hơn. Nói chung, hiệu suất có xu hướng muốn đạt tới mã cấp thấp hơn. Việc thực hiện cấp phát bộ nhớ, chẳng hạn, là không thể nếu không tiếp cận bên dưới các kiểu dữ liệu và làm việc ở mức nguy hiểm của các bit và byte thô. Kết quả là, nó có thể giúp tăng sự tập trung vào quy trình thử nghiệm cẩn thận trong các hệ thống con quan trọng về hiệu năng này, mở rộng quy mô của thử nghiệm với mức độ tối ưu hóa được áp dụng.

sắc đẹp, vẻ đẹp

Tuy nhiên, tất cả điều này sẽ ở cấp độ chi tiết thực hiện. Trong cả một tư duy quan trọng về hiệu suất và quy mô lớn kỳ cựu, "vẻ đẹp" có xu hướng chuyển sang các thiết kế giao diện hơn là chi tiết triển khai. Nó trở thành một ưu tiên cao hơn theo cấp số nhân để tìm kiếm các giao diện "đẹp", có thể sử dụng, an toàn, hiệu quả thay vì triển khai do sự phá vỡ khớp nối và xếp tầng có thể xảy ra khi thay đổi thiết kế giao diện. Việc thực hiện có thể được hoán đổi bất cứ lúc nào. Chúng tôi thường lặp lại theo hướng hiệu suất khi cần thiết, và như được chỉ ra bởi các phép đo. Chìa khóa với thiết kế giao diện là mô hình hóa ở mức đủ thô để chừa chỗ cho các lần lặp như vậy mà không phá vỡ toàn bộ hệ thống.

Trên thực tế, tôi sẽ đề nghị rằng một cựu chiến binh tập trung vào phát triển quan trọng về hiệu suất thường sẽ có xu hướng tập trung chủ yếu vào an toàn, kiểm tra, bảo trì, chỉ là môn đệ của SE nói chung, vì một cơ sở mã hóa quy mô lớn có số lượng hiệu suất hệ thống con -critical (hệ thống hạt, thuật toán xử lý hình ảnh, xử lý video, phản hồi âm thanh, raytracers, động cơ lưới, v.v.) sẽ cần hết sức chú ý đến công nghệ phần mềm để tránh chìm trong cơn ác mộng bảo trì. Không phải ngẫu nhiên mà thường các sản phẩm hiệu quả đáng kinh ngạc nhất ngoài kia cũng có thể có số lượng lỗi ít nhất.

TL; DR

Dù sao, đó là việc tôi tham gia vào chủ đề này, từ các ưu tiên trong các lĩnh vực quan trọng về hiệu suất thực sự, điều gì có thể làm giảm độ trễ và gây ra sự thiếu hiệu quả nhỏ và điều thực sự tạo nên "vẻ đẹp" (khi nhìn vào mọi thứ một cách hiệu quả nhất).


0

Không phải là khác nhau, nhưng đây là những gì tôi làm:

  1. Viết nó sạch sẽ và duy trì.

  2. Thực hiện chẩn đoán hiệu suất và khắc phục các sự cố mà nó cho bạn biết, không phải những vấn đề bạn đoán. Đảm bảo, chúng sẽ khác với những gì bạn mong đợi.

Bạn có thể thực hiện các sửa lỗi này theo cách vẫn rõ ràng và có thể duy trì được, nhưng, bạn sẽ phải thêm bình luận để những người nhìn vào mã sẽ biết tại sao bạn lại làm như vậy. Nếu bạn không, họ sẽ hoàn tác nó.

Vậy có đánh đổi không? Tôi thực sự không nghĩ như vậy.


0

Bạn có thể viết mã xấu rất nhanh và bạn cũng có thể viết mã đẹp nhanh như mã xấu của bạn. Nút thắt sẽ không nằm ở vẻ đẹp / tổ chức / cấu trúc mã của bạn mà nằm ở các kỹ thuật bạn đã chọn. Ví dụ, bạn đang sử dụng ổ cắm không chặn? Bạn đang sử dụng thiết kế đơn luồng? Bạn có đang sử dụng hàng đợi không khóa để liên lạc giữa các luồng không? Bạn đang sản xuất rác cho GC? Bạn có đang thực hiện bất kỳ thao tác I / O chặn nào trong luồng quan trọng không? Như bạn có thể thấy điều này không liên quan gì đến sắc đẹp.


0

Điều gì quan trọng với người dùng cuối?

  • Hiệu suất
  • Tính năng / Chức năng
  • Thiết kế

Trường hợp 1: Tối ưu hóa mã xấu

  • Bảo trì cứng
  • Khó đọc nếu như là một dự án nguồn mở

Trường hợp 2: Mã tốt không được tối ưu hóa

  • Bảo trì dễ dàng
  • Trải nghiệm người dùng xấu

Dung dịch?

Dễ dàng, tối ưu hóa hiệu suất của các đoạn mã quan trọng

ví dụ:

Một chương trình bao gồm 5 Phương thức , 3 trong số đó là để quản lý dữ liệu, 1 cho việc đọc đĩa, còn lại là để ghi đĩa

3 phương thức quản lý dữ liệu này sử dụng hai phương thức I / O và phụ thuộc vào chúng

Chúng tôi sẽ tối ưu hóa các phương pháp I / O.

Lý do: Các phương thức I / O ít có khả năng thay đổi, cũng không ảnh hưởng đến thiết kế của ứng dụng và tất cả, mọi thứ trong chương trình đó đều phụ thuộc vào chúng, và do đó chúng có vẻ quan trọng về hiệu suất, chúng tôi sẽ sử dụng bất kỳ mã nào để tối ưu hóa chúng .

Điều này có nghĩa là chúng tôi có được mã tốt và thiết kế có thể quản lý được của chương trình trong khi giữ cho chương trình nhanh bằng cách tối ưu hóa một số phần nhất định của mã

Tôi đang nghĩ..

Tôi nghĩ rằng mã xấu khiến con người khó đánh bóng - tối ưu hóa và những lỗi nhỏ có thể khiến nó tệ hơn nữa, vì vậy một mã tốt cho người mới / người mới bắt đầu sẽ tốt hơn nếu chỉ viết tốt mã xấu đó.

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.