- Implementieren Sie die Funktionen
void parallel_transform(std::vector<float> &v, size_t num_threads, std::function<float(float)> f);
Startet num_threads
viele std::thread
s um die Einträge im Vektor mit f
zu transformieren.
Die Einträge soll gleichmäßig unter den Threads verteilt werden. Der letzte Thread verarbeitet unter Umständen weniger Einträge.
Statt push_back
können Sie Thread-Objekte ohne Kopieren direkt mit emplace_back
im Vector-Speicher allokieren. emplace_back
erhält dabei dieselben Argumente wie der Konstruktor von std::thread
.void parallel_transform(std::vector<float> &v, size_t num_asyncs, std::function<float(float)> f)
Nutzt std::async
s um die selbe Aufgabe zu lösen. C++ kümmert sich hierbei um das Management der Threads. Verteilen Sie hierbei die Einträge wieder gleichmäßig auf die Async-Instanzen.
- Messen Sie den Laufzeitunterschied für verschieden große Vektoren und Threadzahlen.
Hinweis: Direkt im Code kann die Laufzeitmessung etwa mit folgendem Code erreicht werden:
#include <iostream>
#include <chrono>
#include <thread>
int main(){
using namespace std::chrono_literals;
std::cout << "Hello waiter\n" << std::flush;
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(2s);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "Waited " << elapsed.count() << " ms\n";
}
- Führen Sie den folgenden Code-Schnipsel aus. Falls „Finished“ bei Ihnen auch nach einigen Sekunden nicht angezeigt wird, erklären Sie, warum das passiert. Welche minimalen Änderungen müssen unternommen werden, damit der Code funktioniert? Erklären Sie diese Wahl kurz.
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
std::mutex mutex1, mutex2;
void thread_a() {
mutex2.lock();
std::cout << "Thread A" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
mutex1.lock();
mutex2.unlock();
mutex1.unlock();
}
void thread_b() {
mutex1.lock();
std::cout << "Thread B" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
mutex2.lock();
mutex1.unlock();
mutex2.unlock();
}
int main() {
std::thread t1(thread_a);
std::thread t2(thread_b);
t1.join();
t2.join();
std::cout << "Finished" << std::endl;
return 0;
}
- Wie Sie sicher bemerkt haben, können Sie innerhalb der
parallel_transform
-Threads keine Ausgabe vornehmen, ohne dass die Zeichen wild durchmischt werden, etwa hier: float v_new = f(v);
std::cout << "alte :" << v << ", neue:" << v_new << std::endl;
- Erweitern Sie
parallel_transform
so, dass nach jeder Berechnung eines Wertes durch f
das Ergebnis und der Index ausgegeben wird, es aber keine Vermischung der Ausgabe gibt. Verwenden Sie dafür einen Mutex. - Was bedeutet diese Änderung für den Laufzeitgewinn aus Aufgabenteil 1?