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
18
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 {.head_size = sz, .content_size = 0}; }
95 static SerializeSize Content(u64 sz) { return {.head_size = 0, .content_size = 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
130 class SerializeHelper {
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
200 SerializeHelper(
201 std::shared_ptr<sham::DeviceScheduler> dev_sched,
202 sham::DeviceBuffer<u8> &&storage,
203 bool allow_large_int_size = false);
204
205 void allocate(SerializeSize szinfo, bool allow_large_int_size = false);
206
207 sham::DeviceBuffer<u8> finalize();
208
209 template<class T>
210 inline static SerializeSize serialize_byte_size() {
211 return details::serialize_byte_size<alignment, T>();
212 }
213
214 template<class T>
215 inline static SerializeSize serialize_byte_size(u64 len) {
216 return details::serialize_byte_size<alignment, T>(len);
217 }
218
219 inline static SerializeSize serialize_byte_size(std::string s) {
220 return details::serialize_byte_size<alignment>(s);
221 }
222
223 template<class T>
224 inline void write(T val) {
225 StackEntry stack_loc{false};
226
228
229 u64 current_head = head_host;
230
231 u64 offset = align_repr(Helper::szrepr);
232 check_head_move_host<T>(offset, 1);
233
234 Helper::store(&(storage_header)[current_head], val);
235
236 head_host += offset;
237 }
238
239 template<class T>
240 inline void load(T &val) {
241 StackEntry stack_loc{false};
242
244
245 u64 current_head = head_host;
246 u64 offset = align_repr(Helper::szrepr);
247 check_head_move_host<T>(offset, 1);
248
249 { // using host_acc rather than anything else since other options causes addition
250 // latency
251
252 val = Helper::load(&(storage_header)[current_head]);
253 }
254
255 head_host += offset;
256 }
257
258 inline void write(std::string s) {
259 StackEntry stack_loc{false};
260 write(u32(s.size()));
261
262 sycl::buffer<char> buf(s.size());
263 {
264 sycl::host_accessor acc{buf, sycl::write_only, sycl::no_init};
265 for (u32 i = 0; i < s.size(); i++) {
266 acc[i] = s[i];
267 }
268 }
269 write_buf(buf, s.size());
270 }
271
272 inline void load(std::string &s) {
273 StackEntry stack_loc{false};
274 u32 len;
275 load(len);
276 s.resize(len);
277
278 sycl::buffer<char> buf(len);
279 load_buf(buf, len);
280 {
281 sycl::host_accessor acc{buf, sycl::read_only};
282 for (u32 i = 0; i < len; i++) {
283 s[i] = acc[i];
284 }
285 }
286 }
287
288 template<class T>
289 inline void write_buf(sycl::buffer<T> &buf, u64 len) {
290 StackEntry stack_loc{false};
291
293 u64 current_head = head_device;
294
295 u64 offset = align_repr(len * Helper::szrepr);
296 check_head_move_device<T>(offset, len);
297
298 sham::EventList depends_list;
299
300 auto accbufbyte = storage.get_write_access(depends_list);
301
302 auto e = dev_sched->get_queue().submit(
303 depends_list, [&, current_head](sycl::handler &cgh) {
304 sycl::accessor accbuf{buf, cgh, sycl::read_only};
305
306 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
307 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
308 Helper::store(&accbufbyte[head], accbuf[id]);
309 });
310 });
311
312 storage.complete_event_state(e);
313
314 head_device += offset;
315 }
316
317 template<class T>
318 inline void load_buf(sycl::buffer<T> &buf, u64 len) {
319 StackEntry stack_loc{false};
320
322 u64 current_head = head_device;
323
324 u64 offset = align_repr(len * Helper::szrepr);
325 check_head_move_device<T>(offset, len);
326
327 sham::EventList depends_list;
328
329 auto accbufbyte = storage.get_read_access(depends_list);
330
331 auto e = dev_sched->get_queue().submit(
332 depends_list, [&, current_head](sycl::handler &cgh) {
333 sycl::accessor accbuf{buf, cgh, sycl::write_only, sycl::no_init};
334
335 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
336 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
337 accbuf[id] = Helper::load(&accbufbyte[head]);
338 });
339 });
340
341 storage.complete_event_state(e);
342
343 head_device += offset;
344 }
345
346 template<class T>
347 inline void write_buf(sham::DeviceBuffer<T> &buf, u64 len) {
348 StackEntry stack_loc{false};
349
351 u64 current_head = head_device;
352
353 u64 offset = align_repr(len * Helper::szrepr);
354 check_head_move_device<T>(offset, len);
355
356 sham::EventList depends_list;
357 const T *accbuf = buf.get_read_access(depends_list);
358 auto accbufbyte = storage.get_write_access(depends_list);
359
360 auto e = dev_sched->get_queue().submit(
361 depends_list, [&, current_head](sycl::handler &cgh) {
362 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
363 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
364 Helper::store(&accbufbyte[head], accbuf[id]);
365 });
366 });
367
368 buf.complete_event_state(e);
369 storage.complete_event_state(e);
370
371 head_device += offset;
372 }
373
374 template<class T>
375 inline void load_buf(sham::DeviceBuffer<T> &buf, u64 len) {
376 StackEntry stack_loc{false};
377
379 u64 current_head = head_device;
380
381 u64 offset = align_repr(len * Helper::szrepr);
382 check_head_move_device<T>(offset, len);
383
384 if (buf.get_size() < len) {
386 "SerializeHelper::load_buf: (buf.get_size() < len)\n buf.get_size()={}\n "
387 "len={}",
388 buf.get_size(),
389 len));
390 }
391
392 sham::EventList depends_list;
393 T *accbuf = buf.get_write_access(depends_list);
394 auto accbufbyte = storage.get_read_access(depends_list);
395
396 auto e = dev_sched->get_queue().submit(
397 depends_list, [&, current_head](sycl::handler &cgh) {
398 cgh.parallel_for(sycl::range<1>{len}, [=](sycl::item<1> id) {
399 u64 head = current_head + id.get_linear_id() * Helper::szrepr;
400 accbuf[id] = Helper::load(&accbufbyte[head]);
401 });
402 });
403
404 buf.complete_event_state(e);
405 storage.complete_event_state(e);
406
407 head_device += offset;
408 }
409 };
410
411} // 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).
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.
ExcptTypes make_except_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Create an exception with a message and a location.
This file contains the definition for the stacktrace related functionality.
shambase::details::BasicStackEntry StackEntry
Alias for shambase::details::BasicStackEntry.