2013-07-06 41 views
5

Trong mã chương trình của tôi có nhiều đối tượng khá nhỏ khác nhau, từ một byte hoặc 2 tối đa khoảng 16. Ví dụ: Vector2 (2 * T), Vector3 (3 * T), Vector4 (4 * T), ColourI32 (4), LightValue16 (2), Ngói (2), vv (kích thước byte trong ngoặc).C++ Hiệu suất đối tượng nhỏ

Đã thực hiện một số hồ sơ (dựa trên mẫu) dẫn tôi đến một số hàm chậm hơn mong đợi, ví dụ:

//4 bits per channel natural light and artificial RGB 
class LightValue16 
{ 
... 
    explicit LightValue16(uint16_t value); 
    LightValue16(const LightValueF &); 
    LightValue16(int r, int g, int b, int natural); 

    int natural()const; 
    void natural(int v); 
    int artificialRed()const; 
    ... 
    uint16_t data; 
}; 
... 
LightValue16 World::getLight(const Vector3I &pos) 
{ ... } 

Chức năng này thực hiện một số toán để tra cứu giá trị thông qua một vài mảng, với một số giá trị mặc định ở trên phần đông dân cư của thế giới. Các nội dung được inlined độc đáo và nhìn vào disassembly trông về tốt như nó có thể get.with khoảng 100 hướng dẫn. Tuy nhiên có một điều nổi bật, trên tất cả các trang web trở lại nó đã được thực hiện với một cái gì đó như:

mov eax, dword pyt [ebp + 8] 
mov cx, word ptr[ecx + edx * 2] ; or say mov ecx, Fh 
mov word ptr [eax], cx 
pop ebp 
ret 10h 

Đối với x64 tôi thấy khá nhiều điều tương tự. Tôi đã không kiểm tra xây dựng GCC của tôi, nhưng tôi nghi ngờ nó thực hiện khá nhiều điều tương tự.

Tôi đã thử nghiệm và tìm thấy một chút bằng cách sử dụng loại trả về uint16_t. Nó thực sự dẫn đến chức năng getLight của World :: nhận được nội tuyến (trông giống như các lệnh 80 lõi tương tự hay như vậy, không có cheats với điều kiện/vòng lặp khác nhau) và tổng mức sử dụng CPU cho hàm bên ngoài mà tôi đang điều tra để đi từ 16,87 % đến 14.04% Trong khi tôi có thể làm điều đó trên cơ sở từng trường hợp (cùng với việc thử nội dung buộc nội tuyến tôi giả sử), có cách nào thực tế để tránh các vấn đề hiệu suất như vậy bắt đầu không? Có lẽ thậm chí còn nhận được một vài% nhanh hơn trên toàn bộ mã? Điều tốt nhất tôi có thể nghĩ chỉ là sử dụng các kiểu nguyên thủy trong các trường hợp như vậy (< 4 hoặc có thể là 8 byte) và di chuyển tất cả các thành phần hiện tại thành các hàm không phải thành viên, vì vậy giống như được thực hiện trong C , chỉ với các không gian tên.

Nghĩ về điều này, tôi đoán rằng đó cũng thường là một chi phí cho các công cụ như "t foo (const Vector3F & p)" trên "t foo (float x, float y, float z)"? Và nếu như vậy, trên một chương trình rộng rãi bằng cách sử dụng const &, nó có thể thêm đến một sự khác biệt đáng kể?

+0

tốt , sự khác biệt trong trường hợp đã nêu của bạn là bạn đang trả về một đối tượng với tất cả các chi phí liên quan so với khi bạn trả về một int không dấu 16 bit. Cho rằng trước đây, bạn phải sao chép toàn bộ đối tượng hơn là chỉ int, tôi mong đợi rằng để tiêu thụ một chút thời gian CPU ngay cả khi RVO đi vào chơi. –

+0

Có thể phân bổ đối tượng trong ngăn xếp thay vì đống, ảnh hưởng đến hiệu suất trong trường hợp này không? –

+0

Timo: tại sao đối tượng đầy đủ sẽ mất hơn 2 byte bộ nhớ? Trình biên dịch không nên đặt một vtable trong đó, tôi sẽ nghĩ. –

Trả lời

0

Trong các nhận xét cho câu hỏi này, đã có rất nhiều cuộc thảo luận, cho dù trình biên dịch có được phép xử lý class LightValue16 đơn giản là uint16_t cho hàm bạn đã phân tích hay không.

Nếu lớp học của bạn không chứa ma thuật đặc biệt (như chức năng ảo) cả lớp là nhìn thấy được bằng chức năng phân tích, trình biên dịch thể đang sản mà là 100% không kém hiệu quả sau đó chỉ cần sử dụng một 'kiểu uint16_t.

Sự cố là "có thể". Mặc dù tất cả các trình biên dịch phong nha thường sẽ tạo ra mã 100% nhanh như, sẽ có những tình huống không thường xuyên khi một số tối ưu hóa sẽ không được áp dụng hoặc ít nhất là mã kết quả sẽ khác nhau. Nó có thể chỉ là một tham số của một thay đổi heuristic (ví dụ như inline sẽ không được áp dụng bởi vì chỉ một chút mã trong một số bước tối ưu hóa vẫn còn vì lớp) hoặc một số tối ưu hóa vượt qua chỉ thực sự yêu cầu một loại số đồng bằng ở giai đoạn này , thậm chí không phải là một lỗi thực sự trong trình biên dịch. Ví dụ: nếu bạn thêm "mẫu < bool NotUsed>" vào lớp của bạn ở trên, điều này có thể sẽ thay đổi các bước tối ưu hóa trong trình biên dịch, mặc dù ngữ nghĩa chương trình của bạn không thay đổi.

Vì vậy, nếu bạn muốn chắc chắn 100%, chỉ sử dụng int hoặc double trực tiếp.Nhưng trong 90% thời gian nó sẽ nhanh hơn 100%, chỉ trong 10% nó sẽ chỉ có 90% hiệu suất, mà nên là O.K. cho 99% phần trăm (nhưng không phải 100%) của tất cả các trường hợp sử dụng.

2

Hãy xem Itanium C++ ABI. Trong khi máy tính của bạn chắc chắn không có bộ vi xử lý Itanium, các mô hình gcc x86 và x86-64 ABI rất giống với Itanium ABI. Phần liên kết khẳng định rằng

Tuy nhiên, nếu loại giá trị trả về có một constructor sao chép không tầm thường hoặc destructor, [trở lại vào bộ nhớ gọi-cung cấp xảy ra]

Để tìm hiểu những gì không tầm thường copy constructor hoặc destructor có nghĩa là, hãy xem xét What are Aggregates and PODs and how/why are they special? và xem các quy tắc cho một lớp là "có thể sao chép một cách trivially". Trong trường hợp của bạn, vấn đề là hàm tạo bản sao mà bạn đã xác định. Nó không phải là cần thiết ở tất cả, trình biên dịch sẽ tổng hợp một constructor sao chép mà chỉ cần gán các thành viên data khi cần thiết. Nếu bạn muốn một cách rõ ràng nói rằng bạn muốn có một constructor sao chép, và bạn đang sử dụng C++ 11, bạn cũng có thể viết nó xuống như chức năng defaulted, mà không làm cho nó không tầm thường:

LigthValue16(const LightValue16 & other) = default; 
Các vấn đề liên quan