Tôi phải suy nghĩ về mã máy được biên dịch khi tôi viết mã của mình?


20

Ví dụ: tôi đã nhận được mã sau:

auto z = [](int x) -> int {
    if (x > 0) {
        switch (x) {
            case 2: return 5;
            case 3: return 6;
            default: return 1;
            }
        }
    return 0;
    };

Và sau này tôi gọi điều này nhiều lần. Trong mã asm tôi thấy các cuộc gọi bên ngoài với lambda .... một cái gì đó ... Nó trở nên không dễ đọc và tôi nghĩ nó cũng có thể gây ra hiệu suất. Vì vậy, có thể tôi giành chiến thắng trong lập trình meta nhưng tôi có thua trong việc gỡ lỗi và hiệu suất không? Tôi có nên tránh các tính năng ngôn ngữ hiện đại, macro và các khía cạnh lập trình meta khác để chắc chắn về hiệu suất và gỡ lỗi đơn giản không?


1
Tùy thuộc vào phiên bản của trình biên dịch và thư viện tiêu chuẩn đi kèm, lambda thực sự có thể được triển khai không hiệu quả. Xem câu hỏi này trên Stackoverflow. Tuy nhiên, trách nhiệm cải tiến nên nằm ở nhà cung cấp trình biên dịch.
rwong

15
Bạn không cần phải gỡ lỗi mã lắp ráp, trừ khi bạn đang thực hiện đường dẫn quan trọng. Ngoài ra, "mã sạch"! = "Biểu diễn tốt".
Bовић

Hãy sửa chữa vết lõm của bạn. Tôi đã cố gắng để làm điều đó, nhưng có vẻ như bạn không thể chỉnh sửa chỉ khoảng trắng.
Christoffer Hammarström

3
@Heather: Bạn dường như đang sử dụng phong cách Ratliff , điều mà tôi chưa từng thấy trước đây và khó đọc. Đó chắc chắn là một trong những người ít được biết đến. Ấn tượng của tôi là bạn đã thụt lề đúng cách. Không bao giờ sau đó nếu bạn thấy nó có thể đọc được, tôi chỉ không đồng ý.
Christoffer Hammarström

1
Điều ifnày là hoàn toàn dư thừa trong mã ví dụ, và trong khi trình biên dịch có thể sẽ nắm bắt được rằng không có lý do gì để cám dỗ một dự đoán nhánh xấu.
dmckee

Câu trả lời:


59

Tôi phải suy nghĩ về mã máy được biên dịch khi tôi viết mã của mình?

Không , không phải khi bạn viết mã lần đầu tiên và không gặp phải bất kỳ vấn đề hiệu suất thực tế nào có thể đo lường được. Đối với hầu hết các nhiệm vụ, đây là trường hợp tiêu chuẩn. Suy nghĩ quá sớm về tối ưu hóa được gọi là "tối ưu hóa sớm" và có những lý do chính đáng tại sao D. Knuth gọi đó là "gốc rễ của mọi tội lỗi" .

, khi bạn đo lường một nút cổ chai hiệu năng thực sự có thể chứng minh được và bạn xác định cấu trúc lambda cụ thể đó là nguyên nhân gốc rễ. Trong trường hợp này, bạn nên nhớ "luật trừu tượng rò rỉ" của Joel Spolsky và suy nghĩ về những gì có thể xảy ra ở cấp độ asm. Nhưng hãy cẩn thận, bạn có thể ngạc nhiên về mức tăng hiệu suất sẽ nhỏ như thế nào khi bạn thay thế cấu trúc lambda bằng cấu trúc ngôn ngữ "không quá hiện đại" (ít nhất là khi sử dụng trình biên dịch C ++ đàng hoàng).


2
+1 súc tích, chính xác và dễ theo dõi theo tài liệu thông thường, rất vui khi chúng tôi có bạn ở đây.
Jimmy Hoffa

Đồng ý, trả lời rất rõ ràng.
cnd

