Học cách tối ưu hóa với hội [đóng]


21

Tôi là sinh viên năm thứ hai của Công nghệ trò chơi máy tính. Gần đây tôi đã hoàn thành nguyên mẫu đầu tiên của "loại" máy tìm đường riêng của mình (không sử dụng A * thay vào đó là cách tiếp cận hình học / nhận dạng mẫu, máy tìm đường chỉ cần có kiến ​​thức về địa hình trong tầm nhìn của mình để đưa ra quyết định, bởi vì tôi muốn có một AI thực sự có thể khám phá, nếu địa hình đã được biết đến, thì nó sẽ đi bộ một cách dễ dàng nhất, bởi vì đường dẫn có bộ nhớ các nút).

Dù sao câu hỏi của tôi là tổng quát hơn: Làm thế nào để tôi bắt đầu tối ưu hóa các thuật toán / vòng lặp / for_each / vv. sử dụng hội, mặc dù lời khuyên chung được hoan nghênh. Tôi đặc biệt tìm kiếm những cuốn sách hay, bởi vì thật sự rất khó để tìm thấy những cuốn sách hay về chủ đề này. Có một số bài viết nhỏ như thế này , nhưng vẫn không đủ kiến ​​thức để tối ưu hóa thuật toán / trò chơi ...

Tôi hy vọng có một cuốn sách hay hiện đại ngoài kia, mà tôi không thể tìm thấy ...


1
Điều này không trả lời trực tiếp câu hỏi của bạn, nhưng mang tính khám phá (được gọi là thích nghi) A * đã được nghiên cứu và có hiệu suất thực sự tốt (có nghĩa là bạn sẽ không cần phải tối ưu hóa nó bằng ASM). Hãy xem D * Lite .
Jonathan Dickinson

Câu trả lời:


21

Tôi sẽ là người đi ngược lại hạt gạo ở đây và nói, không bao giờ là quá sớm để tìm hiểu về tối ưu hóa, đặc biệt là tối ưu hóa lắp ráp và quan trọng hơn là gỡ lỗi trong lắp ráp. Tôi tin rằng bạn sẽ đạt được lợi ích tối đa của nó nếu bạn là sinh viên (bởi vì sau đó bạn có rất ít để mất [tức là thời gian / tiền bạc khôn ngoan]) và mọi thứ để đạt được.

Nếu bạn ở trong ngành và không được giao nhiệm vụ lắp ráp, thì không. Mặt khác, nếu bạn là sinh viên hoặc có thời gian nói chung, tôi sẽ tìm thời gian để học cách tháo rời các chương trình và xem liệu tôi có thể đưa ra giải pháp tốt hơn trình biên dịch hay không. Nếu tôi không thể, ai quan tâm! Tôi vừa học cách viết cũng như trình biên dịch và đó là một điểm cộng lớn khi bạn gặp phải một lỗi trong mã phát hành (không có ký hiệu gỡ lỗi) và nhìn chằm chằm vào phần tháo gỡ vì đó là điều duy nhất bạn có thể nhìn vào.

Câu trả lời

Đây là một trong những tài nguyên tốt nhất tôi đã tìm thấy để tìm hiểu về tối ưu hóa.

http://www.agner.org/optizes/

Lời nói

Nếu bạn đọc một số bài viết của các nhà phát triển lớn (ví dụ, lý do đằng sau việc tạo EASTL và kiểm tra mã chặt chẽ hơn sẽ dẫn bạn đến những nhận xét như đã làm điều này bởi vì GCC rất tệ khi đưa ra tuyên bố này sẽ cho bạn biết, phần lớn mọi người nói với bạn rằng trình biên dịch không phải lúc nào cũng đúng, ĐẶC BIỆT trong phát triển trò chơi) và sau đó đặt chân vào ngành công nghiệp bạn sẽ thấy rằng tối ưu hóa là một việc hàng ngày và biết đầu ra lắp ráp có nghĩa là một điểm cộng lớn. Ngoài ra, mọi người dường như không nhận ra (đặc biệt là trên stackoverflow) rằng các trò chơi hồ sơ rất khó và không phải lúc nào cũng chính xác.

