Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mem_tg: fix support for more than 8 memory channels #3138

Merged
merged 4 commits into from
Aug 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 29 additions & 48 deletions samples/mem_tg/tg_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ class tg_test : public test_command
}

// Convert number of transactions to bandwidth (GB/s)
double bw_calc(uint64_t xfer_bytes, uint64_t num_ticks)
double bw_calc(uint64_t xfer_bytes, uint64_t num_ticks) const
{
return (double)(xfer_bytes) / ((1000.0 / (double)tg_exe_->mem_speed_ * (double)num_ticks));
}

void tg_perf (mem_tg *tg_exe_) {
void tg_perf (mem_tg *tg_exe_) const
{
// Lock mutex before printing so print statements don't collide between threads.
std::unique_lock<std::mutex> print_lock(tg_print_mutex);
std::cout << "Channel " << std::stoi(tg_exe_->mem_ch_[0]) << ":" << std::endl;
Expand Down Expand Up @@ -121,7 +121,7 @@ class tg_test : public test_command
print_lock.unlock();
}

bool tg_wait_test_completion (mem_tg *tg_exe_)
bool tg_wait_test_completion (mem_tg *tg_exe_) const
{
/* Wait for test completion */
uint32_t timeout = MEM_TG_TEST_TIMEOUT * tg_exe_->loop_ * tg_exe_->bcnt_;
Expand Down Expand Up @@ -152,7 +152,7 @@ class tg_test : public test_command
return true;
}

int config_input_options(mem_tg *tg_exe_)
int config_input_options(mem_tg *tg_exe_) const
{
if (!tg_exe_)
return -1;
Expand All @@ -178,7 +178,7 @@ class tg_test : public test_command
}

// The test state has been configured. Run one test instance.
int run_mem_test(mem_tg *tg_exe_)
int run_mem_test(mem_tg *tg_exe_) const
{
int status = 0;

Expand Down Expand Up @@ -239,72 +239,53 @@ class tg_test : public test_command
exit(1);
}

// Parse mem_ch_ into array of selected channels and number of channels
int *channels = NULL;
int num_channels = 0;
// Parse mem_ch_ into array of selected channels
std::vector<uint32_t> channels;
if ((tg_exe_->mem_ch_[0]).find("all") == 0) {
uint64_t mem_capability = tg_exe_->read64(MEM_TG_CTRL);
channels = new int[sizeof(uint64_t)]; // size should be same as mem_capability
for (uint32_t i = 0; i < sizeof(uint64_t); i++) { // number of iterations should be same as mem_capability
for (uint32_t i = 0; i < 64; i++) { // number of iterations should be same as mem_capability
if ((mem_capability & (1ULL << i)) != 0) {
channels[num_channels] = i;
num_channels += 1;
channels.emplace_back(i);
}
}
channels[num_channels] = -1; // EOL
} else {
channels = new int[tg_exe_->mem_ch_.size()];
num_channels = tg_exe_->mem_ch_.size();
try{
for (unsigned i = 0; i < tg_exe_->mem_ch_.size(); i++) {
channels[i] = std::stoi(tg_exe_->mem_ch_[i]);
try {
for (const std::string &mem_ch: tg_exe_->mem_ch_) {
channels.emplace_back(std::stoi(mem_ch));
}
} catch (std::invalid_argument &e) {
std::cerr << "Error: invalid argument to std::stoi";
delete[] channels;
return 1;
}
}

// Spawn threads for each channel:
mem_tg *thread_tg_exe_objects[num_channels];

// Spawn threads for each channel
std::vector<std::future<int>> futures;
std::vector<std::promise<int>> promises(num_channels);
std::vector<std::thread> threads;
tg_num_threads = num_channels;
tg_num_threads = channels.size();
tg_waiting_threads_counter = 0;
for (int i = 0; i < num_channels; i++) {
if (channels[i] == -1) break;
thread_tg_exe_objects[i] = new mem_tg;
tg_exe_->duplicate(thread_tg_exe_objects[i]);
thread_tg_exe_objects[i]->mem_ch_.clear();
thread_tg_exe_objects[i]->mem_ch_.push_back(std::to_string(channels[i]));
futures.push_back(promises[i].get_future());
threads.emplace_back([&, i] {
promises[i].set_value(run_thread_single_channel(thread_tg_exe_objects[i]));
for (auto c: channels) {
std::promise<int> p;
futures.emplace_back(p.get_future());
threads.emplace_back([this, c, p = std::move(p)]() mutable {
mem_tg tg_exe;
tg_exe_->duplicate(&tg_exe);
tg_exe.mem_ch_.clear();
tg_exe.mem_ch_.push_back(std::to_string(c));
p.set_value(run_thread_single_channel(&tg_exe));
});
}

// Wait for all threads to exit then collect their exit statuses
// Wait for all threads to exit
for (auto &thread : threads) {
thread.join();
}

std::vector<int> exit_codes;
for (auto &future : futures) {
exit_codes.push_back(future.get());
}

// Print message showing thread statuses
for (int i = 0; i < num_channels; i++) {
std::cout << "Thread on channel " << channels[i] << " exited with status " << (long)exit_codes[i] << std::endl;
for (size_t i = 0; i < channels.size(); i++) {
int ret = futures[i].get();
std::cout << "Thread on channel " << channels[i] << " exited with status " << ret << std::endl;
}

// Delete dynamic allocations
delete[] channels;
for (int i = 0; i < num_channels; i++) {
delete thread_tg_exe_objects[i];
}
return 0;
}

Expand Down
Loading