Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
MpiSerializeDispatcher.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* MpiSerializeDispatcher.cc (C) 2000-2025 */
9/* */
10/* Serialization message handling with MPI. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/message_passing_mpi/internal/MpiSerializeDispatcher.h"
15
16#include "arccore/message_passing_mpi/internal/MpiAdapter.h"
17#include "arccore/message_passing_mpi/MpiMessagePassingMng.h"
18#include "arccore/message_passing_mpi/internal/MpiLock.h"
19
20#include "arccore/message_passing/Request.h"
21#include "arccore/message_passing/internal/SerializeMessageList.h"
22#include "arccore/message_passing/internal/SubRequestCompletionInfo.h"
23
24#include "arccore/serialize/BasicSerializer.h"
25
26#include "arccore/base/NotImplementedException.h"
27#include "arccore/base/FatalErrorException.h"
28#include "arccore/base/NotSupportedException.h"
29#include "arccore/base/ArgumentException.h"
30#include "arccore/base/PlatformUtils.h"
31#include "arccore/trace/ITraceMng.h"
32
33/*---------------------------------------------------------------------------*/
34/*---------------------------------------------------------------------------*/
35
36namespace Arcane::MessagePassing::Mpi
37{
38
39/*---------------------------------------------------------------------------*/
40/*---------------------------------------------------------------------------*/
41
60template <typename SpanType>
61class SerializeByteConverter
62{
63 public:
64
65 SerializeByteConverter(Span<SpanType> buffer, MPI_Datatype byte_serializer_datatype)
66 : m_buffer(buffer)
67 , m_datatype(byte_serializer_datatype)
68 , m_final_size(-1)
69 {
70 Int64 size = buffer.size();
71 const Int64 align_size = BasicSerializer::paddingSize();
72 if ((size % align_size) != 0)
73 ARCCORE_FATAL("Buffer size '{0}' is not a multiple of '{1}' Invalid size", size, align_size);
74 m_final_size = size / align_size;
75 }
76 SpanType* data() { return m_buffer.data(); }
77 Int64 size() const { return m_final_size; }
78 Int64 messageSize() const { return m_buffer.size() * sizeof(Byte); }
79 Int64 elementSize() const { return BasicSerializer::paddingSize(); }
80 MPI_Datatype datatype() const { return m_datatype; }
81
82 private:
83
84 Span<SpanType> m_buffer;
85 MPI_Datatype m_datatype;
86 Int64 m_final_size;
87};
88
89/*---------------------------------------------------------------------------*/
90/*---------------------------------------------------------------------------*/
91
101: public ISubRequest
102{
103 public:
104
105 SendSerializerSubRequest(MpiSerializeDispatcher* pm, BasicSerializer* buf,
106 MessageRank rank, MessageTag mpi_tag)
107 : m_dispatcher(pm)
108 , m_serialize_buffer(buf)
109 , m_rank(rank)
110 , m_mpi_tag(mpi_tag)
111 {}
112
113 public:
114
116 {
117 if (!m_is_message_sent)
118 sendMessage();
119 return m_send_request;
120 }
121
122 public:
123
124 void sendMessage()
125 {
126 if (m_is_message_sent)
127 ARCCORE_FATAL("Message already sent");
128 bool do_print = m_dispatcher->m_is_trace_serializer;
129 if (do_print) {
130 ITraceMng* tm = m_dispatcher->traceMng();
131 tm->info() << " SendSerializerSubRequest::sendMessage()"
132 << " rank=" << m_rank << " tag=" << m_mpi_tag;
133 }
134 Span<Byte> bytes = m_serialize_buffer->globalBuffer();
135 m_send_request = m_dispatcher->_sendSerializerBytes(bytes, m_rank, m_mpi_tag, false);
136 m_is_message_sent = true;
137 }
138
139 private:
140
141 MpiSerializeDispatcher* m_dispatcher;
142 BasicSerializer* m_serialize_buffer;
143 MessageRank m_rank;
144 MessageTag m_mpi_tag;
145 Request m_send_request;
146 bool m_is_message_sent = false;
147};
148
149/*---------------------------------------------------------------------------*/
150/*---------------------------------------------------------------------------*/
151
153: public ISubRequest
154{
155 public:
156
157 ReceiveSerializerSubRequest(MpiSerializeDispatcher* d, BasicSerializer* buf,
158 MessageTag mpi_tag, Integer action)
159 : m_dispatcher(d)
160 , m_serialize_buffer(buf)
161 , m_mpi_tag(mpi_tag)
162 , m_action(action)
163 {}
164
165 public:
166
167 Request executeOnCompletion(const SubRequestCompletionInfo& completion_info) override
168 {
169 MessageRank rank = completion_info.sourceRank();
170 bool is_trace = m_dispatcher->m_is_trace_serializer;
171 ITraceMng* tm = m_dispatcher->traceMng();
172 if (is_trace) {
173 tm->info() << " ReceiveSerializerSubRequest::executeOnCompletion()"
174 << " rank=" << rank << " wanted_tag=" << m_mpi_tag << " action=" << m_action;
175 }
176 if (m_action == 1) {
177 BasicSerializer* sbuf = m_serialize_buffer;
178 Int64 total_recv_size = sbuf->totalSize();
179
180 if (is_trace) {
181 tm->info() << " ReceiveSerializerSubRequest::executeOnCompletion() total_size=" << total_recv_size
182 << BasicSerializer::SizesPrinter(*m_serialize_buffer);
183 }
184 // If the message is smaller than the buffer, deserialize it simply
185 if (total_recv_size <= m_dispatcher->m_serialize_buffer_size) {
186 sbuf->setFromSizes();
187 return {};
188 }
189
190 sbuf->preallocate(total_recv_size);
191 auto bytes = sbuf->globalBuffer();
192
193 // The new request must use the same source rank as this request
194 // to ensure there is no inconsistency.
195 Request r2 = m_dispatcher->_recvSerializerBytes(bytes, rank, m_mpi_tag, false);
196 ISubRequest* sr = new ReceiveSerializerSubRequest(m_dispatcher, m_serialize_buffer, m_mpi_tag, 2);
197 r2.setSubRequest(makeRef(sr));
198 return r2;
199 }
200 if (m_action == 2) {
201 m_serialize_buffer->setFromSizes();
202 }
203 return {};
204 }
205
206 private:
207
208 MpiSerializeDispatcher* m_dispatcher = nullptr;
209 BasicSerializer* m_serialize_buffer = nullptr;
210 MessageTag m_mpi_tag;
211 Int32 m_action = 0;
212};
213
214/*---------------------------------------------------------------------------*/
215/*---------------------------------------------------------------------------*/
216
217/*---------------------------------------------------------------------------*/
218/*---------------------------------------------------------------------------*/
219
220MpiSerializeDispatcher::
221MpiSerializeDispatcher(MpiAdapter* adapter, IMessagePassingMng* message_passing_mng)
222: m_adapter(adapter)
223, m_message_passing_mng(message_passing_mng)
224, m_trace(adapter->traceMng())
225, m_serialize_buffer_size(50000)
226//, m_serialize_buffer_size(20000000)
227, m_max_serialize_buffer_size(m_serialize_buffer_size)
228, m_byte_serializer_datatype(MPI_DATATYPE_NULL)
229{
230 _init();
231}
232
233/*---------------------------------------------------------------------------*/
234/*---------------------------------------------------------------------------*/
235
236MpiSerializeDispatcher::
237~MpiSerializeDispatcher()
238{
239 if (m_byte_serializer_datatype != MPI_DATATYPE_NULL)
240 MPI_Type_free(&m_byte_serializer_datatype);
241}
242
243/*---------------------------------------------------------------------------*/
244/*---------------------------------------------------------------------------*/
245
246MessageTag MpiSerializeDispatcher::
247nextSerializeTag(MessageTag tag)
248{
249 return MessageTag(tag.value() + 1);
250}
251
252/*---------------------------------------------------------------------------*/
253/*---------------------------------------------------------------------------*/
254
255void MpiSerializeDispatcher::
256_init()
257{
258 // Type for byte serialization.
259 MPI_Datatype mpi_datatype;
260 MPI_Type_contiguous(BasicSerializer::paddingSize(), MPI_CHAR, &mpi_datatype);
261 MPI_Type_commit(&mpi_datatype);
262 m_byte_serializer_datatype = mpi_datatype;
263
264 if (!Platform::getEnvironmentVariable("ARCCORE_TRACE_MESSAGE_PASSING_SERIALIZE").empty())
265 m_is_trace_serializer = true;
266}
267
268/*---------------------------------------------------------------------------*/
269/*---------------------------------------------------------------------------*/
270
271Request MpiSerializeDispatcher::
272legacySendSerializer(ISerializer* values, const PointToPointMessageInfo& message)
273{
274 if (!message.isRankTag())
275 ARCCORE_FATAL("Only message.isRangTag()==true are allowed for legacy mode");
276
277 MessageRank rank = message.destinationRank();
278 MessageTag mpi_tag = message.tag();
279 bool is_blocking = message.isBlocking();
280
281 BasicSerializer* sbuf = _castSerializer(values);
282 ITraceMng* tm = m_trace;
283
284 Span<Byte> bytes = sbuf->globalBuffer();
285
286 Int64 total_size = sbuf->totalSize();
287 _checkBigMessage(total_size);
288
289 if (m_is_trace_serializer)
290 tm->info() << "legacySendSerializer(): sending to "
291 << " rank=" << rank << " bytes " << bytes.size()
292 << BasicSerializer::SizesPrinter(*sbuf)
293 << " tag=" << mpi_tag << " is_blocking=" << is_blocking;
294
295 // If the message is smaller than the default serialization buffer,
296 // send the entire message
297 if (total_size <= m_serialize_buffer_size) {
298 if (m_is_trace_serializer)
299 tm->info() << "Small message size=" << bytes.size();
300 return _sendSerializerBytes(bytes, rank, mpi_tag, is_blocking);
301 }
302
303 {
304 // the message is too large to fit in the buffer, first send the sizes,
305 // then the serialized message.
306 auto x = sbuf->copyAndGetSizesBuffer();
307 if (m_is_trace_serializer)
308 tm->info() << "Big message first size=" << x.size();
309 Request r = _sendSerializerBytes(x, rank, mpi_tag, is_blocking);
310 if (!is_blocking) {
311 SerializeSubRequest* sub_request = new SerializeSubRequest();
312 sub_request->m_request = r;
313 //m_trace->info() << "** ADD SUB REQUEST r=" << r;
314 {
315 MpiLock::Section ls(m_adapter->mpiLock());
316 m_sub_requests.add(sub_request);
317 }
318 }
319 }
320
321 if (m_is_trace_serializer)
322 tm->info() << "Big message second size=" << bytes.size();
323 return _sendSerializerBytes(bytes, rank, nextSerializeTag(mpi_tag), is_blocking);
324}
325
326/*---------------------------------------------------------------------------*/
327/*---------------------------------------------------------------------------*/
328
329Request MpiSerializeDispatcher::
330_recvSerializerBytes(Span<Byte> bytes, MessageId message_id, bool is_blocking)
331{
332 SerializeByteConverter<Byte> sbc(bytes, m_byte_serializer_datatype);
333 MPI_Datatype dt = sbc.datatype();
334 if (m_is_trace_serializer)
335 m_trace->info() << "_recvSerializerBytes: size=" << bytes.size()
336 << " message_id=" << message_id << " is_blocking=" << is_blocking;
337 return m_adapter->directRecv(sbc.data(), sbc.size(), message_id, sbc.elementSize(), dt, is_blocking);
338}
339
340/*---------------------------------------------------------------------------*/
341/*---------------------------------------------------------------------------*/
342
343Request MpiSerializeDispatcher::
344_recvSerializerBytes(Span<Byte> bytes, MessageRank rank, MessageTag tag, bool is_blocking)
345{
346 SerializeByteConverter<Byte> sbc(bytes, m_byte_serializer_datatype);
347 MPI_Datatype dt = sbc.datatype();
348 if (m_is_trace_serializer)
349 m_trace->info() << "_recvSerializerBytes: size=" << bytes.size()
350 << " rank=" << rank << " tag=" << tag << " is_blocking=" << is_blocking;
351 Request r = m_adapter->directRecv(sbc.data(), sbc.size(), rank.value(),
352 sbc.elementSize(), dt, tag.value(), is_blocking);
353 if (m_is_trace_serializer)
354 m_trace->info() << "_recvSerializerBytes: request=" << r;
355 return r;
356}
357
358/*---------------------------------------------------------------------------*/
359/*---------------------------------------------------------------------------*/
360
361Request MpiSerializeDispatcher::
362_sendSerializerBytes(Span<const Byte> bytes, MessageRank rank, MessageTag tag,
363 bool is_blocking)
364{
365 SerializeByteConverter<const Byte> sbc(bytes, m_byte_serializer_datatype);
366 MPI_Datatype dt = sbc.datatype();
367 if (m_is_trace_serializer)
368 m_trace->info() << "_sendSerializerBytes: orig_size=" << bytes.size()
369 << " rank=" << rank << " tag=" << tag
370 << " second_size=" << sbc.size()
371 << " message_size=" << sbc.messageSize();
372 Request r = m_adapter->directSend(sbc.data(), sbc.size(), rank.value(),
373 sbc.elementSize(), dt, tag.value(), is_blocking);
374 if (m_is_trace_serializer)
375 m_trace->info() << "_sendSerializerBytes: request=" << r;
376 return r;
377}
378
379/*---------------------------------------------------------------------------*/
380/*---------------------------------------------------------------------------*/
381
382void MpiSerializeDispatcher::
383legacyReceiveSerializer(ISerializer* values, MessageRank rank, MessageTag mpi_tag)
384{
385 BasicSerializer* sbuf = _castSerializer(values);
386 ITraceMng* tm = m_trace;
387
388 if (m_is_trace_serializer)
389 tm->info() << "legacyReceiveSerializer() begin receive"
390 << " rank=" << rank << " tag=" << mpi_tag;
391 sbuf->preallocate(m_serialize_buffer_size);
392 Span<Byte> bytes = sbuf->globalBuffer();
393
394 _recvSerializerBytes(bytes, rank, mpi_tag, true);
395 Int64 total_recv_size = sbuf->totalSize();
396
397 if (m_is_trace_serializer)
398 tm->info() << "legacyReceiveSerializer total_size=" << total_recv_size
399 << " from=" << rank
400 << BasicSerializer::SizesPrinter(*sbuf);
401
402 // If the message is smaller than the buffer, it simply deserializes it
403 if (total_recv_size <= m_serialize_buffer_size) {
404 sbuf->setFromSizes();
405 return;
406 }
407
408 if (m_is_trace_serializer)
409 tm->info() << "Receive overflow buffer: " << total_recv_size;
410 sbuf->preallocate(total_recv_size);
411 bytes = sbuf->globalBuffer();
412 _recvSerializerBytes(bytes, rank, nextSerializeTag(mpi_tag), true);
413 sbuf->setFromSizes();
414 if (m_is_trace_serializer)
415 tm->info() << "End receive overflow buffer: " << total_recv_size;
416}
417
418/*---------------------------------------------------------------------------*/
419/*---------------------------------------------------------------------------*/
420
421void MpiSerializeDispatcher::
422checkFinishedSubRequests()
423{
424 // Checks if the sub-requests are finished to free them
425 // This is only used with the historical mode where we use
426 // the 'MpiSerializeMessageList' class.
427 UniqueArray<SerializeSubRequest*> new_sub_requests;
428 for (Integer i = 0, n = m_sub_requests.size(); i < n; ++i) {
429 SerializeSubRequest* ssr = m_sub_requests[i];
430 bool is_finished = m_adapter->testRequest(ssr->m_request);
431 if (!is_finished) {
432 new_sub_requests.add(ssr);
433 }
434 else {
435 delete ssr;
436 }
437 }
438 m_sub_requests = new_sub_requests;
439}
440
441/*---------------------------------------------------------------------------*/
442/*---------------------------------------------------------------------------*/
443
444void MpiSerializeDispatcher::
445_checkBigMessage(Int64 message_size)
446{
447 if (message_size > m_max_serialize_buffer_size) {
448 m_max_serialize_buffer_size = message_size;
449 m_trace->info() << "big buffer: " << message_size;
450 }
451}
452
453/*---------------------------------------------------------------------------*/
454/*---------------------------------------------------------------------------*/
455
457sendSerializer(const ISerializer* s, const PointToPointMessageInfo& message)
458{
459 return sendSerializer(s, message, false);
460}
461
462/*---------------------------------------------------------------------------*/
463/*---------------------------------------------------------------------------*/
464
466sendSerializer(const ISerializer* s, const PointToPointMessageInfo& message,
467 bool force_one_message)
468{
469 BasicSerializer* sbuf = _castSerializer(const_cast<ISerializer*>(s));
470
471 MessageRank rank = message.destinationRank();
472 MessageTag mpi_tag = message.tag();
473 bool is_blocking = message.isBlocking();
474
475 ITraceMng* tm = m_trace;
476
477 Span<const Byte> bytes = sbuf->globalBuffer();
478 Int64 total_size = sbuf->totalSize();
479 _checkBigMessage(total_size);
480
481 if (m_is_trace_serializer)
482 tm->info() << "sendSerializer(): sending to "
483 << " p2p_message=" << message
484 << " rank=" << rank << " bytes " << bytes.size()
486 << " tag=" << mpi_tag
487 << " total_size=" << total_size;
488
489 // If the message is smaller than the default serialization buffer
490 // or if we choose to send only one message, send the entire message
491 if (total_size <= m_serialize_buffer_size || force_one_message) {
492 if (m_is_trace_serializer)
493 tm->info() << "Small message size=" << bytes.size();
494 return _sendSerializerBytes(bytes, rank, mpi_tag, is_blocking);
495 }
496
497 // Otherwise, first send the sizes and then another request that
498 // will send the entire message.
499 auto x = sbuf->copyAndGetSizesBuffer();
500 Request r1 = _sendSerializerBytes(x, rank, mpi_tag, is_blocking);
501 auto* x2 = new SendSerializerSubRequest(this, sbuf, rank, nextSerializeTag(mpi_tag));
502 // Send the message directly for performance reasons.
503 x2->sendMessage();
504 r1.setSubRequest(makeRef<ISubRequest>(x2));
505 return r1;
506}
507
508/*---------------------------------------------------------------------------*/
509/*---------------------------------------------------------------------------*/
510
513{
514 BasicSerializer* sbuf = _castSerializer(s);
515 MessageRank rank = message.destinationRank();
516 MessageTag tag = message.tag();
517 bool is_blocking = message.isBlocking();
518
519 sbuf->preallocate(m_serialize_buffer_size);
520 Span<Byte> bytes = sbuf->globalBuffer();
521
522 Request r;
523 if (message.isRankTag())
524 r = _recvSerializerBytes(bytes, rank, tag, is_blocking);
525 else if (message.isMessageId())
526 r = _recvSerializerBytes(bytes, message.messageId(), is_blocking);
527 else
528 ARCCORE_THROW(NotSupportedException, "Only message.isRankTag() or message.isMessageId() is supported");
529 auto* sr = new ReceiveSerializerSubRequest(this, sbuf, nextSerializeTag(tag), 1);
530 r.setSubRequest(makeRef<ISubRequest>(sr));
531 return r;
532}
533
534/*---------------------------------------------------------------------------*/
535/*---------------------------------------------------------------------------*/
536
537void MpiSerializeDispatcher::
538broadcastSerializer(ISerializer* values, MessageRank rank)
539{
540 BasicSerializer* sbuf = _castSerializer(values);
541 ITraceMng* tm = m_trace;
542 MessageRank my_rank(m_adapter->commRank());
543 bool is_broadcaster = (rank == my_rank);
544
545 MPI_Datatype int64_datatype = MpiBuiltIn::datatype(Int64());
546 // Performs the sending in two phases. First sends the number of elements
547 // then sends the elements.
548 // TODO: it would be possible to do it in one go for messages
549 // not exceeding a certain size.
550 if (is_broadcaster) {
551 Int64 total_size = sbuf->totalSize();
552 Span<Byte> bytes = sbuf->globalBuffer();
553 _checkBigMessage(total_size);
554 ArrayView<Int64> total_size_buf(1, &total_size);
555 m_adapter->broadcast(total_size_buf.data(), total_size_buf.size(), rank.value(), int64_datatype);
556 if (m_is_trace_serializer)
557 tm->info() << "MpiSerializeDispatcher::broadcastSerializer(): sending "
559 SerializeByteConverter<Byte> sbc(bytes, m_byte_serializer_datatype);
560 m_adapter->broadcast(sbc.data(), sbc.size(), rank.value(), sbc.datatype());
561 }
562 else {
563 Int64 total_size = 0;
564 ArrayView<Int64> total_size_buf(1, &total_size);
565 m_adapter->broadcast(total_size_buf.data(), total_size_buf.size(), rank.value(), int64_datatype);
566 sbuf->preallocate(total_size);
567 Span<Byte> bytes = sbuf->globalBuffer();
568 SerializeByteConverter<Byte> sbc(bytes, m_byte_serializer_datatype);
569 m_adapter->broadcast(sbc.data(), sbc.size(), rank.value(), sbc.datatype());
570 sbuf->setFromSizes();
571 if (m_is_trace_serializer)
572 tm->info() << "MpiSerializeDispatcher::broadcastSerializer(): receiving from "
573 << " rank=" << rank << " bytes " << bytes.size()
575 }
576}
577
578/*---------------------------------------------------------------------------*/
579/*---------------------------------------------------------------------------*/
580
581BasicSerializer* MpiSerializeDispatcher::
582_castSerializer(ISerializer* serializer)
583{
584 BasicSerializer* sbuf = dynamic_cast<BasicSerializer*>(serializer);
585 if (!sbuf)
586 ARCCORE_THROW(ArgumentException, "Can not cast 'ISerializer' to 'BasicSerializer'");
587 return sbuf;
588}
589
590/*---------------------------------------------------------------------------*/
591/*---------------------------------------------------------------------------*/
592
599
600/*---------------------------------------------------------------------------*/
601/*---------------------------------------------------------------------------*/
602
603} // namespace Arcane::MessagePassing::Mpi
604
605/*---------------------------------------------------------------------------*/
606/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
#define ARCCORE_THROW(exception_class,...)
Macro to throw an exception with formatting.
Modifiable view of an array of type T.
Basic implementation of 'ISerializer'.
static ARCCORE_CONSTEXPR Integer paddingSize()
Padding and alignment size.
virtual TraceMessage info()=0
Stream for an information message.
Interface of the message passing manager.
Sub-request of a request.
Definition Request.h:39
Int32 value() const
Rank value.
Definition MessageRank.h:76
int commRank() const
Rank of this instance in the communicator.
Definition MpiAdapter.h:148
Request executeOnCompletion(const SubRequestCompletionInfo &completion_info) override
Callback called when the associated request is finished.
Request executeOnCompletion(const SubRequestCompletionInfo &) override
Callback called when the associated request is finished.
Request receiveSerializer(ISerializer *s, const PointToPointMessageInfo &message) override
Receiving message.
Ref< ISerializeMessageList > createSerializeMessageListRef() override
Create a list of serialization messages.
Request sendSerializer(const ISerializer *s, const PointToPointMessageInfo &message) override
Sending message.
Wrapper for sending a byte array from a serializer.
Information for sending/receiving a point-to-point message.
bool isBlocking() const
Indicates if the message is blocking.
bool isRankTag() const
True if the instance was created with a pair (rank,tag). In this case rank() and tag() are valid.
bool isMessageId() const
True if the instance was created with a MessageId. In this case messageId() is valid.
MessageRank destinationRank() const
Message destination rank.
Completion information for a sub-request.
MessageRank sourceRank() const
Source rank of the request.
Reference to an instance.
constexpr __host__ __device__ SizeType size() const noexcept
Returns the size of the array.
Definition Span.h:327
View of an array of elements of type T.
Definition Span.h:635
String getEnvironmentVariable(const String &name)
Environment variable named name.
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
unsigned char Byte
Type of a byte.
Definition BaseTypes.h:43
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Creates a reference on a pointer.
std::int32_t Int32
Signed integer type of 32 bits.