8

Sự lựa chọn giữa lambda và functor-class là một sự đánh đổi.

Thu được từ lambda chủ yếu là cú pháp, bằng cách giảm thiểu số lượng soạn sẵn và cho phép mã liên quan đến khái niệm được viết nội tuyến, bên trong hàm sẽ sử dụng nó (ngay lập tức hoặc sau đó).

Hiệu năng-khôn ngoan, điều này không tệ hơn một lớp functor , đó là một cấu trúc hoặc lớp C ++ có chứa một "phương thức" duy nhất. Trong thực tế, trình biên dịch đối xử với lambda không khác gì một lớp functor do trình biên dịch tạo ra đằng sau hậu trường.

// define the functor method somewhere
struct some_computer_generated_gibberish_0123456789
{
    int operator() (int x) const
    {
        if (x == 2) return 5;
        if (x == 3) return 6;
        return 0;
    }
};

// make a call
some_computer_generated_gibberish_0123456789 an_instance_of_0123456789;
int outputValue = an_instance_of_0123456789(inputValue);

Trong ví dụ mã của bạn, hiệu năng-khôn ngoan, nó không khác gì một hàm gọi, bởi vì lớp functor đó không có trạng thái (vì nó có một mệnh đề bắt trống), do đó không yêu cầu phân bổ, hàm tạo cũng như hủy.

int some_computer_generated_gibberish_0123456789_method_more_gibberish(int x)
{
    if (...) return ...;
    return ...;
}

Gỡ lỗi bất kỳ mã C ++ không tầm thường nào bằng cách sử dụng trình dịch ngược mã luôn là một nhiệm vụ khó khăn. Điều này đúng với hoặc không sử dụng lambda. Điều này được gây ra bởi việc tối ưu hóa mã tinh vi bởi trình biên dịch C ++ dẫn đến việc sắp xếp lại, xen kẽ và loại bỏ mã chết.

Khía cạnh xáo trộn tên là hơi khó chấp nhận và hỗ trợ trình gỡ lỗi cho lambda vẫn còn ở giai đoạn sơ khai . Chỉ có thể hy vọng rằng sự hỗ trợ của trình gỡ lỗi sẽ cải thiện theo thời gian.

Hiện tại, cách tốt nhất để gỡ lỗi mã lambda là sử dụng trình gỡ lỗi hỗ trợ đặt điểm dừng ở cấp mã nguồn, tức là bằng cách chỉ định tên tệp nguồn và số dòng.


3

Để thêm vào câu trả lời của @DocBrown, hãy nhớ rằng những ngày này CPU rất rẻ nhưng nhân công thì đắt.

Trong tổng chi phí của một chương trình, phần cứng thường không đáng kể so với chi phí bảo trì, đây là phần đắt nhất của một dự án điển hình (thậm chí còn hơn cả sự phát triển của nó).

Do đó, mã của bạn cần tối ưu hóa bảo trì trên tất cả mọi thứ khác, ngoại trừ khi hiệu suất là quan trọng (và thậm chí sau đó bảo trì cần phải được xem xét).


Chỉ đúng một phần. Nếu mã của bạn chạy O (n ^ 2) (bậc hai) và bạn có thể làm cho nó tốt hơn nói O (log (n)) (logarit) thì phần cứng sẽ không bao giờ có hiệu suất tăng nhiều khi thay đổi mã. Trong trường hợp được chỉ định bởi người đăng ban đầu, điều này rất khó xảy ra.
gnash117

@ gnash117 - vâng, bạn đúng nếu mã được chạy nhiều lần; Cảm ơn bạn đã chỉ ra điều này. Trong các trường hợp như vậy, ghi lại mã rõ ràng sẽ giữ cho nó duy trì được trong khi cho phép cải thiện hiệu suất.
Paddy Landau

"Lao động là tốn kém" - Đúng. Thời gian của khách hàng của bạn là rất quan trọng và thường đắt tiền.
Cerad
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.