2013-10-18 36 views
9

Mã này:C++ giá trị trả về tối ưu hóa

#include <vector> 

std::vector<float> getstdvec() { 
    std::vector<float> v(4); 

    v[0] = 1; 
    v[1] = 2; 
    v[2] = 3; 
    v[3] = 4; 

    return v; 
} 

int main() { 
    std::vector<float> v(4); 

    for (int i = 0; i != 1000; ++i) 
    { 
     v = getstdvec(); 
    } 
} 

hiểu biết không chính xác của tôi ở đây là các chức năng getstdvec không cần phải thực sự phân bổ các vector rằng nó trở lại. Khi tôi chạy điều này trong valgrind/callgrind, tôi thấy có 1001 cuộc gọi đến malloc; 1 cho khai báo vector ban đầu trong chính và 1000 cho mỗi lần lặp vòng lặp.

Điều gì mang lại? Làm thế nào tôi có thể trả về một vector (hoặc bất kỳ đối tượng nào khác) từ một hàm như thế này mà không phải phân bổ nó mỗi lần?

chỉnh sửa: Tôi biết rằng tôi chỉ có thể chuyển vectơ theo tham chiếu. Tôi đã được ấn tượng rằng nó đã có thể (và thậm chí thích hợp hơn) để viết một chức năng như thế này mà trả về một đối tượng mà không có một phân bổ không cần thiết.

+0

Đối với chỉnh sửa của bạn: Chúng tôi cần một ví dụ thực sự về vấn đề bạn đang cố gắng giải quyết thay vì mã mẫu cực kỳ tối thiểu này để giúp cung cấp giải pháp không vượt qua tham chiếu. –

+0

@MarkB, nó thực sự chỉ đơn giản như vậy: Tôi muốn một hàm trả về một véc-tơ mà không cần phải sao chép/phân bổ không cần thiết. Tôi đã ấn tượng rằng một cái gì đó liên quan đến RVO hoặc rvalues ​​sẽ làm cho điều này rất đơn giản có thể. Một ví dụ thế giới thực đơn giản sẽ cố gắng làm y = k * x cho vectơ y và x, vô hướng k. Hàm truyền qua tham chiếu truyền thống sẽ giống như 'void mult (const float & k, const vec & x, vec & y)'. Nhưng rõ ràng một hàm gọi 'y = mult (k, x)' là thích hợp hơn với 'mult (k, x, y)'. – Aurelius

+1

RVO (Tối ưu hóa giá trị trả về) là thứ mà trình biên dịch thực hiện với mã của bạn. Trước tiên, mã của bạn cần phải làm điều gì đó có thể được tối ưu hóa (chuyển xung quanh một tạm thời được gán lại cho cùng một đối tượng chẳng hạn). Bạn có thể đã xem mã và suy nghĩ - hmm, tôi có thể tối ưu hóa nó bằng cách chuyển tham chiếu đến getstdvec. Tại sao trình biên dịch không làm điều đó? Vâng, việc chuyển một tham chiếu KHÔNG được ngụ ý bởi mã của bạn. Bạn chỉ có thể mong đợi trình biên dịch tối ưu hóa những thứ mà mã của bạn làm - không phải những thứ mà nó có thể làm. – iheanyi

Trả lời

1

Câu trả lời đơn giản nhất là truyền đối tượng vectơ đã được tạo thành hàm.

