contrib/faiss/faiss/impl/mapped_io.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include <stdio.h> |
9 | | #include <string.h> |
10 | | |
11 | | #ifdef __linux__ |
12 | | |
13 | | #include <fcntl.h> |
14 | | #include <sys/mman.h> |
15 | | #include <sys/stat.h> |
16 | | #include <sys/types.h> |
17 | | #include <unistd.h> |
18 | | |
19 | | #elif defined(_WIN32) |
20 | | |
21 | | #include <Windows.h> // @manual |
22 | | #include <io.h> // @manual |
23 | | |
24 | | #endif |
25 | | |
26 | | #include <cstring> |
27 | | |
28 | | #include <faiss/impl/FaissAssert.h> |
29 | | #include <faiss/impl/mapped_io.h> |
30 | | |
31 | | namespace faiss { |
32 | | |
33 | | #ifdef __linux__ |
34 | | |
35 | | struct MmappedFileMappingOwner::PImpl { |
36 | | void* ptr = nullptr; |
37 | | size_t ptr_size = 0; |
38 | | |
39 | 0 | PImpl(const std::string& filename) { |
40 | 0 | auto f = std::unique_ptr<FILE, decltype(&fclose)>( |
41 | 0 | fopen(filename.c_str(), "r"), &fclose); |
42 | 0 | FAISS_THROW_IF_NOT_FMT( |
43 | 0 | f.get(), |
44 | 0 | "could not open %s for reading: %s", |
45 | 0 | filename.c_str(), |
46 | 0 | strerror(errno)); |
47 | | |
48 | | // get the size |
49 | 0 | struct stat s; |
50 | 0 | int status = fstat(fileno(f.get()), &s); |
51 | 0 | FAISS_THROW_IF_NOT_FMT( |
52 | 0 | status >= 0, "fstat() failed: %s", strerror(errno)); |
53 | | |
54 | 0 | const size_t filesize = s.st_size; |
55 | |
|
56 | 0 | void* address = mmap( |
57 | 0 | nullptr, filesize, PROT_READ, MAP_SHARED, fileno(f.get()), 0); |
58 | 0 | FAISS_THROW_IF_NOT_FMT( |
59 | 0 | address != nullptr, "could not mmap(): %s", strerror(errno)); |
60 | | |
61 | | // btw, fd can be closed here |
62 | | |
63 | 0 | madvise(address, filesize, MADV_RANDOM); |
64 | | |
65 | | // save it |
66 | 0 | ptr = address; |
67 | 0 | ptr_size = filesize; |
68 | 0 | } |
69 | | |
70 | 0 | PImpl(FILE* f) { |
71 | | // get the size |
72 | 0 | struct stat s; |
73 | 0 | int status = fstat(fileno(f), &s); |
74 | 0 | FAISS_THROW_IF_NOT_FMT( |
75 | 0 | status >= 0, "fstat() failed: %s", strerror(errno)); |
76 | | |
77 | 0 | const size_t filesize = s.st_size; |
78 | |
|
79 | 0 | void* address = |
80 | 0 | mmap(nullptr, filesize, PROT_READ, MAP_SHARED, fileno(f), 0); |
81 | 0 | FAISS_THROW_IF_NOT_FMT( |
82 | 0 | address != nullptr, "could not mmap(): %s", strerror(errno)); |
83 | | |
84 | | // btw, fd can be closed here |
85 | | |
86 | 0 | madvise(address, filesize, MADV_RANDOM); |
87 | | |
88 | | // save it |
89 | 0 | ptr = address; |
90 | 0 | ptr_size = filesize; |
91 | 0 | } |
92 | | |
93 | 0 | ~PImpl() { |
94 | | // todo: check for an error |
95 | 0 | munmap(ptr, ptr_size); |
96 | 0 | } |
97 | | }; |
98 | | |
99 | | #elif defined(_WIN32) |
100 | | |
101 | | struct MmappedFileMappingOwner::PImpl { |
102 | | void* ptr = nullptr; |
103 | | size_t ptr_size = 0; |
104 | | HANDLE mapping_handle = INVALID_HANDLE_VALUE; |
105 | | |
106 | | PImpl(const std::string& filename) { |
107 | | HANDLE file_handle = CreateFile( |
108 | | filename.c_str(), |
109 | | GENERIC_READ, |
110 | | FILE_SHARE_READ, |
111 | | nullptr, |
112 | | OPEN_EXISTING, |
113 | | 0, |
114 | | nullptr); |
115 | | if (file_handle == INVALID_HANDLE_VALUE) { |
116 | | const auto error = GetLastError(); |
117 | | FAISS_THROW_FMT( |
118 | | "could not open the file, %s (error %d)", |
119 | | filename.c_str(), |
120 | | error); |
121 | | } |
122 | | |
123 | | // get the size of the file |
124 | | LARGE_INTEGER len_li; |
125 | | if (GetFileSizeEx(file_handle, &len_li) == 0) { |
126 | | const auto error = GetLastError(); |
127 | | |
128 | | CloseHandle(file_handle); |
129 | | |
130 | | FAISS_THROW_FMT( |
131 | | "could not get the file size, %s (error %d)", |
132 | | filename.c_str(), |
133 | | error); |
134 | | } |
135 | | |
136 | | // create a mapping |
137 | | mapping_handle = CreateFileMapping( |
138 | | file_handle, nullptr, PAGE_READONLY, 0, 0, nullptr); |
139 | | if (mapping_handle == 0) { |
140 | | const auto error = GetLastError(); |
141 | | |
142 | | CloseHandle(file_handle); |
143 | | |
144 | | FAISS_THROW_FMT( |
145 | | "could not create a file mapping, %s (error %d)", |
146 | | filename.c_str(), |
147 | | error); |
148 | | } |
149 | | CloseHandle(file_handle); |
150 | | |
151 | | char* data = |
152 | | (char*)MapViewOfFile(mapping_handle, FILE_MAP_READ, 0, 0, 0); |
153 | | if (data == nullptr) { |
154 | | const auto error = GetLastError(); |
155 | | |
156 | | CloseHandle(mapping_handle); |
157 | | mapping_handle = INVALID_HANDLE_VALUE; |
158 | | |
159 | | FAISS_THROW_FMT( |
160 | | "could not get map the file, %s (error %d)", |
161 | | filename.c_str(), |
162 | | error); |
163 | | } |
164 | | |
165 | | ptr = data; |
166 | | ptr_size = len_li.QuadPart; |
167 | | } |
168 | | |
169 | | PImpl(FILE* f) { |
170 | | // obtain a HANDLE from a FILE |
171 | | const int fd = _fileno(f); |
172 | | if (fd == -1) { |
173 | | // no good |
174 | | FAISS_THROW_FMT("could not get a HANDLE"); |
175 | | } |
176 | | |
177 | | HANDLE file_handle = (HANDLE)_get_osfhandle(fd); |
178 | | if (file_handle == INVALID_HANDLE_VALUE) { |
179 | | FAISS_THROW_FMT("could not get an OS HANDLE"); |
180 | | } |
181 | | |
182 | | // get the size of the file |
183 | | LARGE_INTEGER len_li; |
184 | | if (GetFileSizeEx(file_handle, &len_li) == 0) { |
185 | | const auto error = GetLastError(); |
186 | | FAISS_THROW_FMT("could not get the file size (error %d)", error); |
187 | | } |
188 | | |
189 | | // create a mapping |
190 | | mapping_handle = CreateFileMapping( |
191 | | file_handle, nullptr, PAGE_READONLY, 0, 0, nullptr); |
192 | | if (mapping_handle == 0) { |
193 | | const auto error = GetLastError(); |
194 | | FAISS_THROW_FMT( |
195 | | "could not create a file mapping, (error %d)", error); |
196 | | } |
197 | | |
198 | | // the handle is provided externally, so this is not our business |
199 | | // to close file_handle. |
200 | | |
201 | | char* data = |
202 | | (char*)MapViewOfFile(mapping_handle, FILE_MAP_READ, 0, 0, 0); |
203 | | if (data == nullptr) { |
204 | | const auto error = GetLastError(); |
205 | | |
206 | | CloseHandle(mapping_handle); |
207 | | mapping_handle = INVALID_HANDLE_VALUE; |
208 | | |
209 | | FAISS_THROW_FMT("could not get map the file, (error %d)", error); |
210 | | } |
211 | | |
212 | | ptr = data; |
213 | | ptr_size = len_li.QuadPart; |
214 | | } |
215 | | |
216 | | ~PImpl() { |
217 | | if (mapping_handle != INVALID_HANDLE_VALUE) { |
218 | | UnmapViewOfFile(ptr); |
219 | | CloseHandle(mapping_handle); |
220 | | |
221 | | mapping_handle = INVALID_HANDLE_VALUE; |
222 | | ptr = nullptr; |
223 | | } |
224 | | } |
225 | | }; |
226 | | |
227 | | #else |
228 | | |
229 | | struct MmappedFileMappingOwner::PImpl { |
230 | | void* ptr = nullptr; |
231 | | size_t ptr_size = 0; |
232 | | |
233 | | PImpl(const std::string& filename) { |
234 | | FAISS_THROW_MSG("Not implemented"); |
235 | | } |
236 | | |
237 | | PImpl(FILE* f) { |
238 | | FAISS_THROW_MSG("Not implemented"); |
239 | | } |
240 | | }; |
241 | | |
242 | | #endif |
243 | | |
244 | 0 | MmappedFileMappingOwner::MmappedFileMappingOwner(const std::string& filename) { |
245 | 0 | p_impl = std::make_unique<MmappedFileMappingOwner::PImpl>(filename); |
246 | 0 | } |
247 | | |
248 | 0 | MmappedFileMappingOwner::MmappedFileMappingOwner(FILE* f) { |
249 | 0 | p_impl = std::make_unique<MmappedFileMappingOwner::PImpl>(f); |
250 | 0 | } |
251 | | |
252 | 0 | MmappedFileMappingOwner::~MmappedFileMappingOwner() = default; |
253 | | |
254 | | // |
255 | 0 | void* MmappedFileMappingOwner::data() const { |
256 | 0 | return p_impl->ptr; |
257 | 0 | } |
258 | | |
259 | 0 | size_t MmappedFileMappingOwner::size() const { |
260 | 0 | return p_impl->ptr_size; |
261 | 0 | } |
262 | | |
263 | | MappedFileIOReader::MappedFileIOReader( |
264 | | const std::shared_ptr<MmappedFileMappingOwner>& owner) |
265 | 0 | : mmap_owner(owner) {} |
266 | | |
267 | | // this operation performs a copy |
268 | 0 | size_t MappedFileIOReader::operator()(void* ptr, size_t size, size_t nitems) { |
269 | 0 | if (size * nitems == 0) { |
270 | 0 | return 0; |
271 | 0 | } |
272 | | |
273 | 0 | char* ptr_c = nullptr; |
274 | |
|
275 | 0 | const size_t actual_nitems = this->mmap((void**)&ptr_c, size, nitems); |
276 | 0 | if (actual_nitems > 0) { |
277 | 0 | memcpy(ptr, ptr_c, size * actual_nitems); |
278 | 0 | } |
279 | |
|
280 | 0 | return actual_nitems; |
281 | 0 | } |
282 | | |
283 | | // this operation returns a mmapped address, owned by mmap_owner |
284 | 0 | size_t MappedFileIOReader::mmap(void** ptr, size_t size, size_t nitems) { |
285 | 0 | if (size == 0) { |
286 | 0 | return nitems; |
287 | 0 | } |
288 | | |
289 | 0 | size_t actual_size = size * nitems; |
290 | 0 | if (pos + size * nitems > mmap_owner->size()) { |
291 | 0 | actual_size = mmap_owner->size() - pos; |
292 | 0 | } |
293 | |
|
294 | 0 | size_t actual_nitems = (actual_size + size - 1) / size; |
295 | 0 | if (actual_nitems == 0) { |
296 | 0 | return 0; |
297 | 0 | } |
298 | | |
299 | | // get an address |
300 | 0 | *ptr = (void*)(reinterpret_cast<const char*>(mmap_owner->data()) + pos); |
301 | | |
302 | | // alter pos |
303 | 0 | pos += size * actual_nitems; |
304 | |
|
305 | 0 | return actual_nitems; |
306 | 0 | } |
307 | | |
308 | 0 | int MappedFileIOReader::filedescriptor() { |
309 | | // todo |
310 | 0 | return -1; |
311 | 0 | } |
312 | | |
313 | | } // namespace faiss |