Có một cảnh báo mặc dù. Bạn có thể dành thời gian để tối ưu hóa một cái gì đó và sau đó nhận ra rằng đó là thời gian lãng phí. Nhưng bạn đã học được gì? Bạn đã học cách không lặp lại sai lầm tương tự trong một trường hợp tương tự.

Theo tôi, những gì SO đang thực hiện là theo quan điểm tôn giáo đối với tuyên bố không tối ưu hóa cho đến khi bạn lập hồ sơđừng lo lắng, trình biên dịch biết rõ hơn bạn . Nó cản trở việc học. Tôi biết các chuyên gia trong ngành được trả tiền rất tốt (và ý tôi là RẤT tốt tiền) để tìm hiểu về lắp ráp để tối ưu hóa trò chơi và gỡ lỗi vì trình biên dịch kém về nó hoặc đơn giản là không thể giúp bạn, bởi vì, tốt, nó không thể (sự cố liên quan đến GPU, sự cố trong đó dữ liệu liên quan là không thể đọc được trong trình gỡ lỗi, v.v.)!

Điều gì sẽ xảy ra nếu ai đó thích làm điều đó, chưa hoàn toàn nhận ra nó, đặt câu hỏi ở đây và bị tắt / tắt bởi nhiều trình biên dịch câu trả lời biết rõ hơn bạn! và không bao giờ trở thành một trong những lập trình viên được trả lương cao?

Một suy nghĩ cuối cùng. Nếu bạn bắt đầu thực hiện việc này sớm, bạn sẽ thấy rằng bạn sẽ sớm bắt đầu viết mã ở mức tồi tệ nhất, không có cải thiện hiệu suất nào vì trình biên dịch đã tối ưu hóa nó theo cùng một cách hoặc tốt nhất, có một số cải tiến hiệu suất vì bây giờ trình biên dịch có thể tối ưu hóa nó . Trong cả hai trường hợp, nó đã trở thành thói quen và bạn không chậm hơn trong việc viết mã theo cách này so với những gì bạn đã làm trước đây. Một vài ví dụ là (còn nhiều nữa):

  1. Tăng trước trừ khi bạn thực sự muốn tăng sau
  2. Viết các vòng lặp cho các thùng chứa bằng cách sử dụng biến kích thước cục bộ không đổi thay vì gọi kích thước () trên vùng chứa trong vòng lặp.

EDIT: Cập nhật sau 8 năm nữa trong ngành. Học lắp ráp. Tìm hiểu cách tối ưu hóa hoạt động và lắp ráp chúng tạo ra (CompilerExplorer là một công cụ tuyệt vời cho việc đó). Tôi đã chạy qua vô số sự cố trong các bản dựng Thử nghiệm (bản dựng được tối ưu hóa cho thử nghiệm nội bộ) trong đó bạn không thể dựa vào trình gỡ lỗi ngay cả với các biểu tượng gỡ lỗi. Trình biên dịch đã tối ưu hóa quá nhiều thứ và lắp ráp là nguồn thông tin có giá trị duy nhất của bạn để tìm lỗi từ bãi chứa sự cố. Mỗi bản dựng mất 30 - 40 phút nếu bạn may mắn và lần đầu tiên trong hàng đợi bản dựng - vì vậy bạn không thể dựa vào một số kỹ thuật truyền thống để cách ly lỗi. Nhiều người chơi làm cho mọi thứ tồi tệ hơn. Biết lắp ráp và cách đọc lắp ráp tối ưu hóa đơn giản sẽ giúp bạn trở nên tốt hơn và cuối cùng có giá trị hơn cho nhóm.


