Bạn tối ưu hóa ở đâu?


9

Có hai lĩnh vực có thể tối ưu hóa cho tốc độ trong:

  • Nơi dành nhiều thời gian nhất
  • Mã được gọi là nhiều nhất

Đó là nơi tốt nhất để bắt đầu tối ưu hóa?

Thông thường mã được gọi là thường xuyên nhất có thời gian thực hiện thấp. Bạn có tối ưu hóa các khu vực chậm hơn, ít được gọi là hơn hoặc dành thời gian tối ưu hóa các khu vực nhanh hơn, được sử dụng nhiều?


Tối ưu hóa khu vực của ứng dụng gây căng thẳng cho khách hàng hoặc kiến ​​trúc của bạn nhất, phụ thuộc vào việc khách hàng hoặc máy chủ của bạn đang phàn nàn lớn nhất.
Andy

Đó là một phương trình giá trị - câu trả lời có thể là một trong hai. Khi bạn không có phân tích thực sự, bạn đi theo đường ruột của mình, dựa trên khả năng hoàn trả những ý tưởng tốt nhất của bạn.
Nicole

Cũng không. Tìm mã nằm trong ngăn xếp một phần lớn thời gian.
Mike Dunlavey

Câu trả lời:


4

Bạn nên bỏ qua hiệu quả nhỏ 95% thời gian. Đầu tiên, làm cho nó hoạt động chính xác , sau đó phân tích ...

Thiết kế của bạn.

Sự lựa chọn của bạn về các thuật toán cấp cao có thể có tác động rất lớn đến hiệu suất tổng thể của phần mềm của bạn, đến mức một lựa chọn có vẻ tầm thường có thể có nghĩa là sự khác biệt giữa việc chờ đợi 20 phút để chương trình bắt đầu và có giao diện người dùng nhanh nhạy.

Ví dụ: trong trò chơi 3D: nếu bạn bắt đầu với một danh sách các đối tượng phẳng đơn giản cho biểu đồ cảnh của mình, bạn sẽ thấy hiệu suất cực kỳ kém đối với số lượng đối tượng tương đối nhỏ; nhưng nếu bạn thực hiện một hệ thống phân cấp âm lượng (như octree hoặc BVH) và các phần của cây trong khi vẽ, bạn sẽ thấy hiệu suất tăng mạnh.

Khi thiết kế của bạn có vẻ đúng, thì bạn có thể chuyển sang ...

Logic cấp thấp.

Các thuật toán cấp thấp hơn cũng có thể có tác động đáng kể. Khi thực hiện xử lý hình ảnh, ví dụ, nếu bạn đọc hình ảnh không đúng thứ tự, bạn sẽ gặp phải tình trạng chậm lớn khi bạn chạy vào bộ nhớ cache L2 không đổi; sắp xếp lại các hoạt động của bạn có thể có nghĩa là tăng hiệu suất gấp mười lần.

Tại thời điểm này, lập hồ sơ và tìm nơi dành phần lớn thời gian của chương trình và tìm cách loại bỏ nó.


Chương trình tôi đang làm là chính xác. Chúng tôi muốn làm cho nó nhanh hơn nếu có thể, vì đây là một dịch vụ web có thể mất từ ​​30 giây đến một phút để chạy.
Michael K

1
@Michael: Trong trường hợp đó, đã đến lúc cần một số công cụ định hình để phân tích một số thực thi chương trình điển hình và xác định chính xác các phần của mã đang chạy chậm nhất. Tôi thực sự khuyên bạn nên sử dụng một công cụ cho việc này. Bạn có thể thực hiện một số tiền nhất định bằng trực giác, nhưng đôi khi bạn sẽ thấy bất ngờ khi một API gọi đó chiếm một lượng thời gian đáng kể, thay vì mã của riêng bạn. Trong trường hợp đó, đã đến lúc xem xét API và xem những chức năng nào khác có sẵn hoặc nếu bạn nên viết chức năng thay thế của riêng mình ... Hog hiệu suất thực tế không phải lúc nào cũng là hiệu suất đáng ngờ ...
FrustratedWithFormsDesigner

1
Chúng tôi có một công cụ định hình. Tôi vẫn đang học nó và phải làm gì với thông tin. Đây là một chủ đề tương đối mới đối với tôi và rất thú vị.
Michael K

