.. _section-zero-copy-transfer: Zero Copy Transfer ================== Zero Copy transfer enables |rti_me| to transmit data samples without copying them internally, similar to :link_external_community_doc_pro_um:`Zero Copy Transfer Over Shared Memory ` in |core_pro|. This offers several benefits, including higher throughput of user data, reduced latency between DDS endpoints (compared to other transports that send serialized data, such as UDP), and decoupling sample size from latency. This is particularly useful in applications with large sample sizes, such as image or lidar point cloud data. At a high level, Zero Copy transfer works by a *DataWriter* and *DataReader* accessing the same shared memory; see :numref:`FigureZCOverview` below. A Zero Copy-enabled *DataWriter* creates a structure in a shared memory region and allocates samples from its pool to shared memory. When samples are published by the *DataWriter*, matching *DataReaders* are notified via a transport that new samples are available in shared memory. The *DataReader* then accesses the samples in shared memory using the standard DDS read/take APIs. Note that the *DataReader* and *DataWriter* must be co-located—that is, within the same operating system instance. .. _FigureZCOverview: .. figure:: ../images/cert-zc-diagram.png :width: 50% :align: center Zero Copy Transfer in |me| .. _section-zc-compatibility: Compatibility ------------- .. note:: In this documentation, we may refer to the Zero Copy transfer method in |me| as "Zero Copy v2". This is because |me| contains a subset of features from |me_micro|, and |me_micro| has two different methods for performing Zero Copy transfer: v1 and v2. |me| only supports Zero Copy v2. Zero Copy v2 is compatible with |me_micro| applications IF they are also using the Zero Copy v2 method. However, Zero Copy v2 is NOT compatible with the Zero Copy v1 method in |me_micro| (sometimes referred to as "Zero Copy transfer over Shared Memory" in documentation) or the Shared Memory Transport (SHMEM). If you are unsure if your |me_micro| version supports Zero Copy v2, refer to the documentation in your |me_micro| installation. Zero Copy v2 is NOT interoperable with the :link_external_community_doc_pro_um:`Zero Copy Transfer Over Shared Memory ` feature in |core_pro|. Overview -------- Zero Copy samples reside in a shared memory region accessible from multiple processes. When creating a :link_connextmicro_dds_api_c_up_one:`FooDataWriter ` that supports Zero Copy transfer of user samples, a sample must be created with a new non-DDS API (:link_connextmicro_dds_api_c_up_one:`FooDataWriter_get_loan() `). This will return a pointer A* to a sample ``Foo`` that lies inside a shared memory segment. A reference to this sample will be sent to a receiving :link_connextmicro_dds_api_c_up_one:`FooDataReader ` across the shared memory. This :link_connextmicro_dds_api_c_up_one:`FooDataReader ` will attach to a shared memory segment, and a pointer B* to sample ``Foo`` will be presented to you. Because the two processes share different memory spaces, A* and B* will be different but they will point to the same place in RAM. This feature requires using new RTI DDS Extension APIs: - :link_connextmicro_dds_api_c_up_one:`FooDataWriter_get_loan() ` - :link_connextmicro_dds_api_c_up_one:`FooDataWriter_discard_loan() ` - :link_connextmicro_dds_api_c_up_one:`FooDataReader_is_data_consistent() ` Getting started --------------- To enable Zero Copy transfer, follow these steps: 1. Annotate your type with the ``@transfer_mode(SHMEM_REF)`` annotation. Currently, variable-length types (strings and sequences) are not supported for types using this transfer mode when a type is annotated with the PLAIN language binding (e.g., ``@language_binding(PLAIN)`` in IDL). .. code-block:: idl @transfer_mode(SHMEM_REF) struct HelloWorld { long id; char raw_image_data[1024 * 1024]; // 1 MB }; 2. :ref:`section-register-zc-v2-transport`. References will be sent across this transport. 3. Create a :link_connextmicro_dds_api_c_up_one:`FooDataWriter ` for the above type. 4. Get a loan on a sample using :link_connextmicro_dds_api_c_up_one:`FooDataWriter_get_loan() `. 5. Write a sample using :link_connextmicro_dds_api_c_up_one:`FooDataWriter_write() `. For more information, see the example **HelloWorld_zero_copy**, or generate an example for a type annotated with ``@transfer_mode(SHMEM_REF)``: .. code-block:: console rtiddsgen -example -micro -language C HelloWorld.idl Writing samples ............... The following code illustrates how to write samples annotated with ``@transfer_mode(SHMEM_REF)``: .. code-block:: c for (int i = 0; i < 10; i++) { Foo* sample; DDS_ReturnCode_t dds_rc; /* NEW API IMPORTANT - call get_loan each time when writing a NEW sample */ dds_rc = FooDataWriter_get_loan(hw_datawriter, &sample); if (dds_rc != DDS_RETCODE_OK) { printf("Failed to get a loan\n"); return -1; } /* After this function returns with DDS_RETCODE_OK, * the middleware owns the sample */ dds_rc = FooDataWriter_write(hw_datawriter, sample, &DDS_HANDLE_NIL); } Reading samples ............... The following code illustrates how to read samples annotated with ``@transfer_mode(SHMEM_REF)``: .. code-block:: c DDS_ReturnCode_t dds_rc; dds_rc = FooDataReader_take(...) /* process sample here */ /* NEW API IMPORTANT - is_data_consistent will always return true when ZC v2 is being used */ dds_rc = FooDataReader_is_data_consistent(hw_reader, &is_data_consistent, sample,sample_info); if (dds_rc == DDS_RETCODE_OK) { if (is_data_consistent) { /* Sample is consistent. Processing of sample is valid */ } else { /* Sample is NOT consistent. Any processing of the sample should * be discarded and considered invalid. */ } } Synchronizing samples --------------------- Zero Copy v2 provides synchronization between the *DataWriter* and *DataReader*. Since the queue size is limited, samples are reused once the queue is full. However, if a *DataWriter* modifies a sample before the *DataReader* has accessed it, that sample will not be presented to you. Additionally, samples currently being read by the *DataReader* are locked, preventing the *DataWriter* from accessing them. Consider the following example with ``max_samples = 1`` (internally, the middleware will allocate 2 samples): .. code-block:: c ddsrc = FooDataWriter_get_loan(dw, &sample); /* returns pointer to sample 1 */ sample->value = 10000; ddsrc = FooDataWriter_write(datawriter, sample, &DDS_HANDLE_NIL); ddsrc = FooDataWriter_get_loan(dw, &sample); /* returns pointer to sample 2 */ sample->value = 20000; ddsrc = FooDataWriter_write(datawriter, sample, &DDS_HANDLE_NIL); /* Both samples are now available to the user, but the Reader may not have accessed them yet. */ ddsrc = FooDataWriter_get_loan(dw, &sample); /* returns pointer to sample 1 */ sample->value = 30000; ddsrc = FooDataWriter_write(datawriter, sample, &DDS_HANDLE_NIL); If the *DataReader* takes all the samples: .. code-block:: c FooDataReader_take(reader, &sample_seq, &info_seq, DDS_LENGTH_UNLIMITED, DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE); It will only receive two samples with values of ``20000`` and ``30000``, respectively. If you are currently accessing both samples by calling :link_connextmicro_dds_api_c_up_one:`FooDataReader_read() ` or :link_connextmicro_dds_api_c_up_one:`FooDataReader_take() `, they will be locked. Any attempt to call :link_connextmicro_dds_api_c_up_one:`FooDataWriter_get_loan() ` on the *DataWriter* will result in an ``OUT_OF_RESOURCES`` error. Caveats ------- - After you call :link_connextmicro_dds_api_c_up_one:`FooDataWriter_write() `, the middleware takes ownership of the sample. It is no longer safe to make any changes to the sample that was written. If, for whatever reason, you call :link_connextmicro_dds_api_c_up_one:`FooDataWriter_get_loan() ` but never write the sample, you must call :link_connextmicro_dds_api_c_up_one:`FooDataWriter_discard_loan() ` to return the sample back to the :link_connextmicro_dds_api_c_up_one:`FooDataWriter `. Otherwise, subsequently calling :link_connextmicro_dds_api_c_up_one:`FooDataWriter_get_loan() ` may fail, because the :link_connextmicro_dds_api_c_up_one:`FooDataWriter ` has no samples to loan. - The current maximum supported sample size is a little under the maximum value of a signed 32-bit integer. For that reason, do not use any samples greater than 2000000000 bytes.