main.cc 7.41 KB
Newer Older
1 2 3 4
/* 
 * Main script that applies the linearizability tester on embb data structures.
 */

5 6 7 8 9 10
// Enable assertions even in Release mode
#ifdef NDEBUG
#undef NDEBUG
#include <assert.h>
#endif

11
#include <linearizability_tester.h>
12
#include <sequential_data_structures.h>
13 14 15 16 17
#include <tests.h>

#include <embb/base/thread.h>
#include <embb/containers/lock_free_stack.h>
#include <embb/containers/lock_free_mpmc_queue.h>
18
#include <embb/containers/wait_free_spsc_queue.h>
19

20 21
// Each thread executes quasi randomly operations (TryEqneueu, TryDequeue)
// on the concurrent data structure and construct the history.
22 23
template<std::size_t N, class S>
static void embb_worker_stack(
lucapegolotti committed
24 25 26
  const WorkerConfiguration& worker_configuration,
  ConcurrentLog<state::Stack<N>>& concurrent_log,
  S& concurrent_stack)
27
{
lucapegolotti committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
  std::random_device rd;
  std::mt19937 gen(rd());
  std::uniform_int_distribution<> value_dist('\0', worker_configuration.max_value);
  std::uniform_int_distribution<> percentage_dist(0, 100);

  bool ret;

  char value;
  unsigned percentage;
  EntryPtr<state::Stack<N>> call_entry_ptr;
  for (unsigned number_of_ops{ 0U };
  number_of_ops < worker_configuration.number_of_ops;
    ++number_of_ops)
  {
    value = value_dist(rd);
    percentage = percentage_dist(rd);
44 45
    // Note: this threshold affects considerably the running time of the test
    // increasing threshold -> increasing running time
lucapegolotti committed
46 47 48 49 50 51 52 53 54 55 56 57 58
    if (percentage < 30)
    {
      call_entry_ptr = concurrent_log.push_back(state::Stack<N>::make_try_push_call(value));
      ret = concurrent_stack.TryPush(value);
      concurrent_log.push_back(call_entry_ptr, state::Stack<N>::make_try_push_ret(ret));
    }
    else
    {
      call_entry_ptr = concurrent_log.push_back(state::Stack<N>::make_try_pop_call());
      ret = concurrent_stack.TryPop(value);
      concurrent_log.push_back(call_entry_ptr, state::Stack<N>::make_try_pop_ret(ret, value));
    }
  }
59 60
}

61
// Each thread executes quasi randomly operations (TryEnqueue, TryDequeue)
62
// on the concurrent data structure and construct the history.
63 64
template<std::size_t N, class S>
static void embb_worker_queue(
lucapegolotti committed
65 66 67
  const WorkerConfiguration& worker_configuration,
  ConcurrentLog<state::Queue<N>>& concurrent_log,
  S& concurrent_queue)
68
{
lucapegolotti committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
  std::random_device rd;
  std::mt19937 gen(rd());
  std::uniform_int_distribution<> value_dist('\0', worker_configuration.max_value);
  std::uniform_int_distribution<> percentage_dist(0, 100);

  bool ret;

  char value;
  unsigned percentage;
  EntryPtr<state::Queue<N>> call_entry_ptr;
  for (unsigned number_of_ops{ 0U };
  number_of_ops < worker_configuration.number_of_ops;
    ++number_of_ops)
  {
    value = value_dist(rd);
    percentage = percentage_dist(rd);
85 86
    // Note: this threshold affects considerably the running time of the test
    // increasing threshold -> increasing running time
lucapegolotti committed
87 88 89 90 91 92 93 94 95 96 97 98 99
    if (percentage < 20)
    {
      call_entry_ptr = concurrent_log.push_back(state::Queue<N>::make_try_enqueue_call(value));
      ret = concurrent_queue.TryEnqueue(value);
      concurrent_log.push_back(call_entry_ptr, state::Queue<N>::make_try_enqueue_ret(ret));
    }
    else
    {
      call_entry_ptr = concurrent_log.push_back(state::Queue<N>::make_try_dequeue_call());
      ret = concurrent_queue.TryDequeue(value);
      concurrent_log.push_back(call_entry_ptr, state::Queue<N>::make_try_dequeue_ret(ret, value));
    }
  }
100 101
}