@Michael: Tốt! Bạn đang (hy vọng) theo cách của bạn để điều chỉnh hiệu suất thành công! :)
Thất vọngWithFormsDesigner

+1: Đây là những gì tôi sẽ nói, nhưng hùng hồn hơn nhiều.
Dominique McDonnell

3

Đầu tiên, chạy một trình lược tả để tìm ra nơi mã của bạn đang sử dụng thời gian.

Sau đó, nhìn vào những nơi đó để xem cái nào dễ nhìn tối ưu hóa.

Tìm kiếm các bản sửa lỗi dễ nhất sẽ thu được lợi nhuận lớn nhất trước tiên (hãy chọn loại quả treo thấp). Đừng lo lắng quá nhiều về tầm quan trọng của nó, chính xác. Nếu nó dễ, sửa nó. Nó sẽ thêm lên. 25 bản sửa lỗi dễ dàng có thể nhanh hơn 1 bản sửa lỗi lớn và hiệu ứng tích lũy của chúng có thể lớn hơn. Nếu khó, hãy ghi chú hoặc gửi báo cáo lỗi để bạn có thể ưu tiên báo cáo sau. Đừng lo lắng quá nhiều về "lớn" hay "nhỏ" vào thời điểm này - chỉ cần làm điều đó cho đến khi bạn có được các chức năng đang sử dụng rất ít thời gian. Khi bạn thực hiện việc này, bạn sẽ có ý tưởng tốt hơn về vấn đề nào khác mà bạn phát hiện ra có thể nhận được chiến thắng lớn nhất cho khoản đầu tư ít thời gian nhất.

Đừng quên theo dõi với hồ sơ sau khi sửa lỗi của bạn dưới dạng thử nghiệm hồi quy, để xác minh rằng các thay đổi hiệu suất của bạn có các hiệu ứng mà bạn hy vọng. Ngoài ra, đừng quên chạy bộ hồi quy của bạn, để đảm bảo không có chức năng nào bị hỏng. Đôi khi hiệu suất kém cho thấy xung quanh công việc và cố gắng khắc phục hiệu suất sẽ phá vỡ chức năng.

Các chức năng nhỏ không thể được tối ưu hóa nhưng đang sử dụng nhiều thời gian vẫn có thể là gợi ý về nơi tối ưu hóa. Tại sao chức năng đó được gọi là rất nhiều? Có một hàm gọi hàm nhỏ đó không cần sử dụng nhiều như vậy không? Là công việc đang được nhân đôi, hoặc công việc không cần thiết đang được thực hiện? Tra cứu ngăn xếp cho số lần nó được gọi cho đến khi bạn tự tin nên gọi nó thường xuyên và xem bạn có tìm thấy hàm lớn hơn với thuật toán không hiệu quả không.

Chỉnh sửa để thêm: Vì bạn có chức năng cụ thể mất nhiều thời gian, hãy thử thực hiện các bước trên với chỉ chức năng cụ thể đó được chạy 10 lần hoặc lâu hơn.


2

Khó mà nói ra được. Điều này thực sự phụ thuộc vào những gì tha mã đang làm. Chạy thử nghiệm hiệu suất, nhận hồ sơ hiệu suất và xem và xem thời gian thực tế được dành cho các lĩnh vực khác nhau. Khái quát của bạn là ... khái quát hóa và nó thay đổi tùy theo dự án.

Ví dụ, mã được gọi nhiều nhất có thể chỉ cần đăng nhập vào tệp hoặc bảng điều khiển. Không có nhiều điểm để tối ưu hóa rằng nếu nó đã có một hoặc hai dòng mã không thể được làm đơn giản hơn và có thể là bất kỳ nỗ lực nào để tối ưu hóa một cái gì đó như thế này có thể không đáng giá cho việc thực sự mã hóa nó. Mã được gọi ít nhất có thể là một số truy vấn có kích thước quái vật được sử dụng trong một số hàm phức tạp khủng khiếp. Các chức năng chỉ có thể được gọi là 100 lần so với toàn bộ chạy thi (so với 10000 cho báo cáo kết quả khai thác gỗ đơn giản), nhưng nếu nó mất 20 giây cho mỗi lần gọi nó chạy, có lẽ đó của nơi tối ưu hóa nên bắt đầu? Hoặc có thể là cách khác, với truy vấn lớn được gọi nhiều nhất và câu lệnh ghi nhật ký chỉ được gọi một cho mỗi 100 truy vấn ...

