Tôi có thể sử dụng std :: Transform tại chỗ với chính sách thực thi song song không?


11

Nếu tôi không nhầm, tôi có thể thực std::transformhiện tại chỗ bằng cách sử dụng cùng một phạm vi như một trình vòng lặp đầu vào và đầu ra. Giả sử tôi có một số std::vectorđối tượng vec, sau đó tôi sẽ viết

std::transform(vec.cbegin(),vec.cend(),vec.begin(),unary_op)

sử dụng một hoạt động đơn nguyên phù hợp unary_op.

Sử dụng tiêu chuẩn C ++ 17, tôi muốn thực hiện chuyển đổi song song bằng cách dán một std::execution::partrong đó làm đối số đầu tiên. Điều này sẽ làm cho hàm chuyển từ quá tải (1) sang (2) trong bài viết cppreference trênstd::transform . Tuy nhiên, các ý kiến ​​cho sự quá tải này nói:

unary_op[...] không được làm mất hiệu lực bất kỳ trình lặp nào, kể cả các trình vòng lặp cuối hoặc sửa đổi bất kỳ thành phần nào của các phạm vi liên quan. (kể từ C ++ 11)

Có phải "sửa đổi bất kỳ yếu tố nào" thực sự có nghĩa là tôi không thể sử dụng thuật toán tại chỗ hay điều này đang nói về một chi tiết khác mà tôi giải thích sai?

Câu trả lời:


4

Để trích dẫn tiêu chuẩn ở đây

[alg.transform.1]

op [...] sẽ không làm mất hiệu lực các trình vòng lặp hoặc các phần phụ hoặc sửa đổi các phần tử trong phạm vi

điều này cấm bạn unary_opsửa đổi giá trị được cung cấp dưới dạng đối số hoặc chính vùng chứa.

auto unary_op = [](auto& value) 
{ 
    value = 10;    // this is bad
    return value;
}

auto unary_op = [&vec](auto const& value) 
{ 
    vec[0] = value;   // also bad
    return value;
}

auto unary_op = [&vec](auto& value) 
{ 
    vec.erase(vec.begin());   // nope 
    return value;
}

Tuy nhiên, follwing là ok.

auto unary_op = [](auto& value)  // const/ref not strictly needed
{         
    return value + 10;   // totally fine
}

auto unary_op = [&vec](auto& value)
{         
    return value + vec[0];   // ok in sequential but not in parallel execution
}

Độc lập với UnaryOperationchúng ta có

[alg.transform.5]

kết quả có thể bằng đầu tiên trong trường hợp biến đổi đơn nguyên [...].

có nghĩa là hoạt động tại chỗ được cho phép rõ ràng.

Hiện nay

[thuật toán.pool.overloads.2]

Trừ khi có quy định khác, ngữ nghĩa của quá tải thuật toán ExecutPolicy giống hệt với quá tải của chúng mà không có.

có nghĩa là chính sách thực thi không có sự khác biệt hiển thị của người dùng trên thuật toán. Bạn có thể mong đợi thuật toán mang lại kết quả chính xác giống như khi bạn không chỉ định chính sách thực thi.


6

Tôi tin rằng nó đang nói về một chi tiết khác. Việc unary_oplấy một phần tử của chuỗi và trả về một giá trị. Giá trị đó được lưu trữ (bởi transform) vào chuỗi đích.

Vì vậy, điều này unary_opsẽ ổn:

int times2(int v) { return 2*v; }

nhưng điều này sẽ không:

int times2(int &v) { return v*=2; }

Nhưng đó không thực sự là những gì bạn đang hỏi về. Bạn muốn biết liệu bạn có thể sử dụng unary_opphiên bản của transformmột thuật toán song song với cùng phạm vi nguồn và đích không. Tôi không thấy lý do tại sao không. transformánh xạ một phần tử của chuỗi nguồn thành một phần tử duy nhất của chuỗi đích. Tuy nhiên, nếu bạn unary_opkhông thực sự đơn nhất, (nghĩa là, nó tham chiếu các yếu tố khác trong chuỗi - ngay cả khi nó chỉ đọc chúng, thì bạn sẽ có một cuộc đua dữ liệu).


1

Như bạn có thể thấy trong các ví dụ về liên kết mà bạn trích dẫn, sửa đổi bất kỳ yếu tố không có nghĩa là tất cả các loại sửa đổi trên các yếu tố:

Chữ ký của hàm phải tương đương như sau:

Ret fun(const Type &a);

Điều đó bao gồm sửa đổi trên các yếu tố. Trong trường hợp xấu nhất nếu bạn sử dụng cùng một trình vòng lặp cho đích, việc sửa đổi sẽ không gây ra sự vô hiệu của các trình vòng lặp, ví dụ như một push_backvectơ hoặc erasing từ vectorđó có thể sẽ gây ra sự vô hiệu của các trình vòng lặp.

Xem một ví dụ về sự thất bại mà bạn KHÔNG NÊN làm Live .

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.