Line data Source code
1 : // SPDX-License-Identifier: Apache-2.0
2 : /**
3 : * Copyright (C) 2022 Jiho Chu <jiho.chu@samsung.com>
4 : *
5 : * @file swap_device.cpp
6 : * @date 01 July 2022
7 : * @see https://github.com/nnstreamer/nntrainer
8 : * @author Jiho Chu <jiho.chu@samsung.com>
9 : * @bug No known bugs except for NYI items
10 : * @brief Swap device class implementation
11 : *
12 : */
13 :
14 : #include <cstring>
15 : #include <fcntl.h>
16 : #include <malloc.h>
17 : #include <profiler.h>
18 : #include <stdlib.h>
19 :
20 : #include <nntrainer_error.h>
21 : #include <nntrainer_log.h>
22 : #include <swap_device.h>
23 :
24 : #if defined(_WIN32)
25 : #include <Memoryapi.h>
26 : #include <Sysinfoapi.h>
27 : #endif
28 :
29 : namespace nntrainer {
30 :
31 0 : void SwapDevice::start(size_t size, ml::train::ExecutionMode _execution_mode) {
32 0 : if (fd > 0)
33 : return;
34 :
35 0 : if (_execution_mode == ml::train::ExecutionMode::TRAIN) {
36 0 : fd = open(dev_path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_SYNC, 0666UL);
37 : } else {
38 0 : fd = open(dev_path.c_str(), O_RDWR | O_CREAT, 0666UL);
39 0 : execution_mode = _execution_mode;
40 : }
41 0 : NNTR_THROW_IF(fd < 0, std::runtime_error)
42 : << "SwapDevice: open file: " << dev_path;
43 :
44 : off_t off;
45 :
46 : /* make sparse file */
47 0 : off = lseek(fd, size - 1, SEEK_SET);
48 0 : NNTR_THROW_IF(off < 0, std::runtime_error)
49 : << "SwapDevice: seek file: " << dev_path;
50 :
51 0 : if (_execution_mode == ml::train::ExecutionMode::TRAIN) {
52 : ssize_t len;
53 0 : len = write(fd, "", 1);
54 0 : NNTR_THROW_IF(len != 1, std::runtime_error)
55 : << "SwapDevice: write file: " << dev_path;
56 : }
57 0 : off = lseek(fd, 0, SEEK_SET);
58 0 : NNTR_THROW_IF(off < 0, std::runtime_error)
59 : << "SwapDevice: seek file: " << dev_path;
60 : }
61 :
62 0 : void *SwapDevice::getBuffer(off_t offset, size_t size, void *memory_ptr,
63 : unsigned int id, bool alloc_only) {
64 0 : NNTR_THROW_IF(fd <= 0, std::runtime_error)
65 : << "SwapDevice: Device is not started";
66 :
67 : #ifdef USE_MMAP
68 :
69 : #if defined(_WIN32)
70 : SYSTEM_INFO sysInfo;
71 : GetSystemInfo(&sysInfo);
72 : auto page_size = sysInfo.dwAllocationGranularity;
73 : #else
74 0 : auto page_size = sysconf(_SC_PAGE_SIZE);
75 : #endif
76 :
77 0 : if (execution_mode == ml::train::ExecutionMode::INFERENCE) {
78 : // FSU Load Weights
79 0 : auto len_offset = weight_offset.at(id);
80 0 : size_t off = (len_offset.first / page_size) * page_size;
81 0 : size_t diff = len_offset.first - off;
82 0 : size_t len = len_offset.second + diff;
83 :
84 : char *ptr = static_cast<char *>(
85 0 : mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, off));
86 :
87 : const size_t error_buflen = 100;
88 : char error_buf[error_buflen];
89 0 : NNTR_THROW_IF(ptr == MAP_FAILED, std::runtime_error)
90 0 : << "SwapDevice: mmap: " << SAFE_STRERROR(errno, error_buf, error_buflen);
91 :
92 : #if !defined(__ANDROID__) && !defined(_WIN32)
93 : // MADVISE can be used to improve performance.
94 0 : mlock(ptr, len);
95 0 : madvise(ptr, len, MADV_SEQUENTIAL);
96 : #endif
97 :
98 0 : void *buf = static_cast<void *>(ptr + diff);
99 :
100 : memcpy(memory_ptr, buf, len_offset.second);
101 0 : const auto ret = munmap(ptr, len);
102 :
103 0 : NNTR_THROW_IF(ret == -1, std::runtime_error)
104 : << "SwapDevice: munmap: "
105 0 : << SAFE_STRERROR(errno, error_buf, error_buflen);
106 :
107 : return memory_ptr;
108 : } else {
109 0 : size_t off = (offset / page_size) * page_size;
110 0 : size_t diff = offset - off;
111 0 : size_t len = size + diff;
112 : const size_t error_buflen = 100;
113 : char error_buf[error_buflen];
114 : char *ptr = static_cast<char *>(
115 0 : mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, off));
116 0 : NNTR_THROW_IF(ptr == MAP_FAILED, std::runtime_error)
117 0 : << "SwapDevice: mmap: " << SAFE_STRERROR(errno, error_buf, error_buflen);
118 0 : void *buf = static_cast<void *>(ptr + diff);
119 :
120 0 : std::lock_guard<std::mutex> lock(mutex);
121 0 : mapped[buf] = std::make_tuple(ptr, len, offset, (ssize_t)size);
122 :
123 0 : return buf;
124 : }
125 : #else
126 : off_t off;
127 : ssize_t len;
128 : void *ptr;
129 :
130 : ptr = calloc(1, size);
131 : NNTR_THROW_IF(ptr == NULL, std::runtime_error)
132 : << "SwapDevice: memory alloc failed";
133 :
134 : if (!alloc_only) {
135 : off = lseek(fd, offset, SEEK_SET);
136 : NNTR_THROW_IF(off < 0, std::runtime_error)
137 : << "SwapDevice: seek file: " << dev_path;
138 :
139 : len = read(fd, ptr, size);
140 : NNTR_THROW_IF(len != (size_t)size, std::runtime_error)
141 : << "SwapDevice: read file: " << dev_path;
142 : }
143 :
144 : {
145 : std::lock_guard<std::mutex> lock(mutex);
146 : allocated[ptr] = std::make_pair(offset, (ssize_t)size);
147 : }
148 :
149 : return ptr;
150 : #endif
151 : }
152 :
153 0 : void SwapDevice::putBuffer(void *ptr, bool dealloc_only) {
154 0 : NNTR_THROW_IF(fd <= 0, std::runtime_error)
155 : << "SwapDevice: Device is not started";
156 : #ifdef USE_MMAP
157 : decltype(mapped)::mapped_type info;
158 : {
159 0 : std::lock_guard<std::mutex> lock(mutex);
160 : auto it = mapped.find(ptr);
161 0 : NNTR_THROW_IF(it == mapped.end(), std::runtime_error)
162 : << "Couldn't find buffer";
163 : info = it->second;
164 : }
165 :
166 : off_t off;
167 : ssize_t len;
168 0 : if (!dealloc_only) {
169 0 : off = lseek(fd, std::get<2>(info), SEEK_SET);
170 0 : NNTR_THROW_IF(off < 0, std::runtime_error)
171 : << "SwapDevice: seek file: " << dev_path;
172 :
173 : ssize_t size = std::get<3>(info);
174 0 : len = write(fd, ptr, size);
175 0 : NNTR_THROW_IF(len != size, std::runtime_error)
176 0 : << "SwapDevice: write file: " << len << "::" << std::to_string(size)
177 : << dev_path;
178 : }
179 :
180 0 : const auto ret = munmap(std::get<void *>(info), std::get<size_t>(info));
181 : const size_t error_buflen = 100;
182 : char error_buf[error_buflen];
183 0 : NNTR_THROW_IF(ret == -1, std::runtime_error)
184 0 : << "SwapDevice: munmap: " << SAFE_STRERROR(errno, error_buf, error_buflen);
185 :
186 : {
187 : std::lock_guard<std::mutex> lock(mutex);
188 : mapped.erase(ptr);
189 : }
190 :
191 : #if !defined(__ANDROID__) && !defined(_WIN32)
192 0 : madvise(std::get<void *>(info), std::get<size_t>(info), MADV_FREE);
193 : #endif
194 :
195 : #else
196 : decltype(allocated)::mapped_type info;
197 : {
198 : std::lock_guard<std::mutex> lock(mutex);
199 : auto it = allocated.find(ptr);
200 : NNTR_THROW_IF(it == allocated.end(), std::invalid_argument)
201 : << "SwapDevice: Couldn't find buffer";
202 : info = it->second;
203 : }
204 : off_t off;
205 : ssize_t len;
206 :
207 : auto [offset, size] = info;
208 :
209 : if (!dealloc_only) {
210 : off = lseek(fd, offset, SEEK_SET);
211 : NNTR_THROW_IF(off < 0, std::runtime_error)
212 : << "SwapDevice: seek file: " << dev_path;
213 :
214 : len = write(fd, ptr, size);
215 : NNTR_THROW_IF(len != size, std::runtime_error)
216 : << "SwapDevice: write file: " << dev_path;
217 : }
218 :
219 : free(ptr);
220 : {
221 : std::lock_guard<std::mutex> lock(mutex);
222 : allocated.erase(ptr);
223 : }
224 :
225 : #if !defined(__ANDROID__) && !defined(_WIN32)
226 : malloc_trim(0);
227 : #endif
228 :
229 : #endif
230 0 : }
231 :
232 : /**
233 : * @brief Close device
234 : *
235 : */
236 0 : void SwapDevice::finish() {
237 0 : if (fd < 0)
238 : return;
239 :
240 : #ifdef USE_MMAP
241 0 : if (execution_mode == ml::train::ExecutionMode::TRAIN) {
242 0 : for (auto &[ptr, info] : mapped) {
243 0 : if (ptr)
244 0 : free(ptr);
245 : }
246 : }
247 : mapped.clear();
248 : #else
249 : for (auto &alloc : allocated)
250 : free(alloc.first);
251 : allocated.clear();
252 : #endif
253 :
254 0 : close(fd);
255 0 : fd = -1;
256 0 : if (execution_mode == ml::train::ExecutionMode::TRAIN) {
257 0 : int status = std::remove(dev_path.c_str());
258 0 : NNTR_THROW_IF(status, std::runtime_error)
259 : << "SwapDevice: Couldn't remove " << dev_path.c_str();
260 : }
261 : }
262 :
263 : } // namespace nntrainer
|