1
Điểm tốt về tối ưu hóa trình biên dịch. Chúng là tuyệt vời để có, nhưng chúng không hoàn hảo, và không giống như những gì một số người tin rằng thường không khó để tìm thấy một tối ưu hóa đơn giản mà trình biên dịch đã không thực hiện.
aaaaaaaaaaaa

3
Cần lưu ý rằng có một sự khác biệt giữa "học cách đọc lắp ráp" và "học cách tối ưu hóa với lắp ráp". Cả hai không giống nhau và câu trả lời của bạn không thực sự chạm vào việc sử dụng lắp ráp để thực hiện tối ưu hóa. Đọc lắp ráp là một kỹ năng hữu ích, vì nó có thể giúp gỡ lỗi và phát hiện ra những nơi mà trình biên dịch không làm đúng. Nhưng điều đó rất khác so với thực tế sử dụng lắp ráp để viết các thói quen được tối ưu hóa, đòi hỏi kiến ​​thức sâu về lập lịch hướng dẫn cho một CPU cụ thể. Và đó cũng là thứ bạn không bao gồm.
Nicol Bolas

1
Ngoài ra, "Tôi chỉ học cách viết cũng như trình biên dịch" Không, bạn không biết. Bạn đã xem cách một thói quen cụ thể được biên dịch cho một CPU cụ thể. Học cách thực hiện các thói quen lắp ráp được tối ưu hóa đòi hỏi nhiều hơn là xem cách trình biên dịch biên dịch một thường trình. Bạn phải hiểu tại sao trình biên dịch lại chọn các opcodes đó để tạo lại mã C ++ cụ thể đó. Và điều đó đòi hỏi kiến ​​thức sâu sắc về CPU, lập lịch hướng dẫn, v.v. Khái quát hóa điều này đòi hỏi nhiều năm kinh nghiệm; bạn sẽ không có được nó chỉ bằng cách giải mã một vài thói quen.
Nicol Bolas

7
Vì vậy, -1 cho A: không thực sự trả lời câu hỏi về cách viết các thói quen tối ưu hóa lắp ráp. B: trình bày sai cách dễ dàng để học cách đánh bại trình biên dịch khi viết các thói quen tối ưu hóa lắp ráp. Và C: khuyến khích một lập trình viên xem xét tối ưu hóa mức lắp ráp trước khi tối ưu hóa ở cấp độ thuật toán. Ngay cả những "chuyên gia trong ngành" được trả lương cao cũng sẽ nói với bạn rằng đó là việc đặt xe trước ngựa.
Nicol Bolas

2
@Samaursa: Không ai nói rằng mọi người không nên "hiểu về việc tháo gỡ và cách tối ưu hóa mã". Đây không phải là một cuộc tranh luận tôn giáo; đó là một vấn đề thực tế đơn giản. Mọi người đã dành hàng thế kỷ để tối ưu hóa một số thói quen chỉ để tìm ra rằng nó không có nghĩa gì đối với hiệu suất tổng thể. Học cách tối ưu hóa thuật toán là một kỹ năng có giá trị cao. Học cách đọc lắp ráp là một kỹ năng bán giá trị. Học cách viết thói quen lắp ráp là một kỹ năng hiếm khi được sử dụng. Và ngày nay, tối ưu hóa tốt nhất đến từ việc sử dụng bộ đệm tốt hơn, không phải lắp ráp bằng tay.
Nicol Bolas

22

Mẹo đầu tiên bạn sẽ nhận được là - không.

Trình biên dịch hiện đại thực sự thực sự rất tốt trong việc tối ưu hóa mã, và sẽ có nhiều khả năng thực hiện công việc tốt hơn so với bất kỳ ngôn ngữ lắp ráp tự cuộn nào bạn có thể viết.

Ngoại lệ sẽ là bất kỳ trường hợp cụ thể nào mà bạn đã xác định chắc chắn rằng trình biên dịch đang thực hiện công việc tối ưu hóa kém, vì vậy đó là mẹo thứ hai. Không có hướng dẫn chung nào ở đây, bạn cần biết mã của riêng mình, biết nó đang làm gì, có thể nhảy vào tháo gỡ nó và có thể xác định chắc chắn rằng trình biên dịch đang làm một công việc tồi tệ.

