C ++ Programlama Dili: STD :: Yürütme ile Kanalların Bileşimi
Kapsayıcı taramanın eşzamansız yürütülmesi hakkındaki açıklamalarıma göre std::execution Şimdi kendimi vericilerin kompozisyonuna aduyorum.
Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.
Tüp operatörü ile bileşimin basit bir örneğiyle başlamak istiyorum. İç içe işlevsel görünümler yerine
call1(call2(input))
Alternatif olarak yazılabilir
call1 | call2(input)
Veya ayrıca:
input | call1 | call2
Fonksiyonel kompozisyon
Tabii ki, bu örnek çok basit. Biraz daha karmaşık hale getirelim. P2300R10 teklifi, Tüp operatörünü kullanarak geçici bir nesne ve bir kompozisyon kullanarak bir işlevi çağırmak için iç içe bir işlevin çağrısını karşılaştırır.
Her üç fonksiyonel kompozisyonun hepsini hesaplayın 610 = (123*5)-5 Bir iplik ve cuda havuzu kullanarak. Özellikle Lambda'nın aşağıdaki kod sınavlarında not []{ return 123; }). Bu lambda değerlendirilmez.
İç içe işlevlerin çağrısı
auto snd = execution::then(
execution::continues_on(
execution::then(
execution::continues_on(
execution::then(
execution::schedule(thread_pool.scheduler())
[]{ return 123; }),
cuda::new_stream_scheduler()),
[](int i){ return 123 * 5; }),
thread_pool.scheduler()),
[](int i){ return i - 5; });
auto [result] = this_thread::sync_wait(snd).value();
// result == 610
İşlevsel çağrıların bu yuvalanmasını anlamak ve hangi fonksiyonel bedenin bir araya geldiğini tanımak veya Lambda'nın neden gerçekleştirilmediğini anlamak kolay değildir. Ayrıca, bu kompozisyonda hata ayıklamak veya değiştirmek eğlenceli değildir.
Geçici nesnelerle işlevsel çağrı
auto snd0 = execution::schedule(thread_pool.scheduler());
auto snd1 = execution::then(snd0, []{ return 123; });
auto snd2 = execution::continues_on(snd1, cuda::new_stream_scheduler());
auto snd3 = execution::then(snd2, [](int i){ return 123 * 5; })
auto snd4 = execution::continues_on(snd3, thread_pool.scheduler())
auto snd5 = execution::then(snd4, [](int i){ return i - 5; });
auto [result] = *this_thread::sync_wait(snd4);
// result == 610
Geçici değişkenlerin kullanımı, bileşimin yapısını anlamak için çok yararlı olabilir. Şimdi fonksiyonel çağrıların sırasını görmek kolaydır. Lambda işlevinin neden olduğunu da görebilirsiniz []{ return 123; } Gerçekleştirilmez. Verici tüketicisi yok gibi this_thread::sync_wait(snd4). Verici adaptörleri gibi then VE continue_on Onlar “tembel”. Sadece istenirse bir değer üretirler.
Okunabilirlik açısından, bu çözümü seviyorum, ancak ciddi bir dezavantajı var: birçok geçici nesne oluşturuldu.
Tüp operatörü ile fonksiyonel bileşim
auto snd = execution::schedule(thread_pool.scheduler())
| execution::then([]{ return 123; })
| execution::continues_on(cuda::new_stream_scheduler())
| execution::then([](int i){ return 123 * 5; })
| execution:: Double quote continues_on(thread_pool.scheduler())
| execution::then([](int i){ return i - 5; });
auto [result] = this_thread::sync_wait(snd).value();
// result == 610
Tüp operatörü ile fonksiyonel bileşim her iki problemi de çözer. Her şeyden önce, okunaklı ve ikincisi, işe yaramaz geçici değişkenlere gerek yoktur.
Aşağıdaki verici adaptörleri tüp kapasitesi değildir.
Fonksiyonel kompozisyonun düzeni okunaklı hale getirmesi önemlidir. Yani ondan “zeki” bir tane yapma:
auto snd = execution::schedule(thread_pool.scheduler()) | execution::then([]{ return 123; }) | execution::continues_on(cuda::new_stream_scheduler()) | execution::then([](int i){ return 123 * 5; }) | execution::continues_on(thread_pool.scheduler()) | execution::then([](int i){ return i - 5; });
Sırada ne var?
std::execution İş akışını modellemek için bazı iletim fabrikaları, özellikle birçok kanal sunar. Sizi bir sonraki yazımla tanıştıracağım.
(harita)
C ++ Programlama Dili: STD :: Yürütme ile Kanalların Bileşimi
Kapsayıcı taramanın eşzamansız yürütülmesi hakkındaki açıklamalarıma göre std::execution Şimdi kendimi vericilerin kompozisyonuna aduyorum.


