Chapter 7 Optimizing the Code Generation Process

The cost of serialization and deserialization operations increases with type complexity and sample size. It can become a significant contributor to the latency required to send and receive a sample. Code Generator provides the command-line option -optimization, which can be used to indicate the level of optimization of the serialize/deserialize operations. This command-line option allows selecting one of three different levels.

7.1 Optimization Levels

0: No optimization

1: rtiddsgen generates extra code for typedefs but optimizes its use. If a type that is used is a typedef that can be resolved to a primitive, enum, or aggregated type (struct, union, or value type), the generated code will invoke the code of the most basic type to which the typedef can be resolved. This level can be used if the generated code for typedef is not expected to be modified. This is the only optimization level supported for Java and C# languages.

For example:

typedef int32 Latitude;
typedef int32 Latitude;
 
struct Position {
    Latitude x;
    Longitude y;
};

With optimization 0, the serialization of a sample with type Position will require calling the serialize methods for Latitude and Longitude. For example:

LatitudePlugin_serialize(...) {
    serialize_long(...)
}
 
LongitudePlugin_serialize(...) {
    serialize_long(...)
}
 
Position_serialize(...) {
    LatitudePlugin_serialize(...)
    LongitudePlugin_serialize(...)
}

With optimization 1, rtiddsgen resolves Latitude and Longitude to their most primitive types for serialization purposes, resulting in a more efficient serialization. In this case, rtiddsgen will save two function/method calls.

Position_serialize(...) {
    serialize_long(...)
    serialize_long(...)
}

2: This optimization level is the default if not specified. (You can also explicitly specify it.) This optimization level applies only to C, C++, C++11, microC, microC++, and Ada languages. With this optimization level, rtiddsgen optimizes the serialization/deserialization of structures and valuetypes by using more aggressive techniques. These techniques include inline expansion of nested types and serialization/deserialization of a set of consecutive members with a single copy function invocation (memcpy) when the memory layout (C, C++ structure layout) is the same as the wire layout (XCDR).

7.2 How the Optimizations are Applied

In Code Generator, the optimizations (inline expansion of nested types and serialization of consecutive members with a single copy) are related. Inline expansion of a nested structure is only done when the C/C++ memory layout with standard packing of the structure matches the XCDR layout. (In this case, the structure’s members can be serialized with a single memcpy.) If the C/C++ memory layout with standard packing of the structure matches the XCDR layout, then rtiddsgen tries first to do the inline expansion, then the serialization of consecutive members with a single copy.

7.2.1 Inline expansion of nested types

Inline expansion is an optimization in which Code Generator replaces a type definition with another one in which nested types are flattened out. This is done to remove extra function calls during serialization/deserialization. For example:

struct Point {
    int32 x;
    int32 y;
};
 
struct Dimension {
    int32 height;
    int32 width;
};
 
struct Rectangle {
    Point leftTop;
    Dimension size;
};

With optimization level 2, Code Generator replaces the definition of Rectangle with the following equivalent definition:

struct Rectangle {
    int32 leftTop_x;
    int32 leftTop_y;
    int32 size_height;
    int32 size_width;
};

This optimization is only done for serialization/deserialization. The generated type in C/C++ continues using Point and Dimension.

7.2.2 Serialization of consecutive members with a single copy

In the previous Rectangle example, Code Generator, using optimization level 2, further optimizes the serialization and deserialization by serializing a Rectangle sample with a single copy operation (memcpy) instead of four.

Before optimization:

Rectangle_serialize(...) {
    memcpy(..., 4) // leftTop_x
    memcpy(..., 4) // leftTop_y
    memcpy(..., 4) // size_height
    memcpy(..., 4) // size_width
}

After optimization:

Rectangle_serialize(...) {
    memcpy(..., 16) // leftTop_x
}

This optimization is only applicable when the memory layout of the C/C++ structure is equivalent to the serialization layout, which uses the XCDR version 1 or version 2 format.

7.2.3 Rules for Inline Expansion

To be inlinable, a structure 'MyStruct' has to meet the following two requirements:

  • It has to have a C/C++-friendly XCDR layout.
  • No members of 'MyStruct' should be marked with the @min, @max, or @range annotations.

A struct/valuetype 'MyStruct' has a C/C++-friendly XCDR layout when all of the following conditions apply:

  • MyStruct is marked as @final or @appendable when the data representation is XCDR version 1. Mutable structures are not inlinable.
  • MyStruct does not have a base type.
  • MyStruct contains only primitive members, or complex members composed only of primitive members. A primitive member is a member with any of the following types: int81, uint82, int16, int32, int64, uint16, uint32, uint64, float, double, octet, and char. The following primitive types are not supported for inlining purposes: long double, wchar, boolean, enum.
  • struct Dimension {
        int32 height;
        int32 width;
    }; // Inlinable
     
    struct Dimension {
        string label; // Inlinable structures cannot contain strings
        int32 height;
        int32 width;
    }; // Not Inlinable
  • With any initial alignment (1, 2, 4, 8) greater than the alignment of the first member of the struct, there is no padding between the members that are part of MyStruct. To apply this rule, consider these alignments and sizes for primitive types:
  • Table 7.1 Alignments and Sizes for Primitive Types

    Primitive Type

    Alignment (bytes)

    Size (bytes)

    int83

    1

    1

    uint84

    1

    1

    int16

    2

    2

    uint16

    2

    2

    int32

    4

    4

    uint32

    4

    4

    int64

    8

    8

    uint64

    8

    8

    float

    4

    4

    double

    8

    8

    octet

    1

    1

    char

    1

    1

    struct Dimension {
        int32 height;
        int16 width;
    }; // Inlinable. Independently of the alignment of the starting memory address (4 or 8), there is no padding between height and width
     
    struct Dimension {
        int16 height;
        int32 width;
    }; // Not Inlinable. Starting in a memory address aligned to 4 will require adding a padding of two bytes between height and width
  • With any initial alignment (1, 2, 4, 8) greater than the alignment of the first member of the struct, there is no padding between the elements of an array of MyStruct.
  • struct Dimension {
        int32 height;
        int16 width;
    }; // Not inlinable. Let's assume an array of two dimensions Dimension[2]. If the array starts in a memory address aligned to 4, there would be padding between the first and the second element of the array
     
    struct Dimension {
        int32 height;
        int16 width;
        int16 padding;
    }; // Inlinable

For serialization and deserialization purposes, Code Generator will consider an inlinable structure (according to the previous rules) as a primitive array where the alignment of the primitive type corresponds to the alignment of the first member of the structure. A member with type ‘MyStruct’ will be serialized with a single copy (memcpy) invocation.

When Code Generator serializes the members of a data structure, it will also try to coalesce the serialization of consecutive primitive members into a single copy operation if possible. Code Generator only applies this optimization when the alignment of the next member is equal to or smaller than the alignment of the current member.

struct Dimension {
    int16 height;
    int32 width;
}; // Coalescing not possible because the alignment of width 4 is greater than the alignment of height 2
 
struct Dimension {
    int32 width;
    int16 height;
}; // Coalescing is possible because the alignment of width 4 is greater than the alignment of height 2

7.2.4 Guidelines

As a rule of thumb, to take advantage of optimization level 2 for types containing only primitive types:

  • Order the members in descending alignment order (this will help with copy coalescing).
  • For XCDR version 2 encapsulation, use @final extensibility if your types will not evolve. For XCDR version 1 encapsulation, use @final or @appendable if possible (this will help with inline expansion).
  • If you use ContentFilteredTopics, it is recommended that fields that appear in the filter expression are placed at the beginning of the type.