2017-05-10 13 views
5

g++ -fopenmp main.cpp khiếu nại về tham chiếu không xác định đến std::vector. Làm thế nào để sửa lỗi này?Tác vụ OpenMp: không thể chuyển đối số theo tham chiếu

Tôi đã cài đặt gói libomp-dev trên Ubuntu.

main.cpp

#include<vector> 
#include<iostream> 

template<typename T, typename A> 
T recursiveSumBody(std::vector<T, A> &vec) { 
    T sum = 0; 
    #pragma omp task shared(sum) 
    { 
     sum = recursiveSumBody(vec); 
    } 
    return vec[0]; 
} 

int main() { 
    std::vector<int> a; 
    recursiveSumBody(a); 
    return 0; 
} 

Tài liệu tham khảo Không xác định

/tmp/ccTDECNm.o: In function `int recursiveSumBody<int, std::allocator<int> >(std::vector<int, std::allocator<int> >&) [clone ._omp_cpyfn.1]': 
main.cpp:(.text+0x148): undefined reference to `std::vector<int, std::allocator<int> >::vector(std::vector<int, std::allocator<int> > const&)' 
collect2: error: ld returned 1 exit status 
+0

Bất cứ ai đã thấy một cái gì đó như thế này? Tôi đoán tôi có thể sử dụng con trỏ đến phần tử thứ 0 của vectơ, thay vì 'std :: vector', nhưng tôi không muốn sử dụng con trỏ trực tiếp nếu có thể. –

+0

Lưu ý rằng 'libomp-dev' là thời gian chạy LLVM OpenMP không liên quan đến' gomp', là thời gian chạy OpenMP được đóng gói bởi 'gcc'. – Zulan

Trả lời

3

Để khắc phục sự cố, bạn có thể chỉ định thủ công shared(sum, vec) (giả sử bạn muốn chia sẻ nó một cách thủ công).

Điều thú vị đủ các phiên bản gcc cũ (ví dụ 5.4.0) đưa ra một thông báo lỗi nhiều hữu ích hơn:

error: 'vec' implicitly determined as 'firstprivate' has reference type 

Trong khi trình biên dịch Intel icpc 17.0.1 đưa ra một "internal error : 0_1855".

Chỉ định theo cách thủ công firstprivate hoặc private - điều này rất có ý nghĩa trong trường hợp của bạn - dẫn đến các lỗi mô tả khác. Lưu ý, như Hristo Iliev giải thích trong các ý kiến ​​khác, firstprivate có nghĩa là một bản sao của vectơ được tạo cho mỗi luồng.

Theo tiêu chuẩn hiện hành (4.5):

Trong một cấu trúc nhiệm vụ tạo mồ côi, nếu không có điều khoản mặc định là hiện nay, lý luận chính thức thông qua tham khảo là firstprivate.

Tôi cho rằng áp dụng ở đây. Ngoài ra,

Một biến xuất hiện trong mệnh đề firstprivate không được có loại C/C++ chưa hoàn chỉnh hoặc là tham chiếu đến loại không đầy đủ. Nếu một mục danh sách trong mệnh đề firstprivate trên cấu trúc trang tính có loại tham chiếu thì nó phải liên kết với cùng một đối tượng cho tất cả các chủ đề của nhóm.

Nó không xuất hiện trong mệnh đề, nhưng tôi nghĩ đây vẫn là ý nghĩa của tiêu chuẩn.

Bây giờ tôi không nghĩ rằng std::vector<T, A> là một loại không hoàn chỉnh trong khuôn mẫu, trừ khi tôi thiếu một cái gì đó về cách các mẫu được khởi tạo. Vì vậy, tôi nghĩ rằng mã của bạn nên hợp lệ và cho rằng mỗi thread chỉ liên kết với cùng một đối tượng, nó thực sự sẽ có ý nghĩa.

Vì vậy, tôi nghĩ đây là lỗi trong các phiên bản gcc gần đây cũng như trình biên dịch Intel.Dường như trình biên dịch không thể khởi tạo một số thứ cho mẫu.

Hơn nữa, nói thêm:

if (0) std::vector<T, A> wtf = vec; 

vào đầu của hàm làm cho biên dịch mã và liên kết với gcc. Nhưng nếu firstprivate được thêm theo cách thủ công, gcc tiếp tục khiếu nại 'vec' has incomplete type.

P.S .: Cho phép các loại tham chiếu trong mệnh đề thuộc tính chia sẻ dữ liệu được thêm vào trong OpenMP 4.5, đây là gcc cũ cung cấp một lỗi khác.

+0

Intel 18.0b phàn nàn rằng 'vec' là một kiểu không đầy đủ hoặc tham chiếu nếu được xác định rõ ràng trong mệnh đề' firstprivate' và biên dịch mà không có vấn đề gì khác. Chương trình bị treo khi chạy vì đệ quy vô hạn. –