Tôi thường không lo lắng về điều này (cho đến khi tôi cần điều chỉnh hiệu suất) trừ khi tôi có một số ý tưởng trước thời gian những gì sẽ xảy ra.


1

Chà, "chúng tôi" thường không tối ưu hóa cho đến khi có nhu cầu tối ưu hóa rõ ràng khi một thứ gì đó chậm đến mức không thể chấp nhận được.

Và khi nhu cầu này tự thể hiện, nó thường mang theo những gợi ý tốt về chính xác những gì cần để tối ưu hóa.

Vì vậy, câu trả lời là bình thường: "Nó phụ thuộc."


1

Bạn nên sử dụng một trình lược tả trên một số ít các lần chạy điển hình và xem xét tổng thời gian dành cho mỗi phần của mã, bất kể bạn có thường xuyên đến đó hay không. Tối ưu hóa các bộ phận này sẽ luôn luôn tăng tốc độ.

Tùy thuộc vào mức độ ngôn ngữ thực hiện của bạn ở mức độ thấp, bạn cũng nên tìm hiểu phần nào gây ra hầu hết các lỗi bộ nhớ cache. Hợp nhất mã gọi sẽ giúp ở đây.


1

Vấn đề là cụm từ "nơi dành nhiều thời gian nhất" là mơ hồ.

Nếu nó có nghĩa là "nơi bộ đếm chương trình được tìm thấy thường xuyên nhất" thì tôi đã thấy các chương trình dành nhiều thời gian nhất cho việc so sánh chuỗi, cấp phát bộ nhớ, các hàm thư viện toán học. Nói cách khác, các chức năng mà lập trình viên hàng ngày không bao giờ nên chạm vào.

Nếu nó có nghĩa là "trong đó mã của lập trình viên là các câu lệnh được thực thi tiêu tốn một phần lớn thời gian" thì đó là một khái niệm hữu ích hơn.

Vấn đề với khái niệm "mã được gọi là nhiều nhất" là, lượng thời gian cần thiết là sản phẩm của tần suất được gọi và thời gian cho mỗi cuộc gọi (bao gồm cả calle và I / O). Vì lượng thời gian cần thiết có thể thay đổi theo nhiều bậc độ lớn, nên số lần được gọi không cho bạn biết mức độ của một vấn đề. Hàm A có thể được gọi 10 lần và mất 0,1 giây, trong khi chức năng B có thể được gọi 1000 lần và mất một phần triệu giây.

Một điều sẽ cho bạn biết nơi để tìm là đây: Bất cứ khi nào một dòng mã gây ra thời gian để sử dụng nó là trên ngăn xếp . Vì vậy, ví dụ, nếu một dòng mã là một điểm nóng hoặc nếu đó là một cuộc gọi đến chức năng thư viện hoặc nếu đó là cuộc gọi thứ 20 trong cây cuộc gọi 30 cấp, nếu nó chịu trách nhiệm 20% thời gian , sau đó nó nằm trên stack 20% thời gian. Mỗi mẫu thời gian ngẫu nhiên của ngăn xếp sẽ có 20% cơ hội hiển thị nó. Hơn nữa, nếu các mẫu có thể được lấy trong I / O, chúng sẽ cho bạn thấy những tài khoản nào cho I / O, có thể gây lãng phí hoặc nhiều hơn khi chu kỳ CPU bị lãng phí.

Và điều này là hoàn toàn độc lập với bao nhiêu lần nó được gọi.


Bởi 'lập trình viên hàng ngày không bao giờ nên chạm vào', bạn có nghĩa là không có khả năng chạm vào? Ngoài ra, lấy mẫu ngăn xếp là một phương pháp định hình thực tế?
Michael K

@Michael: Có, lấy mẫu ngăn xếp là một phương pháp mà các trình biên dịch hiện đại dựa trên, chẳng hạn như Zoom . Ngoài ra, hoàn toàn thủ công hoạt động tốt đáng ngạc nhiên .
Mike Dunlavey

Rất thú vị. Tôi đã có một số nghiên cứu để làm bây giờ!
Michael K

