2013-04-10 24 views
31

Có thể/thực hiện các hàm lambda nội bộ của trình biên dịch để tăng hiệu quả, vì nó có thể với các hàm tiêu chuẩn đơn giản?Có phải lambdas được nội tuyến giống như các hàm trong C++ không?

ví dụ:

std::vector<double> vd; 
std::for_each(vd.begin(), vd.end(), [](const double d) {return d*d;}); 

Hoặc có mất hiệu quả do thiếu tối ưu hóa không?

Câu hỏi thứ hai: nơi tôi có thể kiểm tra xem trình biên dịch tôi sử dụng có tối ưu hóa các cuộc gọi của các hàm nội tuyến, được gửi đến một thuật toán không? Ý của tôi là, nếu một hàm - không phải là đối tượng hàm - được gửi đến một thuật toán, thì hàm cuối sẽ nhận một con trỏ tới hàm, và một số trình biên dịch tối ưu hóa con trỏ tới các hàm nội tuyến và các hàm khác thì không.

+4

Một số được tối ưu hóa, một số không giống như bất kỳ cuộc gọi chức năng nào. Nếu bạn quan tâm đến một cuộc gọi cụ thể, bạn cần phải kiểm tra xem trình biên dịch cụ thể của bạn làm gì với cuộc gọi cụ thể đó. –

+2

Bạn đang nhầm lẫn các khái niệm ở đây. Tất cả lambdas đều là nội tuyến. Không phải tất cả các cuộc gọi đến họ đều nhất thiết được gạch chân. –

+0

Tôi không nghĩ rằng một lambda có thể được inlined nếu nó được chuyển đến một chức năng bên ngoài. – nobar

Trả lời

26

Trong trường hợp đơn giản, giống như ví dụ của bạn, bạn nên mong đợi hiệu suất tốt hơn với lambdas hơn với con trỏ hàm, thấy

Why can lambdas be better optimized by the compiler than plain functions?

Như những người khác đã chỉ ra, không có đảm bảo rằng cuộc gọi của bạn sẽ được gạch chân nhưng bạn có cơ hội tốt hơn với lambdas. Một cách để kiểm tra xem cuộc gọi đã được đặt nội tuyến hay chưa là kiểm tra mã đã tạo. Nếu bạn đang sử dụng gcc, hãy chuyển cờ -S tới trình biên dịch. Tất nhiên, nó giả định rằng bạn có thể hiểu mã lắp ráp.

+21

Bất cứ ai cũng có thể đọc mã lắp ráp, câu hỏi là nếu bạn có thể hiểu nó. – Bolpat

+3

Downvotes vô danh không giúp đỡ bất cứ ai. Có gì sai với câu hỏi? – Ali

17

Trước hết: toàn bộ điểm thiết kế lambdas trong C++ là chúng không có phí trên cao so với các cuộc gọi chức năng. Điều đó đáng chú ý bao gồm một thực tế là các cuộc gọi đến họ có thể được inlined.

Nhưng có sự nhầm lẫn về khái niệm ở đây: “nội tuyến” là liên kết của hàm, tức là tuyên bố về cách hàm được gọi là được xác định chứ không phải cách gọi. Nhưng chức năng được xác định nội tuyến có thể được hưởng lợi từ tối ưu hóa trình biên dịch mà theo đó các cuộc gọi đến các chức năng như vậy được gạch chân. Đó là một khái niệm khác nhưng có liên quan cao.

Bây giờ trong trường hợp lambda, hàm thực tế là operator() được xác định nội tuyến trong một lớp ẩn danh. Các cuộc gọi trực tiếp của lambda là các cuộc gọi trực tiếp tới số operator() và do đó có thể được gạch chân.

11

Tùy thuộc vào mức tối ưu hóa được cung cấp cho trình biên dịch. Lấy ví dụ, hai hàm này giống hệt nhau về mặt ngữ nghĩa. Một là kiểu C++ 11, kiểu C khác.

void foo1 (void) 
{ 
    int arr[100]; 
    std::generate(std::begin(arr), std::end(arr), [](){return std::rand()%100;}); 
} 

void foo2 (void) 
{ 
    int arr[100]; 
    for (int *i = arr; i < arr+100; i++) *i = std::rand()%100; 
} 

Biên dịch mã này bằng gcc -O4 phát ra mã cực kỳ giống nhau (không giống nhau, nhưng độ phức tạp tương đương) cho hai hàm.

Tuy nhiên, lambda không được gạch chân khi biên dịch chưa được tối ưu hóa (và không phải là tiêu chuẩn :: bắt đầu và std :: kết thúc cuộc gọi). Vì vậy, mặc dù trình biên dịch có thể làm một công việc tuyệt vời để tối ưu hóa mã kiểu hiện đại khi được yêu cầu, có thể hoặc có thể sẽ là một hình phạt hiệu suất cho loại mã này trong một bản dựng gỡ lỗi chưa được tối ưu hóa .

+9

Không có "-O4" thực tế trong gcc bằng cách này. – DrYak

+8

@DrYak Tôi luôn biên dịch với "-O9999" vì tôi chưa tìm thấy [limit-breaker] (http://finalfantasy.wikia.com/wiki/Break_Damage_Limit) nào ... Tất nhiên tôi jest. Về mặt kỹ thuật, mọi thứ ở trên "-O3" dịch thành "-O3". –

Các vấn đề liên quan