Tôi có đang tối ưu hóa sớm?


9

Tôi hiện đang ở giai đoạn thiết kế kiến ​​trúc dựa trên thành phần trong C ++.

Thiết kế hiện tại của tôi bao gồm việc sử dụng các tính năng như:

  • std::vectors của std::shared_ptrs để giữ các thành phần
  • std::dynamic_pointer_cast
  • std::unordered_map<std::string,[yada]>

Các thành phần sẽ đại diện cho dữ liệu và logic của các mục khác nhau cần thiết trong một phần mềm giống như trò chơi, chẳng hạn như Đồ họa, Vật lý, AI, Âm thanh, v.v.

Tôi đã đọc tất cả các vị trí mà bộ nhớ cache rất khó thực hiện, vì vậy tôi đã chạy một số thử nghiệm, điều đó khiến tôi tin rằng, thực sự, nó có thể làm chậm một ứng dụng.

Tôi chưa thể kiểm tra các tính năng ngôn ngữ đã nói ở trên, nhưng ở nhiều nơi người ta nói rằng những thứ này có xu hướng tốn kém và nên tránh nếu có thể.

Vì tôi đang ở giai đoạn thiết kế kiến ​​trúc, và chúng sẽ được bao gồm trong cốt lõi của thiết kế, nên tôi cố gắng tìm cách tránh chúng ngay bây giờ, vì sẽ rất khó để thay đổi nó sau này nếu có hiệu suất vấn đề gì?

Hay tôi chỉ bị cuốn vào việc tối ưu hóa sớm?


3
Tôi sẽ rất miễn cưỡng khi giải quyết một thiết kế khiến cho việc thay đổi sau này trở nên rất khó khăn, bất kể các vấn đề về hiệu suất. Tránh điều đó nếu bạn có thể. Có nhiều thiết kế vừa linh hoạt vừa nhanh chóng.
candied_orange

1
Thậm chí không biết chi tiết, câu trả lời cho câu hỏi này hầu như luôn luôn là "CÓ !!".
Mawg nói rằng phục hồi Monica

2
@Mawg "... Tuy nhiên, chúng ta không nên bỏ qua cơ hội của mình trong 3% quan trọng đó." Vì đây là cốt lõi của thiết kế, làm thế nào tôi có thể biết liệu tôi có làm việc với 3% này không?
Vaillancourt

1
Một điểm xuất sắc, Alexandre (+1), và, vâng, tôi biết rằng một nửa trích dẫn cuối cùng, hầu như không bao giờ được đề cập :-) Nhưng, để quay lại nhận xét của tôi trước đó (được phản ánh trong câu trả lời được trích dẫn) , the answer to this question is almost always a resounding "YES !!". Tôi vẫn cảm thấy tốt hơn là để nó hoạt động trước & tối ưu hóa sau, nhưng YMMV, mọi người đều có ý kiến ​​của mình, tất cả đều hợp lệ và chỉ OP mới thực sự có thể trả lời câu hỏi - chủ quan của riêng mình.
Mawg nói rằng phục hồi Monica

1
@AlexandreVaillancourt Tiếp tục đọc bài viết của Knuth (PDF, trích dẫn xuất phát từ phía bên phải của trang có nhãn 268, trang 8 trong trình đọc PDF). "... anh ấy sẽ khôn ngoan khi xem xét kỹ mã quan trọng, nhưng chỉ sau khi mã đó được xác định. Thường là một sai lầm khi đưa ra phán đoán tiên nghiệm về những phần nào của chương trình thực sự quan trọng, vì kinh nghiệm phổ quát về các lập trình viên đã và đang sử dụng các công cụ đo lường đã đoán được rằng những dự đoán trực quan của họ thất bại. " (nhấn mạnh của anh ấy)
8bittree

Câu trả lời:


26

Không đọc gì ngoài tiêu đề: Có.

Sau khi đọc văn bản: Có. Mặc dù nó sự thật rằng bản đồ và gợi ý chia sẻ vv không thực hiện tốt bộ nhớ cache-khôn ngoan, bạn sẽ chắc chắn nhất tìm thấy rằng những gì bạn muốn sử dụng chúng cho - như xa như tôi hiểu - không phải là nút cổ chai và sẽ không được tổ chức tại hoặc sử dụng bộ nhớ cache hiệu quả bất kể cấu trúc dữ liệu.