@Michael: Nó giống như khái niệm pháp lý về trách nhiệm chung. Tại một thời điểm, trách nhiệm đối với PC trong một lệnh là trách nhiệm chung của không chỉ hướng dẫn đó, mà là mọi cuộc gọi phía trên nó trên ngăn xếp. Loại bỏ bất kỳ một trong số họ sẽ ngăn nó vào trạng thái cụ thể đó.
Mike Dunlavey

0

Tối ưu hóa nơi dành nhiều thời gian nhất trừ khi không có lý do cụ thể nào (nghĩa là phần lớn thời gian được dành cho xử lý không đồng bộ mà con người không thực sự quan tâm liệu nó hoàn thành trong 5 phút hay 10 phút). Mã được gọi là nhiều nhất, một cách tự nhiên, sẽ có xu hướng chiếm một phần tương đối lớn trong tổng thời gian đã trôi qua đơn giản bởi vì ngay cả thời gian thực hiện ngắn cũng tăng lên khi bạn thực hiện hàng ngàn lần.


0

Bạn phải làm việc với mã đang mất nhiều thời gian nhất. Cải thiện mã chỉ chiếm một vài phần trăm thời gian chạy chỉ có thể cung cấp cho bạn một cải tiến nhỏ.

Bạn đã thực hiện các phép đo để bạn biết mã nào đang mất nhiều thời gian nhất?


0

Tôi đã từng làm điểm chuẩn và tiếp thị cho một nhà cung cấp siêu máy tính, do đó, việc đánh bại đối thủ một cách nhanh chóng không phải là điều quan trọng nhất, đó là điều DUY NHẤT. Loại kết quả đó yêu cầu bạn sử dụng kết hợp các thuật toán tốt và cấu trúc dữ liệu cho phép các phần lớn nhất của CPU chạy một cách hiệu quả. Điều đó có nghĩa là bạn phải có một ý tưởng khá tốt về các hoạt động chuyên sâu tính toán nhất sẽ diễn ra và loại cấu trúc dữ liệu nào sẽ cho phép chúng chạy nhanh nhất. Sau đó, vấn đề là xây dựng ứng dụng xung quanh các cấu trúc dữ liệu / hạt nhân được tối ưu hóa đó.

Theo nghĩa chung hơn, điển hình sau khi điều chỉnh thực tế. Bạn hồ sơ, nhìn vào các điểm nóng, và các điểm nóng mà bạn nghĩ rằng bạn có thể tăng tốc là những điểm bạn làm việc. Nhưng hiếm khi phương pháp này sẽ cung cấp cho bạn bất cứ điều gì gần với việc thực hiện nhanh nhất có thể.

Tuy nhiên, nhìn chung hơn, (những sai lầm thuật toán không chịu được), đối với các máy móc hiện đại, bạn phải nghĩ rằng hiệu suất được xác định bởi ba điều: truy cập dữ liệu, truy cập dữ liệu và truy cập dữ liệu! Tìm hiểu về hệ thống phân cấp bộ nhớ (thanh ghi, bộ nhớ cache, TLB, trang, v.v.) và thiết kế cấu trúc dữ liệu của bạn để sử dụng chúng càng tốt càng tốt. Nói chung, điều này có nghĩa là bạn muốn có các vòng lặp chạy trong một bộ nhớ nhỏ gọn. Thay vào đó, nếu bạn chỉ viết (hoặc được cung cấp) một ứng dụng và sau đó cố gắng tối ưu hóa nó, bạn thường bị ảnh hưởng bởi các cấu trúc dữ liệu sử dụng kém hệ thống phân cấp bộ nhớ và thay đổi cấu trúc dữ liệu thường liên quan đến việc tái cấu trúc chính, vì vậy bạn thường bị mắc kẹt.


0

Nếu bạn muốn hoàn vốn cho nỗ lực tối ưu hóa của mình, bạn phải xem mã mất nhiều thời gian nhất. Mục tiêu chung của tôi là một cái gì đó chiếm ít nhất 80% thời gian. Tôi thường nhắm mục tiêu tăng 10 lần hiệu suất. Điều này đôi khi đòi hỏi một sự thay đổi lớn trong cách mã được thiết kế. Loại thay đổi này mang lại cho bạn thứ gì đó chạy nhanh hơn khoảng bốn lần.

