void ConductExperiment(uint64_t bytes, int iterations, api::Context& ctx1, api::Context& ctx2, const std::string& type_as_string) { auto data = generate<Type>(bytes, 1, 100); common::ThreadPool pool; for (int i = 0; i < iterations; i++) { StatsTimer<true> write_timer; pool.Enqueue([&data, &ctx1, &write_timer]() { auto stream = ctx1.GetNewCatStream(); auto writers = stream->OpenWriters(); assert(writers.size() == 2); write_timer.Start(); auto& writer = writers[1]; for (auto& s : data) { writer(s); } writer.Close(); writers[0].Close(); write_timer.Stop(); }); StatsTimer<true> read_timer; pool.Enqueue([&ctx2, &read_timer]() { auto stream = ctx2.GetNewCatStream(); auto readers = stream->OpenReaders(); assert(readers.size() == 2); auto& reader = readers[0]; read_timer.Start(); while (reader.HasNext()) { reader.Next<Type>(); } read_timer.Stop(); }); pool.LoopUntilEmpty(); std::cout << "RESULT" << " datatype=" << type_as_string << " size=" << bytes << " write_time=" << write_timer << " read_time=" << read_timer << std::endl; } }
void ExperimentAllPairs( api::Context& ctx, const std::string& type_as_string) { for (size_t src = 0; src < ctx.num_workers(); ++src) { for (size_t tgt = 0; tgt < ctx.num_workers(); ++tgt) { // transmit data from worker src -> tgt: only send data if we are // tgt, but as tgt receive from all. auto stream = ctx.GetNewCatStream(); // write phase StatsTimer<true> write_timer(true); { auto writers = stream->OpenWriters(); if (ctx.my_rank() == src) { auto data = Generator<Type>(g_bytes); auto& writer = writers[tgt]; while (data.HasNext()) { writer(data.Next()); } } } write_timer.Stop(); // read phase StatsTimer<true> read_timer(true); { auto reader = stream->OpenCatReader(true); while (reader.HasNext()) { reader.Next<Type>(); } } read_timer.Stop(); size_t read_microsecs = read_timer.Microseconds(); read_microsecs = ctx.AllReduce(read_microsecs, common::maximum<size_t>()); size_t write_microsecs = write_timer.Microseconds(); write_microsecs = ctx.AllReduce(write_microsecs, common::maximum<size_t>()); if (ctx.my_rank() == 0) { std::cout << "RESULT" << " datatype=" << type_as_string << " size=" << g_bytes << " src=" << src << " tgt=" << tgt << " write_time=" << write_microsecs << " read_time=" << read_microsecs << " write_speed_MiBs=" << (g_bytes / write_microsecs * 1000000 / 1024 / 1024) << " read_speed_MiBs=" << (g_bytes / read_microsecs * 1000000 / 1024 / 1024) << std::endl; } } } }
void ExperimentFull( api::Context& ctx, const std::string& type_as_string) { // transmit data to all workers. auto stream = ctx.GetNewCatStream(); // write phase StatsTimer<true> write_timer(true); { auto writers = stream->OpenWriters(); auto data = Generator<Type>(g_bytes); while (data.HasNext()) { Type value = data.Next(); for (size_t tgt = 0; tgt < ctx.num_workers(); ++tgt) { writers[tgt](value); } } } write_timer.Stop(); // read phase StatsTimer<true> read_timer(true); { auto reader = stream->OpenCatReader(true); while (reader.HasNext()) { reader.Next<Type>(); } } read_timer.Stop(); size_t read_microsecs = read_timer.Microseconds(); read_microsecs = ctx.AllReduce(read_microsecs, common::maximum<size_t>()); size_t write_microsecs = write_timer.Microseconds(); write_microsecs = ctx.AllReduce(write_microsecs, common::maximum<size_t>()); uint64_t host_volume = ctx.num_workers() * g_bytes; uint64_t total_volume = ctx.num_workers() * ctx.num_workers() * g_bytes; if (ctx.my_rank() == 0) { std::cout << "RESULT" << " datatype=" << type_as_string << " size=" << g_bytes << " write_time=" << write_microsecs << " read_time=" << read_microsecs << " write_speed_MiBs=" << (g_bytes / write_microsecs * 1000000 / 1024 / 1024) << " read_speed_MiBs=" << (g_bytes / read_microsecs * 1000000 / 1024 / 1024) << " host_write_speed_MiBs=" << (host_volume / write_microsecs * 1000000 / 1024 / 1024) << " host_read_speed_MiBs=" << (host_volume / read_microsecs * 1000000 / 1024 / 1024) << " total_write_speed_MiBs=" << (total_volume / write_microsecs * 1000000 / 1024 / 1024) << " total_read_speed_MiBs=" << (total_volume / read_microsecs * 1000000 / 1024 / 1024) << std::endl; } }
void ConductExperiment(uint64_t bytes, int iterations, api::Context& ctx0, api::Context& ctx1, api::Context& ctx2, const std::string& type_as_string) { // prepare file with random data auto data0 = generate<Type>(bytes / 2, 1, 100); auto data1 = generate<Type>(bytes / 2, 1, 100); std::vector<data::File> files; files.reserve(3); { files.emplace_back(ctx0.GetFile()); auto writer0 = files[0].GetWriter(); for (auto& d : data0) writer0(d); files.emplace_back(ctx1.GetFile()); auto writer1 = files[1].GetWriter(); for (auto& d : data1) writer1(d); files.emplace_back(ctx2.GetFile()); auto writer2 = files[2].GetWriter(); } // worker 0 and worker 1 hold 50% each // worker 0 keeps 2/3 of his data, sends 1/3 to worker 1 // worker 1 keeps first 1/3 of his data, sends 2/3 to worker 2 // worker 2 receives 2/3 from worker 1 // afterwards everybody holds 33% of the data std::vector<std::vector<size_t> > offsets; offsets.push_back({ (size_t)(2 * data0.size() / 3), data0.size(), data0.size() }); offsets.push_back({ 0, (size_t)(data1.size() / 3), data1.size() }); offsets.push_back({ 0, 0, 0 }); std::vector<std::shared_ptr<data::CatStream> > streams; streams.push_back(ctx0.GetNewCatStream()); streams.push_back(ctx1.GetNewCatStream()); streams.push_back(ctx2.GetNewCatStream()); std::vector<StatsTimer<true> > read_timers(3); std::vector<StatsTimer<true> > write_timers(3); common::ThreadPool pool; for (int i = 0; i < iterations; i++) { for (int id = 0; id < 3; id++) { pool.Enqueue([&files, &streams, &offsets, &read_timers, &write_timers, id]() { write_timers[id].Start(); streams[id]->Scatter<Type>(files[id], offsets[id]); write_timers[id].Stop(); auto reader = streams[id]->OpenCatReader(true); read_timers[id].Start(); while (reader.HasNext()) { reader.Next<Type>(); } read_timers[id].Stop(); }); } pool.LoopUntilEmpty(); std::cout << "RESULT" << " datatype=" << type_as_string << " size=" << bytes << " write_time_worker0=" << write_timers[0].Microseconds() << " read_time_worker0=" << read_timers[0].Microseconds() << " write_time_worker1=" << write_timers[1].Microseconds() << " read_time_worker1=" << read_timers[1].Microseconds() << " write_time_worker2=" << write_timers[2].Microseconds() << " read_time_worker2=" << read_timers[2].Microseconds() << std::endl; } }