Viết phần mềm tránh những sai lầm ngu ngốc nhất, sau đó kiểm tra, sau đó tìm các nút thắt cổ chai, sau đó tối ưu hóa!

Fwiw: https://xkcd.com/1691/


3
Đã đồng ý. Làm cho nó hoạt động ngay trước tiên, bởi vì nó sẽ không hoạt động nhanh như thế nào. Và luôn nhớ rằng các tối ưu hóa hiệu quả nhất không liên quan đến việc điều chỉnh mã, chúng liên quan đến việc tìm kiếm một thuật toán khác hiệu quả hơn.
Todd Knarr

10
Tôi muốn chỉ ra rằng dòng đầu tiên không đúng vì tối ưu hóa luôn sớm, nhưng vì tối ưu hóa chỉ không sớm nếu bạn biết bạn cần nó, trong trường hợp đó bạn sẽ không hỏi về nó. Vì vậy, dòng đầu tiên chỉ đúng vì thực tế là bạn đang đặt câu hỏi về việc tối ưu hóa có sớm hay không có nghĩa là bạn không chắc chắn rằng bạn cần tối ưu hóa, theo định nghĩa làm cho nó sớm. Phù.
Jörg W Mittag

@ JörgWMittag: đã đồng ý.
steffen

3

Tôi không quen thuộc với C ++, nhưng nói chung là tùy.

Bạn không cần phải tối ưu hóa sớm các thuật toán bị cô lập, nơi bạn có thể dễ dàng tối ưu hóa khi nói đến điều đó.

Tuy nhiên, bạn cần có được thiết kế tổng thể của ứng dụng để đạt được các chỉ số hiệu suất chính mong muốn.

Ví dụ: nếu bạn cần thiết kế một ứng dụng để phục vụ hàng triệu yêu cầu mỗi giây, bạn cần suy nghĩ về khả năng mở rộng của ứng dụng khi thiết kế ứng dụng, thay vì làm cho ứng dụng hoạt động.


3

Nếu bạn phải hỏi, thì có. Tối ưu hóa sớm có nghĩa là tối ưu hóa trước khi bạn chắc chắn có một vấn đề hiệu suất đáng kể.


1

ECS? Tôi thực sự sẽ đề xuất rằng nó có thể không còn sớm nếu như vậy để suy nghĩ nhiều về khía cạnh định hướng dữ liệu của thiết kế và các đại diện khác nhau bởi vì nó có thể ảnh hưởng đến thiết kế giao diện của bạn và sau này rất tốn kém để thay đổi muộn tro choi. Ngoài ra ECS chỉ đòi hỏi rất nhiều công việc và suy nghĩ thẳng thắn và tôi nghĩ rằng đáng để sử dụng một số thời gian đó để đảm bảo rằng nó sẽ không mang lại cho bạn sự đau buồn về hiệu suất ở cấp độ thiết kế khi đưa nó vào trung tâm của bạn toàn bộ động cơ kỳ dị. Phần này trừng mắt với tôi:

unordered_map<string,[yada]>

Ngay cả với tối ưu hóa chuỗi nhỏ, bạn vẫn có một thùng chứa có kích thước thay đổi (chuỗi) bên trong một thùng chứa có kích thước thay đổi khác (unordered_maps). Trong thực tế, tối ưu hóa chuỗi nhỏ thực sự có thể có hại như trong trường hợp này nếu bảng của bạn rất thưa thớt, vì tối ưu hóa chuỗi nhỏ sẽ ngụ ý rằng mỗi chỉ mục không sử dụng của bảng băm vẫn sẽ sử dụng nhiều bộ nhớ hơn cho tối ưu hóa SS ( sizeof(string)sẽ lớn hơn nhiều) đến mức tổng chi phí bộ nhớ của bảng băm của bạn có thể đắt hơn bất cứ thứ gì bạn lưu trữ vào nó, đặc biệt nếu đó là một thành phần đơn giản như một thành phần vị trí, ngoài việc phát sinh thêm bộ nhớ cache với bước tiến lớn để có được từ một mục trong bảng băm tiếp theo.

