C++ Programlama Dili: std::execution ile İstasyon Oluşturma

Adanali

Active member
C++ Programlama Dili: std::execution ile İstasyon Oluşturma


  1. C++ Programlama Dili: std::execution ile İstasyon Oluşturma

Kapsamlı Taramayı eşzamansız olarak çalıştırmayla ilgili açıklamalarım aşağıdadır. std::execution Şimdi kendimi istasyonları oluşturmaya adadım.


Duyuru














Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.







Boru operatörünü kullanarak basit bir kompozisyon örneğiyle başlayayım. İç içe işlev çağrıları yerine


call1(call2(input))


alternatif olarak yazılabilir


call1 | call2(input)


veya hatta:


input | call1 | call2


Fonksiyonel bileşim


Bu örnek kesinlikle çok basittir. Bunu biraz daha karmaşık hale getirelim. Teklif P2300R10, iç içe geçmiş bir işlevin çağrılmasını, boru operatörünü kullanarak geçici bir nesne ve kompozisyon kullanarak bir işlevin çağrılmasıyla karşılaştırır.

Üç fonksiyon bileşiminin tümünü hesaplayın 610 = (123*5)-5 bir iş parçacığı havuzu ve CUDA kullanarak. Aşağıdaki kod örneklerinde lambdaya özellikle dikkat edin []{ return 123; }). Bu lambda değerlendirilmez.

İç içe geçmiş işlevleri çağırma



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


İşlev çağrılarının bu şekilde iç içe geçmesini anlamak ve hangi işlev gövdelerinin birbirine ait olduğunu görmek veya lambda'nın neden yürütülmediğini anlamak kolay değildir. Bu kompozisyonun hatalarını ayıklamak veya düzenlemek de eğlenceli değil.

Geçici nesnelerle işlev çağrısı



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şkenleri kullanmak kompozisyonunuzun yapısını anlamanıza çok yardımcı olabilir. Artık işlev çağrılarının sırasını görmek kolaydır. Lambda fonksiyonunun nedeni de netleşiyor []{ return 123; } idam edilmez. Böyle bir tüketim kanalı yok this_thread::sync_wait(snd4). Verici adaptörlerini seviyorum then VE continue_on onlar “tembel”dirler. Yalnızca istendiğinde değer üretirler.

Okunabilirlik açısından bu çözümü beğendim ancak ciddi bir dezavantajı var: çok sayıda geçici nesne yaratıyor.

Boru operatörünü kullanarak fonksiyonun bileşimi



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


Fonksiyonların boru operatörüyle birleşimi her iki sorunu da çözer. Birincisi okunabilir olması ve ikincisi gereksiz geçici değişkenlere gerek olmamasıdır.

Aşağıdaki verici adaptörleri borularla uyumlu değildir.

  • execution::when_all VE execution::when_all_with_variant: Her iki verici adaptörü de herhangi bir sayıda vericiyi kabul eder. Bu nedenle hangi kanalın uyarlanması gerektiği açık değildir.
  • execution::starts_on: Bu gönderen bağdaştırıcısı, gönderenin üzerinde çalıştığı yürütme kaynağını değiştirir. Kanalı ayarlamaz.
Düzen önemlidir


İşlevsel bir kompozisyon için mizanpajın okunaklı olması önemlidir. O yüzden bunu “zekice” bir şaka haline getirmeyin:


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


Bir sonraki adım nedir?


std::execution iş akışını şekillendirmek için bazı fabrikalara vericiler, özellikle de birçok verici sağlar. Bir sonraki yazımda onları tanıtacağım.


(harita)
 
Üst