OpenLB 1.7
Loading...
Searching...
No Matches
column.h
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2021 Adrian Kummerlaender
4 *
5 * E-mail contact: info@openlb.net
6 * The most recent release of OpenLB can be downloaded at
7 * <http://www.openlb.net/>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23*/
24
25#ifndef CPU_SIMD_COLUMN_H
26#define CPU_SIMD_COLUMN_H
27
28#include <memory>
29#include <array>
30#include <cstring>
31#include <stdexcept>
32
33#include <unistd.h>
34#include <fcntl.h>
35#include <sys/mman.h>
36#include <asm/unistd_64.h>
37
39#include "core/serializer.h"
41
42#include "pack.h"
43
44namespace olb {
45
46namespace cpu {
47
48namespace simd {
49
51template<typename T>
52class Column final : public AbstractColumn<T>
53 , public Serializable {
54private:
55 std::size_t _count;
56 std::unique_ptr<T[]> _data;
57
58public:
59 using value_t = T;
60
61 Column(std::size_t count):
62 _count(count),
63 _data(new T[count] {})
64 { }
65
67 Column(0)
68 { }
69
71 _count(rhs._count),
72 _data(rhs._data.release())
73 { }
74
75 Column(const Column<T>& rhs):
76 _count(rhs._count),
77 _data(new T[_count] {})
78 {
79 std::copy(rhs._data.get(),
80 rhs._data.get() + _count,
81 _data.get());
82 }
83
84 virtual ~Column() = default;
85
86 void resize(std::size_t count)
87 {
88 std::unique_ptr<T[]> data = std::unique_ptr<T[]>(new T[count] { });
89 std::copy(_data.get(), _data.get() + std::min(_count, count), data.get());
90 _data.swap(data);
91 _count = count;
92 }
93
94 const T& operator[](std::size_t i) const override
95 {
96 return _data[i];
97 }
98
99 T& operator[](std::size_t i) override
100 {
101 return _data[i];
102 }
103
104 std::size_t size() const
105 {
106 return _count;
107 }
108
109 const T* data() const
110 {
111 return _data.get();
112 }
113
114 T* data()
115 {
116 return _data.get();
117 }
118
120
122 std::size_t getNblock() const override;
124 std::size_t getSerializableSize() const override;
126 bool* getBlock(std::size_t iBlock, std::size_t& sizeBlock, bool loadingMode) override;
127
128};
129
130
131const int PROT_RW = PROT_READ | PROT_WRITE;
132
133template <typename T>
134std::size_t getPageAlignedCount(std::size_t count)
135{
136 const std::size_t page_size = sysconf(_SC_PAGESIZE);
137 const std::size_t size = ((count * sizeof(T) - 1) / page_size + 1) * page_size;
138 const std::size_t volume = size / sizeof(T);
139
140 if (size % page_size != 0) {
141 throw std::invalid_argument("Buffer size must be multiple of PAGE_SIZE");
142 }
143
144 return volume;
145};
146
148
152template<typename T>
153class CyclicColumn final : public AbstractCyclicColumn<T>
154 , public Serializable {
155private:
156 const std::ptrdiff_t _count;
157 const std::size_t _size;
158
159 std::uint8_t* _buffer;
160
161 T* _base;
162 T* _f;
163
164 std::ptrdiff_t _shift;
165
166public:
167 using value_t = T;
168
169 CyclicColumn(std::size_t count):
170 _count(getPageAlignedCount<T>(count)),
171 _size(_count * sizeof(T)),
172 _shift(0)
173 {
174 #ifdef __NR_memfd_create
175 // Open anonymous file for physical lattice memory
176 // Manual call of "memfd_create("openlb", MFD_CLOEXEC)" in case GLIB is old
177 const int shm_file = syscall(__NR_memfd_create, "openlb", MFD_CLOEXEC);
178 #else
179 std::string shm_path = "/openlb_block_XXXXXX";
180 const int shm_name = mkstemp(const_cast<char*>(shm_path.data()));
181 if (shm_name != -1) {
182 throw std::runtime_error("Could not generate unique shared memory object name");
183 }
184 // Open shared memory object as physical lattice memory
185 const int shm_file = shm_open(shm_path.c_str(), O_CREAT | O_RDWR | O_EXCL | O_CLOEXEC, S_IRUSR | S_IWUSR);
186 shm_unlink(shm_path.c_str());
187 #endif
188 if (shm_file == -1) {
189 throw std::runtime_error("Failed to create shared memory object");
190 }
191
192 // Resize to fit lattice populations
193 if (ftruncate(shm_file, _size) == -1) {
194 throw std::runtime_error("Failed to resize shared memory object");
195 }
196
197 // Allocate virtual address space for q times two consecutive lattices
198 _buffer = static_cast<std::uint8_t*>(
199 mmap(NULL, 2 * _size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
200
201 // Map single physical lattice into virtual address space
202 mmap(_buffer, _size, PROT_RW, MAP_SHARED | MAP_FIXED, shm_file, 0);
203 mmap(_buffer + _size, _size, PROT_RW, MAP_SHARED | MAP_FIXED, shm_file, 0);
204
205 // Store base pointer for reference
206 _base = reinterpret_cast<T*>(_buffer);
207 // Initialize shiftable f pointer to be used for lattice access
208 _f = _base;
209 }
210
212 munmap(_buffer, 2 * _size);
213 }
214
215 const T& operator[](std::size_t i) const override
216 {
217 return _f[i];
218 }
219
220 T& operator[](std::size_t i) override
221 {
222 return _f[i];
223 }
224
225 std::size_t size() const
226 {
227 return _count;
228 }
229
230 void refresh()
231 {
232 _f = _base + _shift;
233 }
234
235 void rotate(std::ptrdiff_t offset)
236 {
237 _shift -= offset;
238 if (_shift >= _count) {
239 _shift -= _count;
240 }
241 else if (_shift < 0) {
242 _shift += _count;
243 }
244 refresh();
245 }
246
247 void resize(std::size_t count)
248 {
249 throw std::logic_error("Cyclic column can not be resized");
250 }
251
253
255 std::size_t getNblock() const override;
257 std::size_t getSerializableSize() const override;
259 bool* getBlock(std::size_t iBlock, std::size_t& sizeBlock, bool loadingMode) override;
260 void postLoad() override { refresh(); }
261
262};
263
264}
265
266}
267
269template <typename T>
273
275template <typename T>
279
280
281template <typename T>
282class ConcreteCommunicatable<cpu::simd::CyclicColumn<T>> final : public Communicatable {
283private:
285
286public:
288 _column{column} { }
289
291 std::size_t size(ConstSpan<CellID> indices) const
292 {
293 return indices.size() * sizeof(T);
294 }
295
297 std::size_t serialize(ConstSpan<CellID> indices,
298 std::uint8_t* buffer) const
299 {
300 if (meta::is_aligned<T>(buffer)) {
301 T* target = reinterpret_cast<T*>(buffer);
302 const typename cpu::simd::Pack<T>::index_t* offsets = indices.data();
303
304 for (std::size_t i=0;
307 target += cpu::simd::Pack<T>::size,
308 offsets += cpu::simd::Pack<T>::size) {
309 cpu::simd::store(target, {&_column[0], offsets});
310 }
311 for (std::size_t i=(indices.size() / cpu::simd::Pack<T>::size) * cpu::simd::Pack<T>::size;
312 i < indices.size();
313 ++i) {
314 *(target++) = _column[indices[i]];
315 }
316 } else {
317 std::uint8_t* target = buffer;
318 for (CellID index : indices) {
319 std::memcpy(target, reinterpret_cast<const void*>(&_column[index]), sizeof(T));
320 target += sizeof(T);
321 }
322 }
323 return indices.size() * sizeof(T);
324 }
325
327 std::size_t deserialize(ConstSpan<CellID> indices,
328 const std::uint8_t* buffer)
329 {
330 if (meta::is_aligned<T>(buffer)) {
331 const T* source = reinterpret_cast<const T*>(buffer);
332 const typename cpu::simd::Pack<T>::index_t* offsets = indices.data();
333
334 for (std::size_t i=0;
337 source += cpu::simd::Pack<T>::size,
338 offsets += cpu::simd::Pack<T>::size) {
339 cpu::simd::store(&_column[0], cpu::simd::Pack<T>{source}, offsets);
340 }
341 for (std::size_t i=(indices.size() / cpu::simd::Pack<T>::size) * cpu::simd::Pack<T>::size;
342 i < indices.size();
343 ++i) {
344 _column[indices[i]] = *(source++);
345 }
346 } else {
347 const std::uint8_t* source = buffer;
348 for (CellID index : indices) {
349 std::memcpy(reinterpret_cast<void*>(&_column[index]), source, sizeof(T));
350 source += sizeof(T);
351 }
352 }
353 return indices.size() * sizeof(T);
354 }
355
356};
357
358
359}
360
361#endif
362
363#include "column.hh"
ConcreteCommunicatable(cpu::simd::CyclicColumn< T > &column)
Definition column.h:287
std::size_t serialize(ConstSpan< CellID > indices, std::uint8_t *buffer) const
Serialize data at locations indices to buffer
Definition column.h:297
std::size_t size(ConstSpan< CellID > indices) const
Get serialized size for data at locations indices
Definition column.h:291
std::size_t deserialize(ConstSpan< CellID > indices, const std::uint8_t *buffer)
Deserialize data at locations indices to buffer
Definition column.h:327
const T * data() const
std::size_t size() const
Base class for serializable objects of constant size. For dynamic size use BufferSerializable.
Definition serializer.h:145
Plain column for SIMD CPU targets.
Definition column.h:53
Column(const Column< T > &rhs)
Definition column.h:75
Column(std::size_t count)
Definition column.h:61
Column(Column< T > &&rhs)
Definition column.h:70
void resize(std::size_t count)
Definition column.h:86
virtual ~Column()=default
std::size_t getSerializableSize() const override
Binary size for the serializer.
Definition column.hh:43
const T * data() const
Definition column.h:109
bool * getBlock(std::size_t iBlock, std::size_t &sizeBlock, bool loadingMode) override
Return a pointer to the memory of the current block and its size for the serializable interface.
Definition column.hh:49
std::size_t getNblock() const override
Number of data blocks for the serializable interface.
Definition column.hh:37
std::size_t size() const
Definition column.h:104
void setProcessingContext(ProcessingContext)
Definition column.h:119
T & operator[](std::size_t i) override
Definition column.h:99
const T & operator[](std::size_t i) const override
Definition column.h:94
Virtual memory based cyclic column for usage in ColumnVector.
Definition column.h:154
void postLoad() override
Definition column.h:260
void rotate(std::ptrdiff_t offset)
Definition column.h:235
const T & operator[](std::size_t i) const override
Definition column.h:215
void setProcessingContext(ProcessingContext)
Definition column.h:252
std::size_t size() const
Definition column.h:225
CyclicColumn(std::size_t count)
Definition column.h:169
T & operator[](std::size_t i) override
Definition column.h:220
void resize(std::size_t count)
Definition column.h:247
std::size_t getSerializableSize() const override
Binary size for the serializer.
Definition column.hh:71
bool * getBlock(std::size_t iBlock, std::size_t &sizeBlock, bool loadingMode) override
Return a pointer to the memory of the current block and its size for the serializable interface.
Definition column.hh:77
std::size_t getNblock() const override
Number of data blocks for the serializable interface.
Definition column.hh:65
const int PROT_RW
Definition column.h:131
std::size_t getPageAlignedCount(std::size_t count)
Definition column.h:134
void store(T *target, Pack< T > value)
Top level namespace for all of OpenLB.
std::uint32_t CellID
Type for sequential block-local cell indices.
ProcessingContext
OpenLB processing contexts.
Definition platform.h:55
Platform
OpenLB execution targets.
Definition platform.h:36
@ CPU_SIMD
Basic scalar CPU.
Abstract declarator of Column-like storage.
Definition column.h:34
Abstract declarator of cyclic Column-like storage.
Definition column.h:43
Specializable declarator for concrete implementations of abstract storage types.
Definition column.h:52