102
// Creates the history and apply the tester on it
103
template <class S>
104
static void embb_experiment_stack()
105
{
lucapegolotti committed
106 107 108 109 110 111 112 113 114 115
  constexpr std::chrono::hours max_duration{ 1 };
  constexpr std::size_t N = 560000U;
  constexpr unsigned number_of_threads = 4U;
  constexpr WorkerConfiguration worker_configuration = { '\24', 70000U };
  constexpr unsigned log_size = number_of_threads * worker_configuration.number_of_ops;

  Result<state::Stack<N>> result;
  ConcurrentLog<state::Stack<N>> concurrent_log{ 2U * log_size };
  S concurrent_stack(N);

116 117 118 119 120
  // Check if push and pop operations are possible
  char value;
  assert(concurrent_stack.TryPush(5));
  assert(concurrent_stack.TryPop(value));
  
lucapegolotti committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
  // create history
  start_threads(number_of_threads, embb_worker_stack<N, S>, std::cref(worker_configuration),
    std::ref(concurrent_log), std::ref(concurrent_stack));

  const std::size_t number_of_entries{ concurrent_log.number_of_entries() };
  const LogInfo<state::Stack<N>> log_info{ concurrent_log.info() };

  auto start = std::chrono::system_clock::now();
  auto end = std::chrono::system_clock::now();
  std::chrono::seconds seconds;

  start = std::chrono::system_clock::now();
  {
    Log<state::Stack<N>> log_copy{ log_info };
    assert(log_copy.number_of_entries() == number_of_entries);

    LinearizabilityTester<state::Stack<N>, Option::LRU_CACHE> tester{ log_copy.info(), max_duration };
    tester.check(result);
139 140
    // If structure is not linearizabile break run using assertion
    assert(result.is_timeout() || result.is_linearizable());
lucapegolotti committed
141 142 143 144 145 146
  }
  end = std::chrono::system_clock::now();
  seconds = std::chrono::duration_cast<std::chrono::seconds>(end - start);
  std::cout << "History length: " << number_of_entries
    << ", elapsed time: "
    << seconds.count() << " s " << std::endl;
147 148
}

149
// Creates the history and apply the tester on it
150
template <class S>
151
static void embb_experiment_queue()
152
{
lucapegolotti committed
153 154 155 156 157 158 159 160 161 162 163
  constexpr std::chrono::hours max_duration{ 1 };
  constexpr std::size_t N = 560000U;
  constexpr unsigned number_of_threads = 4U;
  constexpr WorkerConfiguration worker_configuration = { '\24', 70000U };
  constexpr unsigned log_size = number_of_threads * worker_configuration.number_of_ops;


  Result<state::Queue<N>> result;
  ConcurrentLog<state::Queue<N>> concurrent_log{ 2U * log_size };
  S concurrent_queue(N);

164 165 166 167
  // check if enqueue and dequeue operations are possible
  char value;
  assert(concurrent_queue.TryEnqueue(5));
  assert(concurrent_queue.TryDequeue(value));
lucapegolotti committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

  // create history
  start_threads(number_of_threads, embb_worker_queue<N, S>, std::cref(worker_configuration),
    std::ref(concurrent_log), std::ref(concurrent_queue));
  const std::size_t number_of_entries{ concurrent_log.number_of_entries() };
  const LogInfo<state::Queue<N>> log_info{ concurrent_log.info() };

  auto start = std::chrono::system_clock::now();
  auto end = std::chrono::system_clock::now();
  std::chrono::seconds seconds;

  start = std::chrono::system_clock::now();
  {
    Log<state::Queue<N>> log_copy{ log_info };
    assert(log_copy.number_of_entries() == number_of_entries);
    LinearizabilityTester<state::Queue<N>, Option::LRU_CACHE> tester{ log_copy.info(), max_duration };
    tester.check(result);
185 186
    // If structure is not linearizabile break run using assertion
    assert(result.is_timeout() || result.is_linearizable());
lucapegolotti committed
187 188 189 190 191 192
  }
  end = std::chrono::system_clock::now();
  seconds = std::chrono::duration_cast<std::chrono::seconds>(end - start);
  std::cout << "History length: " << number_of_entries
    << ", elapsed time:  "
    << seconds.count() << " s " << std::endl;
193 194 195 196
}


int main()
lucapegolotti committed
197
{  
198

199
  // Test functions and structures in linearizability_tester.h and sequential_data_structures.h
200 201 202 203
  run_tests();
  embb::base::Thread::SetThreadsMaxCount(255);
  
  std::cout << "Linearizability test on LockFreeMPMCQueue" << std::endl;
204
  embb_experiment_queue<embb::containers::LockFreeMPMCQueue<char>>();
205

206 207 208
  std::cout << "Linearizability test on WaitFreeSPSCQueue" << std::endl;
  embb_experiment_queue<embb::containers::WaitFreeSPSCQueue<char>>();

209
  std::cout << "Linearizability test on LockFreeStack" << std::endl;
210
  embb_experiment_stack<embb::containers::LockFreeStack<char>>();
211 212 213
  return 0;
}