Ngay cả trong trường hợp này bạn vẫn có thể không muốn. Bạn cần chắc chắn rằng sẽ không có bất kỳ chi phí bảo trì liên tục nào cho bạn. Bạn có thể muốn quay lại mã này sau 6 tháng và sửa đổi một phần của mã đó, hoặc bạn có thể tìm thấy một lỗi cực kỳ tinh vi sẽ khó khắc phục hơn trong phiên bản ngôn ngữ lắp ráp. Ngay cả khi bạn nghĩ rằng bạn đã xử lý tất cả các lỗi, một khi chương trình của bạn chuyển sang các lỗi công khai mà bạn chưa từng nghĩ có thể xảy ra sẽ trở thành hiện thực đối với bạn. Đó là một cái mở mắt (và một kinh nghiệm khiêm tốn).

Và ngay cả khi bạn vui vẻ chấp nhận điều đó, bạn vẫn có thể thấy rằng hoàn toàn không có cải thiện hiệu suất có thể đo lường được vì nút cổ chai chính của bạn có thể ở đâu đó hoàn toàn khác trong chương trình của bạn. Vì vậy, điều đó đưa tôi trở lại số 1 một lần nữa. Đừng.


15

Thông thường, tối ưu hóa vững chắc không phụ thuộc vào việc sử dụng hội hoặc thực hiện tối ưu hóa vi mô với mã bằng các ngôn ngữ cấp cao hơn. Nếu bạn đọc nhiều tài liệu nghiên cứu (như tôi làm - hoặc cố gắng!), Bạn sẽ thấy rằng thông thường các cải tiến được thực hiện cho các thuật toán ở cấp độ khái niệm rộng hơn, "định tính", thay vì ở mức "định lượng" hơn mức độ tối ưu hóa vi mô. Tôi sẽ nhấn mạnh rằng các mức tăng theo thứ tự có nhiều khả năng được tìm thấy bằng cách xem xét các thuật toán từ quan điểm này, hoặc từ véc tơ / song song hóa các giải pháp hiện có.

Phải nói rằng, gần đây tôi đã tình cờ thấy điều này , có thể là một hướng tốt để học x86 ASM dành riêng cho các nhà phát triển trò chơi.


ĐỊA CHỈ

Hai nguồn ngoài đỉnh đầu của tôi:

Ngoài ra, đọc các tài liệu nghiên cứu là một cách tuyệt vời để theo dõi các quá trình suy nghĩ của người khôn ngoan khi họ tối ưu hóa các thuật toán để có hiệu suất tốt hơn. Thông thường, lợi nhuận được nhìn thấy bởi:

  • Giảm việc sử dụng các hoạt động tốn kém nhất (div, SQRT, trig ops và điều kiện, chủ yếu);
  • Cải thiện hiệu suất bộ đệm thông qua việc sử dụng các cấu trúc dữ liệu hiệu quả hơn, căn chỉnh bộ nhớ và giảm các điều kiện;
  • Giảm chất lượng đầu ra trong các khu vực chấp nhận được để cải thiện hiệu suất;
  • Vector hóa (SIMD);
  • Song song hóa (phân luồng, bao gồm chuyển các tác vụ sang GPU);
  • Và tất nhiên (ngày càng hiếm) lắp ráp bằng tay. Đầu tiên kiểm tra các hội đồng C / C ++ để xem trình biên dịch đang đưa ra các lựa chọn không tối ưu, tất nhiên. Bạn sẽ tìm thấy nhiều hơn về điều này trong các bài báo cũ từ thập niên 80 và 90, IME.

Đọc nghiên cứu cũng giữ cho bạn ở đỉnh cao của lĩnh vực của bạn, thay vì chờ đợi kiến ​​thức đó để lọc xuống ngành công nghiệp.