Hiệu suất tốt nhất mọi thời đại của tôi là giảm thời gian chạy từ 3 ngày xuống còn 9 phút. Mã tôi tối ưu hóa đã đi từ 3 ngày đến 3 phút. Ứng dụng thay thế ứng dụng đó đã giảm xuống còn 9 giây, nhưng yêu cầu thay đổi ngôn ngữ và viết lại hoàn toàn.

Tối ưu hóa một ứng dụng đã nhanh chóng có thể là một việc vặt. Tôi vẫn sẽ nhắm mục tiêu khu vực mất nhiều thời gian nhất. Nếu bạn có thể lấy thứ gì đó bằng cách sử dụng 10% thời gian để trả lại ngay lập tức, bạn vẫn cần 90% thời gian. Bạn nhanh chóng đạt được quy tắc giảm lợi nhuận.

Tùy thuộc vào những gì bạn đang tối ưu hóa cho quy tắc vẫn được áp dụng. Tìm kiếm người dùng tài nguyên chính và tối ưu hóa chúng. Nếu tài nguyên bạn đang tối ưu hóa là nút cổ chai hệ thống, bạn có thể thấy rằng tất cả những gì bạn làm là thay đổi nút cổ chai sang tài nguyên khác.


0

Thông thường, nó sẽ là các hàm thịt hơn trong hầu hết các trường hợp, không phải là các hàm được gọi thường xuyên nhất một tỷ lần trong một vòng lặp.

Khi bạn thực hiện hồ sơ dựa trên mẫu (bằng công cụ hoặc bằng tay), thường thì các điểm nóng lớn nhất sẽ xuất hiện trong các cuộc gọi lá nhỏ làm những việc đơn giản, như một hàm để so sánh hai số nguyên.

Chức năng đó thường sẽ không được hưởng lợi từ nhiều, nếu có, tối ưu hóa. Ít nhất là những điểm nóng dạng hạt hiếm khi được ưu tiên hàng đầu. Đó là hàm gọi hàm lá đó có thể là người gây rắc rối hoặc hàm gọi hàm gọi hàm, giống như thuật toán sắp xếp tối ưu phụ. Với các công cụ tốt, bạn có thể đi sâu từ callee đến người gọi, và cũng xem ai dành nhiều thời gian nhất để gọi callee.

Đó thường là một sai lầm khi ám ảnh về calle và không nhìn vào người gọi xuống biểu đồ cuộc gọi trong một phiên hồ sơ trừ khi bạn đang làm những việc rất kém hiệu quả ở cấp độ vi mô. Nếu không, bạn có thể đổ mồ hôi quá nhiều cho những thứ nhỏ nhặt và đánh mất hình ảnh lớn. Chỉ cần có một hồ sơ trong tay sẽ không bảo vệ bạn khỏi bị ám ảnh bởi những thứ tầm thường. Đó chỉ là một bước đầu tiên đi đúng hướng.

Ngoài ra, bạn phải chắc chắn rằng bạn đang lập hồ sơ các hoạt động phù hợp với những điều người dùng thực sự muốn làm, nếu không thì hoàn toàn kỷ luật và khoa học trong các phép đo và điểm chuẩn của bạn là vô ích vì nó không phù hợp với những gì khách hàng làm với sản phẩm. Tôi đã có một đồng nghiệp một lần điều chỉnh địa ngục ra khỏi thuật toán phân chia để chia một khối lập phương thành một tỷ khía cạnh và anh ta rất tự hào về điều đó .... ngoại trừ người dùng không chia các khối 6 đa giác đơn giản thành một tỷ các khía cạnh. Toàn bộ sự việc đã chậm lại khi thu thập thông tin khi nó cố gắng chạy trên một mẫu xe sản xuất với hơn 100.000 đa giác để phân chia, tại thời điểm đó, nó thậm chí không thể thực hiện phân chia 2 hoặc 3 cấp độ mà không làm chậm quá trình thu thập thông tin. Nói một cách đơn giản, anh ta đã viết mã siêu tối ưu hóa cho các kích thước đầu vào nhỏ một cách phi thực tế mà không '

Bạn phải tối ưu hóa các trường hợp sử dụng thực tế phù hợp với sở thích của người dùng hoặc nếu không thì vô ích, vì tất cả những tối ưu hóa đó có xu hướng ít nhất làm giảm khả năng duy trì của mã có ít lợi ích người dùng và chỉ có tất cả những tiêu cực cho codebase.

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.