Skip to content

Commit

Permalink
Add test case for Concurrent Vector Guard
Browse files Browse the repository at this point in the history
  • Loading branch information
lczech committed Dec 9, 2024
1 parent b52958b commit a586bf0
Showing 1 changed file with 69 additions and 0 deletions.
69 changes: 69 additions & 0 deletions test/src/utils/threading/concurrent_vector_guard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@

#include "src/common.hpp"

#include "genesis/utils/math/random.hpp"
#include "genesis/utils/threading/concurrent_vector_guard.hpp"
#include "genesis/utils/threading/thread_pool.hpp"

#include <atomic>
#include <ctime>
#include <vector>

Expand Down Expand Up @@ -89,3 +91,70 @@ TEST( Threading, ConcurrentVectorGuard )
}
EXPECT_EQ( 0, cnt_wrong );
}

TEST( Threading, VectorEntries )
{
// Test siez
size_t const num_threads = 10;
size_t const num_vecs = 1000;
size_t const max_length = 1000;

auto const seed = ::time(nullptr);
permuted_congruential_generator_init( seed );
LOG_INFO << "Seed: " << seed;

// Fill a vector with vectors of different lengths, with values that are all 1.
// Then, we can add up those entries to get our expected number of processed elements.
size_t exp_num_elem = 0;
auto data = std::vector<std::vector<int>>( num_vecs );
for( auto& vec : data ) {
vec.resize( permuted_congruential_generator( max_length ), 1 );
exp_num_elem += vec.size();
}

// Now we spin up some threads and erase elements in parallel from the vector, starting
// at different offsets for speed. Each thread starts processing elements, and removes
// them from their vec within data, until empty, and then moves to the next one.
auto thread_pool = std::make_shared<ThreadPool>( num_threads );
auto vector_guard = ConcurrentVectorGuard( num_vecs );
std::atomic<size_t> num_elem = 0;
for( size_t t = 0; t < num_threads; ++t ) {
thread_pool->enqueue_detached([&, t]()
{
// Get equally distributed starting group indices.
size_t const start_group_idx = t * num_vecs / num_threads;
size_t cur_group_idx = start_group_idx;

while( true ) {
// Lock the vector that we are currently operating on.
auto lock = vector_guard.get_lock_guard(cur_group_idx);

// Find the next vector that has data.
// If the current one does not, we move to the next (in the next iteration).
// If we looped around and arrive back where we started, we are done.
if( data[cur_group_idx].empty() ) {
++cur_group_idx;
cur_group_idx %= num_vecs;
if( cur_group_idx == start_group_idx ) {
break;
}
continue;
}

// Remove the last entry from the vector
// LOG_DBG << "thread " << t << " popping data[" << cur_group_idx << "] at " << (data[cur_group_idx].size() - 1);
data[cur_group_idx].pop_back();
++num_elem;
}
});
}
thread_pool->wait_for_all_pending_tasks();

// Now we expect to have processed exactly the elements that we put in data,
// and that we have left the vectors in data empty.
EXPECT_EQ( exp_num_elem, num_elem );
for( auto const& vec : data ) {
EXPECT_TRUE( vec.empty() );
EXPECT_EQ( 0, vec.size() );
}
}

0 comments on commit a586bf0

Please sign in to comment.