bạn nói về tối ưu hóa thuật toán nhưng bạn không cung cấp thông tin nào về nó, nếu chúng tôi làm theo lời khuyên của bạn và xem xét điều đó thay vào đó bạn có thể đưa ra một số hướng?
Skeith

Trong thực tế, tôi có đề cập đến nó; bạn cần nghiên cứu các thuật toán, hiểu các nhà khoa học máy tính làm gì để cải thiện hiệu suất một cách định tính. Đắm chìm trong điều này đủ, và trong thời gian, bạn bắt đầu suy nghĩ theo cách tương tự. Những nỗ lực gia tăng ở đây đã trả hết thời gian lớn, trái ngược với việc dành nhiều năm (và gần đây tôi đã thấy điều này được đề cập trên một diễn đàn ASM) làm chủ mọi thứ trong và ngoài (ví dụ). kiến trúc x86. Săn trò chơi lớn: học cách giảm bớt các vấn đề xuống chính cốt lõi của chúng, và sau đó quyết định những gì là thừa để tối ưu hóa. Xem sách giới thiệu ở trên.
Kỹ sư

@NickWiggill Nguồn tài liệu nghiên cứu thông thường của bạn là gì?
kizzx2

3

Tôi nghĩ rằng nó có thể là quá sớm.

Dù sao, điều quan trọng là phải hiểu rằng chính trình biên dịch không tạo ra mã chậm hơn so với tương đương lắp ráp, bạn không nhận được bất kỳ hiệu suất nào chỉ bằng cách viết mã lắp ráp giống như trình biên dịch.

Để bắt đầu ít nhất tập trung vào tối ưu hóa lắp ráp. Igor Ostrovsky có một vài bài viết hay thể hiện một số điều cơ bản: http://igoro.com/archive/fast-and-slow-if-statements-branch-prediction-in-modern- Processors /

Xin lưu ý rằng các dự đoán sai về chi nhánh và lỗi bộ nhớ cache là những gì bạn chủ yếu nên tối ưu hóa, ngay cả khi bạn phải trả bằng cách thực hiện một số thao tác số học bổ sung, điều đó thường đáng để tránh một nhánh không thể đoán trước hoặc đọc ngẫu nhiên từ quá nhiều bộ nhớ.

Và tất nhiên, quan trọng nhất, tối ưu hóa thuật toán của bạn đầu tiên. Việc triển khai chậm thuật toán nhanh hầu như sẽ luôn nhanh hơn so với triển khai nhanh thuật toán chậm.


2

Cuốn sách này đặc biệt tốt cho một cuốn sách văn bản. Nhưng nó không đặc biệt hướng đến tối ưu hóa. Ngôn ngữ hội cho bộ xử lý x86, phiên bản thứ 6

Đó là nhiều hơn về việc dạy các nguyên tắc cơ bản của lắp ráp, sử dụng MASM. Sau đó, đến cuối cuốn sách, chúng ta sẽ tìm hiểu cách lắp ráp nội tuyến với c ++ và tích hợp nó vào các chương trình lớn hơn.

Tôi đặt nó ở đây bởi vì nó có ý nghĩa để tìm hiểu các nguyên tắc cơ bản của lắp ráp trước khi bạn học cách tối ưu hóa các chương trình với nó.

Tôi thích cuốn sách này bởi vì Irvine dạy bạn cách sử dụng các công cụ cần thiết để viết các chương trình masm. Anh đặc biệt đi sâu vào cách sử dụng IDE (Visual Studio C ++) và trình gỡ lỗi. Mỗi chương có một vài video dành riêng cho việc giải quyết vấn đề. Một số thông tin này có sẵn miễn phí trên trang web được liệt kê.


1
"thật hợp lý khi tìm hiểu các nguyên tắc cơ bản của lắp ráp trước khi bạn học cách tối ưu hóa các chương trình với nó" - lời khuyên tốt.
Maximus Minimus
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.