You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

memoryfile.cpp 6.2 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. #include <atomic>
  2. #include <unistd.h>
  3. #include <sys/mman.h>
  4. #include <fcntl.h>
  5. #include <filesystem>
  6. #include <thread>
  7. #include <vector>
  8. #include <iostream>
  9. #include <chrono>
  10. class SpinLock {
  11. public:
  12. void lock() {
  13. while (locked_.test_and_set(std::memory_order_acquire))
  14. std::this_thread::yield();
  15. }
  16. void unlock() {
  17. locked_.clear(std::memory_order_release);
  18. }
  19. private:
  20. std::atomic_flag locked_ = ATOMIC_FLAG_INIT;
  21. };
  22. class SpinLockGuard {
  23. public:
  24. SpinLockGuard(SpinLock& lock)
  25. : lock_(lock) {
  26. lock_.lock();
  27. }
  28. ~SpinLockGuard() {
  29. lock_.unlock();
  30. }
  31. private:
  32. SpinLock& lock_;
  33. };
  34. class MemoryFile {
  35. public:
  36. enum Mode { CREATE, OPEN, CREATE_OR_OPEN };
  37. struct Header {
  38. size_t bytes_written{}, bytes_mapped{};
  39. };
  40. static constexpr size_t DataOffset = sizeof(Header);
  41. MemoryFile(const std::string& filename, Mode mode) {
  42. switch (mode) {
  43. case CREATE:
  44. create_chunk(filename);
  45. break;
  46. case OPEN:
  47. open_chunk(filename);
  48. break;
  49. case CREATE_OR_OPEN:
  50. if (std::filesystem::exists(filename)) open_chunk(filename);
  51. else create_chunk(filename);
  52. break;
  53. }
  54. }
  55. ~MemoryFile() {
  56. if (base_)
  57. munmap(base_, *ptr_bytes_mapped_);
  58. if (fd_ != -1)
  59. close(fd_);
  60. }
  61. void commit_message(std::string_view msg) {
  62. // SpinLockGuard guard(lock_);
  63. std::lock_guard guard(mutex_);
  64. ensure_room_for_message(msg.size());
  65. auto nbytes = *ptr_bytes_written_;
  66. std::copy(msg.begin(), msg.end(), base_ + nbytes);
  67. nbytes += msg.size();
  68. *ptr_bytes_written_ = nbytes;
  69. }
  70. private:
  71. size_t grow_file(size_t new_size) {
  72. auto rc = lseek(fd_, new_size - 1, SEEK_SET);
  73. if (rc + 1 != new_size)
  74. throw std::runtime_error("error: lseek failed trying to grow file");
  75. rc = write(fd_, "", 1);
  76. if (rc != 1)
  77. throw std::runtime_error("error: write to extend file failed");
  78. return new_size;
  79. }
  80. void map_memory(size_t nbytes) {
  81. base_ = (char*)mmap(nullptr, nbytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
  82. if (base_ == nullptr)
  83. throw std::runtime_error("error: failed to map memory");
  84. Header *header = (Header*)base_;
  85. ptr_bytes_written_ = &(header->bytes_written);
  86. ptr_bytes_mapped_ = &(header->bytes_mapped);
  87. *ptr_bytes_mapped_ = nbytes;
  88. }
  89. void create_chunk(const std::string& filename) {
  90. namespace fs = std::filesystem;
  91. if (fs::exists(fs::path(filename)))
  92. throw std::runtime_error("error: chunk already exists");
  93. fd_ = open(filename.c_str(), O_CREAT | O_RDWR, 0777);
  94. if (fd_ == -1)
  95. throw std::runtime_error("error: failed to create chunk");
  96. auto nbytes = grow_file(1024 * 1024);
  97. map_memory(nbytes);
  98. *ptr_bytes_written_ = sizeof(Header);
  99. }
  100. void open_chunk(const std::string& filename) {
  101. namespace fs = std::filesystem;
  102. if (not fs::exists(fs::path(filename)))
  103. throw std::runtime_error("error: chunk does not exist");
  104. fd_ = open(filename.c_str(), O_RDWR, 0777);
  105. if (fd_ == -1)
  106. throw std::runtime_error("error: failed to open chunk");
  107. auto nbytes = fs::file_size(fs::path(filename));
  108. map_memory(nbytes);
  109. }
  110. void ensure_room_for_message(size_t n) {
  111. auto nw = *ptr_bytes_written_, nm = *ptr_bytes_mapped_;
  112. if (nw + n >= nm) {
  113. munmap(base_, nm);
  114. base_ = nullptr;
  115. auto new_size = std::max(2 * nm, nw + n + 1);
  116. auto nbytes = grow_file(new_size);
  117. map_memory(nbytes);
  118. }
  119. }
  120. // Pointer to the number of bytes written.
  121. volatile size_t *ptr_bytes_written_{nullptr};
  122. // Pointer to the number of bytes in the mapped region.
  123. volatile size_t *ptr_bytes_mapped_{nullptr};
  124. // Pointer to the mapped region.
  125. char *base_{nullptr};
  126. // File descriptor of the mapped region.
  127. int fd_{-1};
  128. // Use simple spin-lock for thread synchronization.
  129. // SpinLock lock_;
  130. // Use a mutex for thread synchronization.
  131. std::mutex mutex_;
  132. };
  133. template<class Clock = std::chrono::high_resolution_clock>
  134. class StopWatch
  135. {
  136. public:
  137. StopWatch()
  138. : start_tp_(Clock::now())
  139. , last_tp_(start_tp_)
  140. { }
  141. auto now() const
  142. {
  143. std::atomic_thread_fence(std::memory_order_relaxed);
  144. auto current_tp = Clock::now();
  145. std::atomic_thread_fence(std::memory_order_relaxed);
  146. return current_tp;
  147. }
  148. auto mark()
  149. {
  150. auto current_tp = now();
  151. auto elapsed = current_tp - last_tp_;
  152. last_tp_ = current_tp;
  153. return elapsed;
  154. }
  155. template<class Units = typename Clock::duration>
  156. auto elapsed_duration()
  157. {
  158. auto elapsed = mark();
  159. return std::chrono::duration_cast<Units>(elapsed);
  160. }
  161. template<class Units = typename Clock::duration>
  162. auto elapsed_time()
  163. {
  164. auto elapsed = mark();
  165. return std::chrono::duration_cast<Units>(elapsed).count();
  166. }
  167. private:
  168. typename Clock::time_point start_tp_;
  169. typename Clock::time_point last_tp_;
  170. };
  171. int main(int argc, const char *argv[]) {
  172. const auto filename = "/tmp/x.out";
  173. size_t nth = argc >= 2 ? atoi(argv[1]) : 1;
  174. size_t nmsg = argc >= 3 ? atoi(argv[2]) : 1'000'000;
  175. size_t msg_per_thread = nmsg / nth;
  176. std::filesystem::remove(std::filesystem::path(filename));
  177. MemoryFile log(filename, MemoryFile::CREATE);
  178. StopWatch timer;
  179. timer.mark();
  180. std::vector<std::thread> threads;
  181. for (auto i = 0; i < nth; ++i)
  182. threads.emplace_back([&]() {
  183. for (auto i = 0; i < msg_per_thread; ++i)
  184. log.commit_message("This is a log message\n");
  185. });
  186. for (auto& thread : threads)
  187. thread.join();
  188. auto ms = timer.elapsed_duration<std::chrono::milliseconds>().count();
  189. std::cout << ms << " ms" << std::endl;
  190. return 0;
  191. }