2017-07-14 32 views
8

Czy istnieje sposób jawnego ustawienia/ograniczenia stopnia paralelizmu (= liczba oddzielnych wątków) używanego przez std::async i powiązanych klas?Kontrola stopnia paralelizmu z std :: async

Perusing the thread support library nie znalazłem niczego obiecującego.

Tak blisko, jak mogłem się domyślić, implementacje std::async (zwykle?) Korzystają z puli wątków wewnętrznie. Czy istnieje standardowy interfejs API do kontrolowania tego?

Dla tła: Jestem w ustawieniu (klastrze współdzielonym), w którym muszę ręcznie ograniczyć liczbę używanych rdzeni. Jeśli tego nie zrobię, planista dzielenia obciążenia rzuca dopasowanie i jestem ukarany. W szczególności, std::thread::hardware_concurrency() nie zawiera żadnych użytecznych informacji, ponieważ liczba rdzeni fizycznych jest nieistotna dla ograniczeń, w których jestem.

Oto odnośny fragment kodu (co w C++ 17 z TS równoległości, prawdopodobnie być napisane przy użyciu parallel std::transform):

auto read_data(std::string const&) -> std::string; 

auto multi_read_data(std::vector<std::string> const& filenames, int ncores = 2) -> std::vector<std::string> { 
    auto futures = std::vector<std::future<std::string>>{}; 

    // Haha, I wish. 
    std::thread_pool::set_max_parallelism(ncores); 

    for (auto const& filename : filenames) { 
     futures.push_back(std::async(std::launch::async, read_data, filename)); 
    } 

    auto ret = std::vector<std::string>(filenames.size()); 
    std::transform(futures.begin(), futures.end(), ret.begin(), 
      [](std::future<std::string>& f) {return f.get();}); 
    return ret; 
} 

Z punktu widzenia projektu bym oczekiwać std::execution::parallel_policy klasa (od paralelizmu TS), aby umożliwić określenie, że (w rzeczywistości tak to zrobiłem w ramach zaprojektowałem dla mojej pracy magisterskiej). Ale tak się nie wydaje.

Idealnie chciałbym rozwiązania dla C++ 11, ale jeśli jest jeden dla późniejszych wersji nadal chciałbym o tym wiedzieć (choć nie mogę go użyć).

+0

dlaczego używasz 'std :: async' na początek? Implementacje puli wątków i tak nie są zgodne, a standardowe zgodne z nimi otworzą nowy wątek dla każdego zadania. Tak czy siak, to jest do bani –

+0

@DavidHaim Wydawało się to najłatwiejszym rozwiązaniem Próbuję napisać tak mało kodu, jak to tylko możliwe, aby przetwarzać te wywołania funkcji równolegle, i miałem nadzieję, że uda mi się uciec przy użyciu standardowej biblioteki. Wygląda na to, że nie. –

+1

będziemy zmuszeni sami pisać rozwiązania, o ile komisja będzie nalegać na marnowanie czasu na podstawowe funkcje językowe zamiast na ważne nowoczesne narzędzia. –

Odpowiedz

4

Nie. std::async jest nieprzezroczysty i nie masz kontroli nad używaniem wątków, pul wątków lub czegokolwiek innego. W rzeczywistości nie masz nawet żadnej gwarancji, że w ogóle użyje on wątku - może równie dobrze zostać wykonany w tym samym wątku (potencjalnie, komentarz @ T.C. Komentarz poniżej), a taka implementacja nadal będzie zgodna.

Biblioteka wątków w C++ nigdy nie miała obsługiwać dostrajania specyfiki OS/sprzętu zarządzania wątkami, więc obawiam się, że w twoim przypadku będziesz musiał sam napisać kod dla właściwego wsparcia, potencjalnie używając kontrolki wątków dostarczanej przez system operacyjny prymitywy.

+0

"i taka implementacja nadal byłaby zgodna" Nie bez bohaterskich wysiłków ze zmiennymi lokalnymi wątku. –

+0

Uwaga Używam 'std :: launch :: async' - gwarantuje to użycie nowego (" jak gdyby ") nowego wątku (§30.6.8/3.1). –

+0

@ T.C. tak, nie myślałem o tym. – SergeyA

2

Jak zauważyli inni, std::async nie pozwala ci tego zrobić.

Jeszcze

Ty opisując jeden z prostszych przypadków użytkowych Executors które obecnie nadal podskakują wokół projektowania przestrzeni C++ Normalizacyjny, szczególnie teraz w Study Group 1: Concurrency.

Ponieważ czytanie propozycji standardów WG21 może być slogiem, autorzy ci pomocnie połączyli się z prototype header-only reference implementation i some example code.

nawet zawiera statyczny puli wątków oraz przykładowe prawie dokładnie to, co chcesz: async_1.cpp

#include <experimental/thread_pool> 
#include <iostream> 
#include <tuple> 

namespace execution = std::experimental::execution; 
using std::experimental::static_thread_pool; 

template <class Executor, class Function> 
auto async(Executor ex, Function f) 
{ 
    return execution::require(ex, execution::twoway).twoway_execute(std::move(f)); 
} 

int main() 
{ 
    static_thread_pool pool{1}; 
    auto f = async(pool.executor(), []{ return 42; }); 
    std::cout << "result is " << f.get() << "\n"; 
} 

Dziękuję @ Jared-hoberock za naprowadzenie mnie na P0668R0 jak dużo prostsze kontynuacja do P0443R1, do której odwołałem się we wcześniejszej wersji tej odpowiedzi.

To uproszczenie zostało zastosowane, a teraz jest zarówno artykuł opisujący uzasadnienie (P0761R0), jak i znacznie prostszą wersję standardowego brzmienia w P0443R2.


W lipcu 2017 roku, jedyny rzeczywisty przypuszczenie Widziałem na dostawę jest: Michael Wong, redaktor Concurrency TS --- pojazd standaryzacja eXecutors --- czuje "confident that it will make it into C++20"

.