Bisakah saya menggunakan std :: transform di tempat dengan kebijakan eksekusi paralel?

11

Jika saya tidak salah, saya dapat melakukan std::transformperform di tempat dengan menggunakan rentang yang sama sebagai input dan output iterator. Anggaplah saya memiliki beberapa std::vectorobjek vec, maka saya akan menulis

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

menggunakan operasi unary yang cocok unary_op.

Menggunakan standar C ++ 17, saya ingin menjalankan transformasi secara paralel dengan menempelkannya std::execution::pardi sana sebagai argumen pertama. Ini akan membuat fungsi beralih dari kelebihan (1) ke (2) di artikel cppreference aktifstd::transform . Namun komentar untuk kelebihan ini mengatakan:

unary_op[...] tidak boleh membatalkan iterator apa pun, termasuk iterator akhir, atau memodifikasi elemen apa pun dari rentang yang terlibat. (sejak C ++ 11)

Apakah "memodifikasi elemen apa pun" benar-benar berarti saya tidak dapat menggunakan algoritma di tempat atau apakah ini berbicara tentang detail yang berbeda yang saya salah artikan?

geo
sumber

Jawaban:

4

Mengutip standar di sini

[alg.transform.1]

op [...] tidak akan membatalkan iterator atau subranges, atau memodifikasi elemen dalam rentang

ini melarang Anda unary_opuntuk memodifikasi nilai yang diberikan sebagai argumen atau wadah itu sendiri.

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;
}

Namun, follwing tidak apa-apa.

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
}

Independen dari yang UnaryOperationkita miliki

[alg.transform.5]

hasilnya mungkin sama dengan yang pertama dalam kasus transformasi unary [...].

artinya operasi di tempat diizinkan secara eksplisit.

Sekarang

[algoritme.parallel.overloads.2]

Kecuali ditentukan lain, semantik dari kelebihan algoritma ExecutionPolicy identik dengan kelebihannya tanpa.

berarti bahwa kebijakan eksekusi tidak memiliki perbedaan yang terlihat oleh pengguna pada algoritme. Anda dapat mengharapkan algoritma untuk menghasilkan hasil yang sama persis seolah-olah Anda tidak akan menentukan kebijakan eksekusi.

Timo
sumber
6

Saya percaya itu berbicara tentang detail yang berbeda. The unary_opmengambil elemen dari urutan dan mengembalikan nilai. Nilai itu disimpan (oleh transform) ke dalam urutan tujuan.

Jadi ini unary_optidak masalah:

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

tapi yang ini tidak mau:

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

Tapi bukan itu yang sebenarnya Anda tanyakan. Anda ingin tahu apakah Anda dapat menggunakan unary_opversi transformsebagai algoritma paralel dengan rentang sumber dan tujuan yang sama. Saya tidak mengerti mengapa tidak. transformmemetakan elemen tunggal dari urutan sumber ke elemen tunggal dari urutan tujuan. Namun, jika Anda unary_optidak benar-benar unary, (yaitu, ia mereferensikan elemen lain dalam urutan - bahkan jika itu hanya membacanya, maka Anda akan melakukan perlombaan data).

Marshall Clow
sumber
1

Seperti yang Anda lihat pada contoh tautan yang Anda kutip, memodifikasi elemen apa pun tidak berarti semua jenis modifikasi pada elemen:

Tanda tangan fungsi harus sama dengan yang berikut ini:

Ret fun(const Type &a);

Itu termasuk modifikasi pada elemen. Dalam kasus terburuk jika Anda menggunakan iterator yang sama untuk tujuan, modifikasi tidak boleh menyebabkan pembatalan iterator yang misalnya a push_backke vektor atau erasdari vectoryang mungkin akan menyebabkan pembatalan iterator.

Lihat contoh kegagalan yang TIDAK HARUS Anda lakukan secara Langsung .

Pelupaan
sumber