std::vector<float> getstdvec(std::vector<float> &myvec){ 

Trong trường hợp đó bạn không thực sự phải trả lại nó để

void getstdvec(std::vector<float> &myvec){ 
3

Bạn có thể vượt qua nó bằng cách tham khảo ... sao chép sự bỏ bớt làm cho nó để v = getstdvect() phân bổ v (trong chính của bạn) trực tiếp đến v (trong getstdvec()) của bạn và bỏ qua bản sao thường được kết hợp với trả về theo giá trị, nhưng nó sẽ KHÔNG bỏ qua v (4) trong hàm của bạn. Để làm điều đó, bạn cần phải thực hiện các vector trong bằng cách tham khảo:

#include <vector> 
void getstdvec(std::vector<float>& v){ 
    v.resize(4);//will only realocate if v is wrong size 
    v[0] = 1; v[1] = 2; v[2] = 3; v[3] = 4; 
    return v; 
} 
int main() { 
    std::vector<float> v(4); 
    for (int i=0; i!=1000;++i) 
    getstdvec(v); 
} 
1

Làm thế nào tôi có thể trở lại một vector (hoặc bất kỳ đối tượng khác) từ một chức năng như thế này mà không cần phải phân bổ nó mỗi thời gian?

Theo cách của bạn, bạn khai báo vector cục bộ với kích thước 4, vì vậy mỗi khi hàm được gọi, nó sẽ cấp phát bộ nhớ. Nếu bạn có nghĩa là bạn luôn luôn sửa đổi trên cùng một vectơ, thì bạn có thể xem xét chuyển vector bằng tham chiếu thay thế.

Ví dụ:

void getstdvec(std::vector<float>& vec) 
{        //^^ 
    //do something with vec 
} 

bên main, bạn khai báo các vector và bố trí không gian như những gì bạn đã làm. Bây giờ bạn làm như sau:

for (int i=0; i!=1000;++i) 
{  //^^^minor: Don't use magic number in the code like this, 
     //define a const instead 
    getstdvec(vec); 
} 
1

trong stead sử dụng giá trị trả về, bạn có thể sử dụng một tài liệu tham khảo:

void getstdvec(std::vector<float> &v) 

Mà có thể tránh được những bản sao của đối tượng tạm thời

2

Bạn đang làm bản sao -giao trong vòng lặp của bạn, không phải sao chép-xây dựng. Việc tối ưu hóa RVO chỉ áp dụng cho việc tạo các biến từ một giá trị trả về, chứ không phải gán cho chúng.

Tôi hoàn toàn không thể giải quyết được vấn đề thực sự bạn đang cố giải quyết ở đây. Với nhiều chi tiết hơn, có thể cung cấp câu trả lời hay giải quyết vấn đề cơ bản của bạn.

Khi nó đứng, để trở về từ chức năng của bạn theo cách như vậy, bạn sẽ cần tạo một vector tạm thời để trả về mỗi khi hàm được gọi.

15

Khi bạn gọi hàm, đối với kiểu trả về như std::vector<T> trình biên dịch cung cấp bộ nhớ cho đối tượng được trả về. Hàm được gọi có trách nhiệm xây dựng cá thể nó trả về trong khe nhớ này.

Các RVO/NRVO bây giờ cho phép trình biên dịch bỏ qua việc tạo ra một đối tượng tạm thời địa phương, sao chép-xây dựng giá trị trả lại trong khe cắm bộ nhớ từ nó, huỷ đối tượng tạm thời và cuối cùng trở về người gọi. Thay vào đó, hàm được gọi chỉ đơn giản là xây dựng đối tượng cục bộ trong bộ nhớ của khe trả về trực tiếp và ở cuối hàm, nó chỉ trả về.

Từ quan điểm của người gọi, điều này là minh bạch: Nó cung cấp bộ nhớ cho giá trị trả về và khi hàm được gọi trả về, có một phiên bản hợp lệ. Bây giờ người gọi có thể sử dụng đối tượng này và chịu trách nhiệm gọi hàm hủy và giải phóng bộ nhớ sau này.

Điều này có nghĩa là RVO/NRVO chỉ hoạt động khi bạn gọi hàm để tạo phiên bản mới, không phải khi bạn gán nó. Sau đây là một ví dụ về nơi RVO/NRVO thể được áp dụng:

std::vector<float> v = getstdvec(); 

nhưng bạn gốc mã sử dụng một vòng lặp và trong mỗi lần lặp, kết quả từ getstdvec() cần phải được xây dựng và tạm thời này được gán cho v. Không có cách nào mà RVO/NRVO có thể loại bỏ điều này.

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