Tôi giả sử chuỗi là một loại khóa, như ID thành phần. Nếu vậy, điều này đã làm cho mọi thứ rẻ hơn đáng kể:

unordered_map<int,[yada]>

... Nếu bạn muốn những lợi ích của việc có thể có những tên thân thiện với người dùng mà các nhà viết kịch bản có thể sử dụng, ví dụ, các chuỗi được thực hiện có thể cung cấp cho bạn những điều tốt nhất của cả hai thế giới ở đây.

Điều đó nói rằng, nếu bạn có thể ánh xạ chuỗi tới một phạm vi các chỉ số được sử dụng dày đặc, thì bạn có thể làm điều này:

vector<[yada]> // the index and key become one and the same

Lý do tôi không xem xét việc này quá sớm là vì một lần nữa, nó có thể ảnh hưởng đến các thiết kế giao diện của bạn. Quan điểm của DOD không phải là cố gắng đưa ra các biểu diễn dữ liệu hiệu quả nhất có thể tưởng tượng được trong một lần IMO (điều đó thường phải đạt được lặp đi lặp lại khi cần thiết), nhưng hãy nghĩ về chúng đủ để thiết kế giao diện trên đầu để làm việc với điều đó dữ liệu khiến bạn có đủ phòng thở để lập hồ sơ và tối ưu hóa mà không thay đổi thiết kế theo tầng.

Như một ví dụ ngây thơ, một phần mềm xử lý video kết hợp tất cả các mã của nó chống lại điều này:

// Abstract pixel that could be concretely represented by
// RGB, BGR, RGBA, BGRA, 1-bit channels, 8-bit channels, 
// 16-bit channels, 32-bit channels, grayscale, monochrome, 
// etc. pixels.
class IPixel
{
public:
    virtual ~IPixel() {}
    ...
};

Sẽ không đi xa mà không có khả năng viết lại có khả năng sử thi, vì ý tưởng trừu tượng hóa ở mức pixel đơn lẻ cực kỳ kém hiệu quả ( vptrbản thân nó thường sẽ tốn nhiều bộ nhớ hơn toàn bộ pixel) so với trừu tượng hóa ở cấp độ hình ảnh (sẽ thường đại diện cho hàng triệu pixel). Vì vậy, hãy suy nghĩ đầy đủ về các biểu diễn dữ liệu của bạn trước để bạn không phải đối mặt với kịch bản ác mộng như vậy, và lý tưởng là không còn nữa, nhưng ở đây tôi nghĩ rằng đáng để suy nghĩ về công cụ này ngay từ đầu vì bạn không muốn xây dựng một công cụ phức tạp xung quanh ECS của bạn và thấy rằng chính ECS là nút cổ chai theo những cách yêu cầu bạn thay đổi mọi thứ ở mức thiết kế.

Đối với các lỗi bộ đệm ECS, theo tôi, các nhà phát triển thường cố gắng quá mức để làm cho bộ đệm ECS của họ thân thiện. Nó bắt đầu mang lại quá ít tiếng nổ cho việc cố gắng truy cập tất cả các thành phần của bạn theo cách hoàn toàn liền kề và thường sẽ ngụ ý sao chép và xáo trộn dữ liệu ở mọi nơi. Nói chung, nó đủ tốt để chỉ các chỉ số thành phần sắp xếp cơ số trước khi truy cập chúng để bạn truy cập chúng theo cách mà ít nhất bạn không tải một vùng bộ nhớ vào một dòng bộ đệm, chỉ để đuổi nó đi, sau đó tải tất cả lặp lại trong cùng một vòng lặp chỉ để truy cập vào một phần khác của cùng một dòng bộ đệm. Và một ECS không phải cung cấp hiệu quả đáng kinh ngạc trên tất cả các bảng. Nó không giống như một hệ thống đầu vào được hưởng lợi nhiều như hệ thống vật lý hoặc kết xuất, vì vậy tôi khuyên bạn nên nhắm đến mục tiêu "tốt" hiệu quả trên bảng và "xuất sắc" chỉ ở những nơi bạn thực sự cần nó. Điều đó nói rằng, sử dụngunordered_mapstringở đây là đủ dễ dàng để tránh.

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.