Rainer Grimm yıllardır yazılım mimarı, ekip ve eğitim müdürü olarak çalıştı. C ++ programlama dilleri, Python ve Haskell hakkında makaleler yazmayı seviyor, ancak uzman konferanslarla konuşmayı da seviyor. Modern C ++ blogunda, C ++ tutkusuyla yoğun bir şekilde ilgileniyor.
Tüp operatörü ile bileşimin basit bir örneğiyle başlamak istiyorum. İç içe işlevsel görünümler yerine
call1(call2(input))
Alternatif olarak yazılabilir
call1 | call2(input)
Veya ayrıca:
input | call1 | call2
Fonksiyonel kompozisyon
Tabii ki, bu örnek çok basit. Biraz daha karmaşık hale getirelim. P2300R10 teklifi, Tüp operatörünü kullanarak geçici bir nesne ve bir kompozisyon kullanarak bir işlevi çağırmak için iç içe bir işlevin çağrısını karşılaştırır.
Her üç fonksiyonel kompozisyonun hepsini hesaplayın 610 = (123*5)-5 Bir iplik ve cuda havuzu kullanarak. Özellikle Lambda'nın aşağıdaki kod sınavlarında not []{ return 123; }). Bu lambda değerlendirilmez.
İç içe işlevlerin çağrısı
auto snd = execution::then(
execution::continues_on(
execution::then(
execution::continues_on(
execution::then(
execution::schedule(thread_pool.scheduler())
[]{ return 123; }),
cuda::new_stream_scheduler()),
[](int i){ return 123 * 5; }),
thread_pool.scheduler()),
[](int i){ return i - 5; });
auto [result] = this_thread::sync_wait(snd).value();
// result == 610
İşlevsel çağrıların bu yuvalanmasını anlamak ve hangi fonksiyonel bedenin bir araya geldiğini tanımak veya Lambda'nın neden gerçekleştirilmediğini anlamak kolay değildir. Ayrıca, bu kompozisyonda hata ayıklamak veya değiştirmek eğlenceli değildir.
Geçici nesnelerle işlevsel çağrı
auto snd0 = execution::schedule(thread_pool.scheduler());
auto snd1 = execution::then(snd0, []{ return 123; });
auto snd2 = execution::continues_on(snd1, cuda::new_stream_scheduler());
auto snd3 = execution::then(snd2, [](int i){ return 123 * 5; })
auto snd4 = execution::continues_on(snd3, thread_pool.scheduler())
auto snd5 = execution::then(snd4, [](int i){ return i - 5; });
auto [result] = *this_thread::sync_wait(snd4);
// result == 610
Geçici değişkenlerin kullanımı, bileşimin yapısını anlamak için çok yararlı olabilir. Şimdi fonksiyonel çağrıların sırasını görmek kolaydır. Lambda işlevinin neden olduğunu da görebilirsiniz []{ return 123; } Gerçekleştirilmez. Verici tüketicisi yok gibi this_thread::sync_wait(snd4). Verici adaptörleri gibi then VE continue_on Onlar “tembel”. Sadece istenirse bir değer üretirler.
Okunabilirlik açısından, bu çözümü seviyorum, ancak ciddi bir dezavantajı var: birçok geçici nesne oluşturuldu.
Tüp operatörü ile fonksiyonel bileşim
auto snd = execution::schedule(thread_pool.scheduler())
| execution::then([]{ return 123; })
| execution::continues_on(cuda::new_stream_scheduler())
| execution::then([](int i){ return 123 * 5; })
| execution:: Double quote continues_on(thread_pool.scheduler())
| execution::then([](int i){ return i - 5; });
auto [result] = this_thread::sync_wait(snd).value();
// result == 610
Tüp operatörü ile fonksiyonel bileşim her iki problemi de çözer. Her şeyden önce, okunaklı ve ikincisi, işe yaramaz geçici değişkenlere gerek yoktur.
Aşağıdaki verici adaptörleri tüp kapasitesi değildir.
- execution::when_all VE execution::when_all_with_variant: Her iki verici adaptör de herhangi bir sayıda kanalı emer. Bu nedenle hangi vericinin düzenlenmesi gerektiği belirsiz olacaktır.
- execution::starts_on: Bu verici adaptörü, istasyonun gerçekleştirildiği yürütme kaynağını değiştirir. Vericiyi uyarlamaz.
Fonksiyonel kompozisyonun düzeni okunaklı hale getirmesi önemlidir. Yani ondan “zeki” bir tane yapma:
auto snd = execution::schedule(thread_pool.scheduler()) | execution::then([]{ return 123; }) | execution::continues_on(cuda::new_stream_scheduler()) | execution::then([](int i){ return 123 * 5; }) | execution::continues_on(thread_pool.scheduler()) | execution::then([](int i){ return i - 5; });
Sırada ne var?
std::execution İş akışını modellemek için bazı iletim fabrikaları, özellikle birçok kanal sunar. Sizi bir sonraki yazımla tanıştıracağım.
(harita)