1

Vấn đề sẽ biến mất nếu bạn cũng tuyên bố vec như là một biến chia sẻ:

#pragma omp task shared(sum, vec) 

Dường như khả năng hiển thị mặc định cho taskfirstprivate, không được chia sẻ như mong đợi. Bạn có thể tìm thêm thông tin trong this forum entry.

+0

Nhiệm vụ là các đoạn mã có thể (và có thể) sẽ thực hiện trong một thời điểm tương lai. Như những người thường được dự kiến ​​sẽ làm việc trên các giá trị đầu vào khác nhau được cung cấp trong cùng một biến, sẽ tự nhiên hơn khi mong đợi những giá trị đó là 'firstprivate' và không phải là 'shared'. Hãy suy nghĩ đóng cửa không đồng bộ. –

+0

@HristoIliev Tôi tìm thấy ngữ nghĩa của 'firstprivate' về các tham chiếu không trực quan ngay lập tức (nghĩ' auto firstprivate = reference'). Bạn có đồng ý rằng không có sự khác biệt ngữ nghĩa giữa 'firstprivate' và' shared' cho tài liệu tham khảo? – Zulan

+0

@Zulan, tham chiếu là tên thay thế của một số đối tượng bộ nhớ và không phải con trỏ, do đó 'firstprivate', theo ngữ nghĩa của' private', tạo một bản sao của toàn bộ đối tượng và không chỉ là một tham chiếu khác. –

1

Điều này trông giống như một lỗi trong GCC, không tạo ra một hàm tạo bản sao cho std::vector<int, std::allocator<int> >. Lưu ý rằng lỗi đến từ trình liên kết và không xảy ra trong giai đoạn biên dịch. Hàm tạo bản sao được sử dụng trong hàm sao chép khởi tạo các tham số firstprivate của hàm tác vụ được nêu. Buộc trình biên dịch tạo ra nó, ví dụ: thay đổi

std::vector<int> a; 

để

std::vector<int> a, b(a); 

sửa chữa vấn đề.

Dưới đây là mô tả chi tiết hơn. GCC biến đổi đoạn mã sau

#pragma omp task shared(sum) 
{ 
    sum = recursiveSumBody(vec); 
} 

vào một cái gì đó như:

struct omp_data_a data_o; 

data_o.vec = vec; 
data_o.sum = &sum; 
GOMP_task(omp_fn_0, &data_o, omp_cpyfn_1, 32, 8, 1, 0, 0, 0); 

// --- outlined task body --- 
void omp_fn_0(struct omp_data_s & restrict data_i) 
{ 
    struct vector & vec = &data_i->vec; 
    *data_i->sum = recursiveSumBody<int>(vec); 
    std::vector<int>::~vector(vec); 
} 

// --- task firstprivate initialisation function --- 
void omp_cpyfn_1(struct omp_data_s *data_o, struct omp_data_a *data_i) 
{ 
    data_o->sum = data_i->sum; 
    struct vector &d40788 = data_i->vec; 
    struct vector *this = &data_o->vec; 
    std::vector<int>::vector(this, d40788); // <--- invocation of the copy constructor 
} 

omp_cpyfn_1 được gọi bởi GOMP_task() để khởi sự lập luận firstprivate. Nó gọi hàm tạo bản sao của std::vector<int>, bởi vì (first-)private xử lý các tham chiếu đến kiểu T là kiểu T, nhưng hàm tạo không được tạo ra, do đó mã đối tượng không liên kết. Đây có lẽ là một lỗi trong mã gimplifier như các nhà xây dựng bản sao được tạo ra khi một tổ chức phi tham khảo std::vector<T, A> được tư nhân hóa, ví dụ, với mã như thế này:

... 
std::vector<T, A> b; 
#pragma omp task shared(sum) 
{ 
    sum = recursiveSumBody(b); 
} 
... 

mã biên dịch với Intel 18.0b. Chỉ định rõ ràng vecfirstprivate vi phạm theo cách tương tự như với GCC (icpc phàn nàn về vec là loại không đầy đủ). Bạn có thể sử dụng giải pháp sau:

template<typename T, typename A> 
T recursiveSumBody(std::vector<T, A> &vec) { 
    T sum = 0; 
    std::vector<T, A> *ptr = &vec; 
    #pragma omp task shared(sum) 
    { 
     sum = recursiveSumBody(*ptr); 
    } 
    return vec[0]; 
} 

Trong trường hợp này ptr là con trỏ. Phiên bản firstprivate của nó là một con trỏ trỏ đến cùng một vị trí, tức là trường hợp vectơ. Ngữ nghĩa khác với mã ban đầu vì ở đây không có bản sao riêng của toàn bộ vectơ được tạo ra, thay vào đó vector gốc được sử dụng.

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