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.

166 lines
6.7 KiB

  1. #pragma once
  2. #include <iostream>
  3. #include <vector>
  4. #include <chrono>
  5. #include <numeric>
  6. #include <pthread.h>
  7. #include <semaphore.h>
  8. #include <numa.h>
  9. #include <dml/dml.hpp>
  10. #include "util/barrier.hpp"
  11. #include "util/dml-helper.hpp"
  12. #include "util/task-data.hpp"
  13. #include "util/array_utils.h"
  14. #define LOG_CODE_INFO "Location: " << __FILE__ << "@" << __LINE__ << "::" << __FUNCTION__ << std::endl
  15. #define LOG_ERR { pthread_t t = pthread_self(); std::cerr << "--- BEGIN ERROR MSG ---" << std::endl << "Physical: [Node " << args->numa_node << " | Thread " << t << "]" << std::endl; } std::cerr << LOG_CODE_INFO
  16. #define CHECK_STATUS(status,msg) { if (status != dml::status_code::ok) { LOG_ERR << "Status Code: " << StatusCodeToString(status) << std::endl << msg << std::endl; args->status = status; return nullptr; }}
  17. #define ADD_TIMING_MESSUREMENT { if (i >= 5) { args->submit_duration.emplace_back(std::chrono::duration_cast<std::chrono::nanoseconds>(se - st).count()); args->complete_duration.emplace_back(std::chrono::duration_cast<std::chrono::nanoseconds>(et - se).count()); args->combined_duration.emplace_back(std::chrono::duration_cast<std::chrono::nanoseconds>(et - st).count());}}
  18. template <typename path>
  19. void* thread_function(void* argp) {
  20. TaskData* args = reinterpret_cast<TaskData*>(argp);
  21. // set numa node and core affinity of the current thread
  22. numa_run_on_node(args->numa_node);
  23. // allocate memory for the move operation on the requested numa nodes
  24. void* src = numa_alloc_onnode(args->size, args->nnode_src);
  25. void* dst = numa_alloc_onnode(args->size, args->nnode_dst);
  26. dml::data_view srcv = dml::make_view(reinterpret_cast<uint8_t*>(src), args->size);
  27. dml::data_view dstv = dml::make_view(reinterpret_cast<uint8_t*>(dst), args->size);
  28. fill_mt(reinterpret_cast<uint8_t*>(src), args->size, std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max());
  29. args->status = dml::status_code::ok;
  30. args->rep_completed = 0;
  31. // we add 5 as the first 5 iterations will not be meassured
  32. // to remove exceptional values encountered during warmup
  33. for (uint32_t i = 0; i < args->rep_count + 5; i++) {
  34. // synchronize the start of each iteration
  35. // using the barrier structure
  36. args->barrier_->wait();
  37. if (args->batch_submit) {
  38. const auto st = std::chrono::steady_clock::now();
  39. auto sequence = dml::sequence(args->batch_size, std::allocator<dml::byte_t>());
  40. for (uint32_t j = 0; j < args->batch_size; j++) {
  41. // block_on_fault() is required to submit the task in a way so that the
  42. // DSA engine can handle page faults itself together with the IOMMU which
  43. // requires the WQ to be configured to allow this too
  44. const auto status = sequence.add(dml::mem_copy.block_on_fault(), srcv, dstv);
  45. CHECK_STATUS(status, "Adding operation to batch failed!");
  46. }
  47. // we use the asynchronous submit-routine even though this is not required
  48. // here, however the project later on will only use async operation and
  49. // therefore this behaviour should be benchmarked
  50. auto handler = dml::submit<path>(dml::batch, sequence);
  51. const auto se = std::chrono::steady_clock::now();
  52. auto result = handler.get();
  53. const auto et = std::chrono::steady_clock::now();
  54. const dml::status_code status = result.status;
  55. CHECK_STATUS(status, "Batch completed with an Error!");
  56. ADD_TIMING_MESSUREMENT;
  57. }
  58. else if (args->batch_size > 1) {
  59. // implementation for non-batched batch submit follows here
  60. // this means we submit a bunch of work as single descriptors
  61. // but then dont wait for the completion immediately
  62. std::vector<dml::handler<dml::mem_copy_operation, std::allocator<uint8_t>>> handlers;
  63. const auto st = std::chrono::steady_clock::now();
  64. for (uint32_t j = 0; j < args->batch_size; j++) {
  65. // block_on_fault() is required to submit the task in a way so that the
  66. // DSA engine can handle page faults itself together with the IOMMU which
  67. // requires the WQ to be configured to allow this too
  68. handlers.emplace_back(dml::submit<path>(dml::mem_copy.block_on_fault(), srcv, dstv));
  69. }
  70. const auto se = std::chrono::steady_clock::now();
  71. for (auto& handler : handlers) {
  72. auto result = handler.get();
  73. const dml::status_code status = result.status;
  74. CHECK_STATUS(status, "Operation completed with an Error!");
  75. }
  76. const auto et = std::chrono::steady_clock::now();
  77. ADD_TIMING_MESSUREMENT;
  78. }
  79. else {
  80. const auto st = std::chrono::steady_clock::now();
  81. // we use the asynchronous submit-routine even though this is not required
  82. // here, however the project later on will only use async operation and
  83. // therefore this behaviour should be benchmarked
  84. // block_on_fault() is required to submit the task in a way so that the
  85. // DSA engine can handle page faults itself together with the IOMMU which
  86. // requires the WQ to be configured to allow this too
  87. auto handler = dml::submit<path>(dml::mem_copy.block_on_fault(), srcv, dstv);
  88. const auto se = std::chrono::steady_clock::now();
  89. auto result = handler.get();
  90. const auto et = std::chrono::steady_clock::now();
  91. const dml::status_code status = result.status;
  92. CHECK_STATUS(status, "Operation completed with an Error!");
  93. ADD_TIMING_MESSUREMENT;
  94. }
  95. // again: we do not count the first 5 repetitions
  96. if (i >= 5) args->rep_completed++;
  97. }
  98. // free the allocated memory regions on the selected nodes
  99. numa_free(src, args->size);
  100. numa_free(dst, args->size);
  101. return nullptr;
  102. }
  103. template <typename path>
  104. void execute_dml_memcpy(std::vector<TaskData>& args) {
  105. barrier task_barrier(args.size());
  106. std::vector<pthread_t> threads;
  107. // initialize numa library
  108. numa_available();
  109. // for each submitted task we link the semaphore
  110. // and create the thread, passing the argument
  111. for (auto& arg : args) {
  112. arg.barrier_ = &task_barrier;
  113. threads.emplace_back();
  114. if (pthread_create(&threads.back(), nullptr, thread_function<path>, &arg) != 0) {
  115. std::cerr << "Error creating thread" << std::endl;
  116. exit(1);
  117. }
  118. }
  119. for (pthread_t& t : threads) {
  120. pthread_join(t, nullptr);
  121. }
  122. }