This contains my bachelors thesis and associated tex files, code snippets and maybe more. Topic: Data Movement in Heterogeneous Memories with Intel Data Streaming Accelerator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
2.9 KiB

  1. #pragma once
  2. #include <iostream>
  3. #include <vector>
  4. #include <chrono>
  5. #include <pthread.h>
  6. #include <semaphore.h>
  7. #include <numa.h>
  8. #include <dml/dml.hpp>
  9. struct ThreadArgs {
  10. // thread placement / engine selection
  11. uint8_t numa_node;
  12. uint8_t core;
  13. // region size and source+destination for move
  14. size_t size;
  15. uint8_t nnode_src;
  16. uint8_t nnode_dst;
  17. // thread output
  18. dml::status_code status;
  19. std::chrono::microseconds duration;
  20. // set by execution
  21. sem_t* sig;
  22. };
  23. template <typename path>
  24. void* thread_function(void* argp) {
  25. ThreadArgs* args = reinterpret_cast<ThreadArgs*>(argp);
  26. // set numa node and core affinity of the current thread
  27. numa_run_on_node(args->numa_node);
  28. cpu_set_t cpuset;
  29. CPU_ZERO(&cpuset);
  30. CPU_SET(args->core, &cpuset);
  31. if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) != 0) {
  32. std::cerr << "Error setting affinity for thread designated to core " << args->core << " on node " << args->numa_node << std::endl;
  33. return nullptr;
  34. }
  35. // allocate memory for the move operation on the requested numa nodes
  36. void* src = numa_alloc_onnode(args->size, args->nnode_src);
  37. void* dst = numa_alloc_onnode(args->size, args->nnode_dst);
  38. dml::data_view srcv = dml::make_view(reinterpret_cast<uint8_t*>(src), args->size);
  39. dml::data_view dstv = dml::make_view(reinterpret_cast<uint8_t*>(dst), args->size);
  40. // wait for specified signal so that all operations start at the same time
  41. sem_wait(args->sig);
  42. const auto st = std::chrono::high_resolution_clock::now();
  43. // we use the asynchronous submit-routine even though this is not required
  44. // here, however the project later on will only use async operation
  45. auto handler = dml::submit<path>(dml::mem_move, srcv, dstv);
  46. auto result = handler.get();
  47. const auto et = std::chrono::high_resolution_clock::now();
  48. // free the allocated memory regions on the selected nodes
  49. numa_free(src, args->size);
  50. numa_free(dst, args->size);
  51. args->duration = std::chrono::duration_cast<std::chrono::microseconds>(et - st);
  52. args->status = result.status;
  53. args->sig = nullptr;
  54. return nullptr;
  55. }
  56. template <typename path>
  57. void execute_mem_move(std::vector<ThreadArgs>& args) {
  58. sem_t sem;
  59. std::vector<pthread_t> threads;
  60. // initialize semaphore and numactl-library
  61. sem_init(&sem, 0, 0);
  62. numa_available();
  63. // for each submitted task we link the semaphore
  64. // and create the thread, passing the argument
  65. for (auto& arg : args) {
  66. arg.sig = &sem;
  67. threads.emplace_back();
  68. if (pthread_create(&threads.back(), nullptr, thread_function<path>, &arg) != 0) {
  69. std::cerr << "Error creating thread" << std::endl;
  70. exit(1);
  71. }
  72. }
  73. // post will make all waiting threads pass
  74. sem_post(&sem);
  75. for (pthread_t& t : threads) {
  76. pthread_join(t, nullptr);
  77. }
  78. sem_destroy(&sem);
  79. }