Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
serialize.hpp
Go to the documentation of this file.
1// -------------------------------------------------------//
2//
3// SHAMROCK code for hydrodynamics
4// Copyright (c) 2021-2026 Timothée David--Cléris <tim.shamrock@proton.me>
5// SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1
6// Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information
7//
8// -------------------------------------------------------//
9
10#pragma once
11
19#include "shambase/assert.hpp"
21#include "shambase/memory.hpp"
27#include "shambackends/sycl.hpp"
29#include <type_traits>
30#include <memory>
31#include <stdexcept>
32#include <string>
33#include <utility>
34
35namespace shamalgs {
36
38 u64 head_size = 0;
39 u64 content_size = 0;
40
41 SerializeSize &operator+=(
42 const SerializeSize &rhs) // compound assignment (does not need to be a member,
43 { // but often is, to modify the private members)
44 /* addition of rhs to *this takes place here */
45 head_size += rhs.head_size;
46 content_size += rhs.content_size;
47 return *this; // return the result by reference
48 }
49
50 // friends defined inside class body are inline and are hidden from non-ADL lookup
51 friend SerializeSize operator+(
52 SerializeSize lhs, // passing lhs by value helps optimize chained a+b+c
53 const SerializeSize &rhs) // otherwise, both parameters may be const references
54 {
55 lhs += rhs; // reuse compound assignment
56 return lhs; // return the result by value (uses move constructor)
57 }
58 SerializeSize &operator*=(
59 const SerializeSize &rhs) // compound assignment (does not need to be a member,
60 { // but often is, to modify the private members)
61 /* addition of rhs to *this takes place here */
62 head_size *= rhs.head_size;
63 content_size *= rhs.content_size;
64 return *this; // return the result by reference
65 }
66
67 // friends defined inside class body are inline and are hidden from non-ADL lookup
68 friend SerializeSize operator*(
69 SerializeSize lhs, // passing lhs by value helps optimize chained a*b*c
70 const SerializeSize &rhs) // otherwise, both parameters may be const references
71 {
72 lhs *= rhs; // reuse compound assignment
73 return lhs; // return the result by value (uses move constructor)
74 }
75
76 SerializeSize &operator*=(
77 const int &rhs) // compound assignment (does not need to be a member,
78 { // but often is, to modify the private members)
79 /* addition of rhs to *this takes place here */
80 head_size *= rhs;
81 content_size *= rhs;
82 return *this; // return the result by reference
83 }
84
85 // friends defined inside class body are inline and are hidden from non-ADL lookup
86 friend SerializeSize operator*(
87 SerializeSize lhs, // passing lhs by value helps optimize chained a*b*c
88 const int &rhs) // otherwise, both parameters may be const references
89 {
90 lhs *= rhs; // reuse compound assignment
91 return lhs; // return the result by value (uses move constructor)
92 }
93
94 static SerializeSize Header(u64 sz) { return {sz, 0}; }
95 static SerializeSize Content(u64 sz) { return {0, sz}; }
96
97 inline u64 get_total_size() { return head_size + content_size; }
98 };
99
100 namespace details {
101 template<u64 alignment>
102 inline u64 align_repr(u64 offset) {
103 u64 modval = offset % alignment;
104 if (modval == 0) {
105 return offset;
106 }
107 return offset + (alignment - modval);
108 }
109
110 template<u64 alignment, class T>
111 inline SerializeSize serialize_byte_size() {
112 using Helper = details::SerializeHelperMember<T>;
113 return SerializeSize::Header(align_repr<alignment>(Helper::szrepr));
114 }
115
116 template<u64 alignment, class T>
117 inline SerializeSize serialize_byte_size(u64 len) {
118 using Helper = details::SerializeHelperMember<T>;
119 return SerializeSize::Content(align_repr<alignment>(len * Helper::szrepr));
120 }
121
122 template<u64 alignment>
123 inline SerializeSize serialize_byte_size(std::string s) {
124 return serialize_byte_size<alignment, u32>()
125 + serialize_byte_size<alignment, u32>(s.size());
126 }
127
128 } // namespace details
129
131
132 u64 header_size = 0;
133
135 std::vector<u8> storage_header = {};
136 u64 head_device = 0;
137 u64 head_host = 0;
138
139 static constexpr u64 alignment = 8;
140
141 template<class T>
142 inline void check_head_move_device(u64 off, u64 len) {
144
145 SHAM_ASSERT(off == align_repr(len * Helper::szrepr));
146
147 if (head_device + off > storage.get_size()) {
149 "Serializer device buffer overflow: cannot move device head.\n"
150 " storage size : {}\n"
151 " current head_device : {}\n"
152 " requested head_device : {}\n"
153 " offset : {}\n"
154 " len : {}\n"
155 " Helper::szrepr : {}",
156 storage.get_size(),
157 head_device,
158 head_device + off,
159 off,
160 len,
161 Helper::szrepr));
162 }
163 }
164
165 template<class T>
166 inline void check_head_move_host(u64 off, u64 len) {
168
169 SHAM_ASSERT(off == align_repr(len * Helper::szrepr));
170
171 if (head_host + off > storage_header.size()) {
173 "Serializer host buffer overflow: cannot move host head.\n"
174 " storage_header size : {}\n"
175 " current head_host : {}\n"
176 " requested head_host : {}\n"
177 " offset : {}\n"
178 " len : {}\n"
179 " Helper::szrepr : {}",
180 storage_header.size(),
181 head_host,
182 head_host + off,
183 off,
184 len,
185 Helper::szrepr));
186 }
187 }
188
189 inline static u64 align_repr(u64 offset) { return details::align_repr<alignment>(offset); }
190
191 static u64 pre_head_length();
192
193 std::shared_ptr<sham::DeviceScheduler> dev_sched;
194
195 public:
196 std::shared_ptr<sham::DeviceScheduler> &get_device_scheduler() { return dev_sched; }
197
198 SerializeHelper(std::shared_ptr<sham::DeviceScheduler> dev_sched);
199
201 std::shared_ptr<sham::DeviceScheduler> dev_sched, sham::DeviceBuffer<u8> &&storage);
202
203 void allocate(SerializeSize szinfo);
204
205 sham::DeviceBuffer<u8> finalize();
206
207 template<class T>
208 inline static SerializeSize serialize_byte_size() {
209 return details::serialize_byte_size<alignment, T>();
210 }
211
212 template<class T>
213 inline static SerializeSize serialize_byte_size(u64 len) {
214 return details::serialize_byte_size<alignment, T>(len);
215 }
216
217 inline static SerializeSize serialize_byte_size(std::string s) {
218 return details::serialize_byte_size<alignment>(s);
219 }
220
221 template<class T>
222 inline void write(T val) {
223 StackEntry stack_loc{false};
224
226
227 u64 current_head = head_host;
228
229 u64 offset = align_repr(Helper::szrepr);
230 check_head_move_host<T>(offset, 1);
231
232 Helper::store(&(storage_header)[current_head], val);
233
234 head_host += offset;
235 }
236
237 template<class T>
238 inline void load(T &val) {
239 StackEntry stack_loc{false};
240
242
243 u64 current_head = head_host;
244 u64 offset = align_repr(Helper::szrepr);
245 check_head_move_host<T>(offset, 1);
246
247 { // using host_acc rather than anything else since other options causes addition
248 // latency
249
250 val = Helper::load(&(storage_header)[current_head]);
251 }
252
253 head_host += offset;
254 }
255
256 inline void write(std::string s) {
257 StackEntry stack_loc{false};
258 write(u32(s.size()));
259
260 sycl::buffer<char> buf(s.size());
261 {
262 sycl::host_accessor acc{buf, sycl::write_only, sycl::no_init};
263 for (u32 i = 0; i < s.size(); i++) {
264 acc[i] = s[i];
265 }
266 }
267 write_buf(buf, s.size());
268 }
269
270 inline void load(std::string &s) {
271 StackEntry stack_loc{false};
272 u32 len;
273 load(len);
274 s.resize(len);
275
276 sycl::buffer<char> buf(len);
277 load_buf(buf, len);
278 {
279 sycl::host_accessor acc{buf, sycl::read_only};
280 for (u32 i = 0; i < len; i++) {
281 s[i] = acc[i];
282 }
283 }
284 }
285
286 template<class T>
287 inline void write_buf(sycl::buffer<T> &buf, u64 len) {
288 StackEntry stack_loc{false};
289
291 u64 current_head = head_device;
292
293 u64 offset = align_repr(len * Helper::szrepr);
294 check_head_move_device<T>(offset, len);
295
296 sham::EventList depends_list;
297
298 auto accbufbyte = storage.get_write_access(depends_list);
299
300 auto e = dev_sched->get_queue().submit(
301 depends_list, [&, current_head](sycl::handler &cgh) {
302 sycl::accessor accbuf{buf, cgh, sycl::read_only};
303
304 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
305 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
306 Helper::store(&accbufbyte[head], accbuf[id]);
307 });
308 });
309
310 storage.complete_event_state(e);
311
312 head_device += offset;
313 }
314
315 template<class T>
316 inline void load_buf(sycl::buffer<T> &buf, u64 len) {
317 StackEntry stack_loc{false};
318
320 u64 current_head = head_device;
321
322 u64 offset = align_repr(len * Helper::szrepr);
323 check_head_move_device<T>(offset, len);
324
325 sham::EventList depends_list;
326
327 auto accbufbyte = storage.get_read_access(depends_list);
328
329 auto e = dev_sched->get_queue().submit(
330 depends_list, [&, current_head](sycl::handler &cgh) {
331 sycl::accessor accbuf{buf, cgh, sycl::write_only, sycl::no_init};
332
333 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
334 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
335 accbuf[id] = Helper::load(&accbufbyte[head]);
336 });
337 });
338
339 storage.complete_event_state(e);
340
341 head_device += offset;
342 }
343
344 template<class T>
345 inline void write_buf(sham::DeviceBuffer<T> &buf, u64 len) {
346 StackEntry stack_loc{false};
347
349 u64 current_head = head_device;
350
351 u64 offset = align_repr(len * Helper::szrepr);
352 check_head_move_device<T>(offset, len);
353
354 sham::EventList depends_list;
355 const T *accbuf = buf.get_read_access(depends_list);
356 auto accbufbyte = storage.get_write_access(depends_list);
357
358 auto e = dev_sched->get_queue().submit(
359 depends_list, [&, current_head](sycl::handler &cgh) {
360 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
361 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
362 Helper::store(&accbufbyte[head], accbuf[id]);
363 });
364 });
365
366 buf.complete_event_state(e);
367 storage.complete_event_state(e);
368
369 head_device += offset;
370 }
371
372 template<class T>
373 inline void load_buf(sham::DeviceBuffer<T> &buf, u64 len) {
374 StackEntry stack_loc{false};
375
377 u64 current_head = head_device;
378
379 u64 offset = align_repr(len * Helper::szrepr);
380 check_head_move_device<T>(offset, len);
381
382 if (buf.get_size() < len) {
384 "SerializeHelper::load_buf: (buf.get_size() < len)\n buf.get_size()={}\n "
385 "len={}",
386 buf.get_size(),
387 len));
388 }
389
390 sham::EventList depends_list;
391 T *accbuf = buf.get_write_access(depends_list);
392 auto accbufbyte = storage.get_read_access(depends_list);
393
394 auto e = dev_sched->get_queue().submit(
395 depends_list, [&, current_head](sycl::handler &cgh) {
396 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
397 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
398 accbuf[id] = Helper::load(&accbufbyte[head]);
399 });
400 });
401
402 buf.complete_event_state(e);
403 storage.complete_event_state(e);
404
405 head_device += offset;
406 }
407 };
408
409} // namespace shamalgs
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
Shamrock assertion utility.
#define SHAM_ASSERT(x)
Shorthand for SHAM_ASSERT_NAMED without a message.
Definition assert.hpp:67
A buffer allocated in USM (Unified Shared Memory)
void complete_event_state(sycl::event e) const
Complete the event state of the buffer.
T * get_write_access(sham::EventList &depends_list, SourceLocation src_loc=SourceLocation{})
Get a read-write pointer to the buffer's data.
size_t get_size() const
Gets the number of elements in the buffer.
const T * get_read_access(sham::EventList &depends_list, SourceLocation src_loc=SourceLocation{}) const
Get a read-only pointer to the buffer's data.
Class to manage a list of SYCL events.
Definition EventList.hpp:31
This header file contains utility functions related to exception handling in the code.
Namespace for internal details of the logs module.
namespace to contain everything implemented by shamalgs
Definition algorithm.hpp:21
void throw_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Throw an exception and append the source location to it.
This file contains the definition for the stacktrace related functionality.