Contenu principal

Effects of Implicit Expansion on Generated Code

R2026b

When MATLAB® performs element-wise operations, it implicitly expands arrays with compatible sizes to produce arrays of the same size. Arrays have compatible sizes if, for every dimension, the dimension sizes of the inputs are either the same or one of them is 1. See Compatible Array Sizes for Basic Operations.

By default, the code generator performs implicit expansion on operands with compatible array dimensions. Code generation behavior depends on whether the code generator can determine at code generation time that it must expand an operand. Variable-size dimensions can prevent the code generator from making this determination.

When the code generator cannot determine the expansion behavior, it produces additional code to compare operand sizes at run time and expand operands. When the code generator determines the expansion behavior at code generation time, it hardcodes the expansion and does not produce additional code. To learn more about variable-size and fixed-size arrays, see Generate Code for Variable-Size Arrays.

The implicit expansion of operands in the generated code can:

  • Increase the sizes of output arrays

  • Increase the size of the generated code

  • Decrease the performance of the generated code

If your application does not permit or require implicit expansion, you can disable implicit expansion globally, in specific functions, or for specific operations. See Control Implicit Expansion in Generated Code.

Effect of Implicit Expansion on Output Sizes

Implicit expansion applies to element-wise operations. For example, consider this MATLAB function:

function out = vector_sum(a,b)
out = a+b;
end

Suppose you generate code for this function and specify inputs with compatible sizes, one of which has a variable-size dimension. For example:

a_type = coder.typeof(1,[2 1]);
b_type = coder.typeof(1,[2 inf]);

By default, the code generator expands a in the + operation to match the size of b, and the function vector_sum returns a 2-by-Inf array. If you disable implicit expansion, the code generator does not expand a in the + operation, and the function vector_sum returns a 2-by-1 array. In this case, the code generator produces a run-time check that verifies that b is 2-by-1.

Under certain circumstances, implicit expansion can lead to size mismatch errors. For example, code generation can fail if you assign the implicitly expanded output of a binary operation to a fixed-size variable of different size. See Resolve Error: Fixed Size on the Left Side but Variable Size on the Right.

Effect of Implicit Expansion on Size of Generated Code

To perform implicit expansion, the code generator introduces helper functions that expand the operands. This table shows how the generated C code can differ for variable-size operands, based on whether you enable or disable implicit expansion.

MATLAB CodeImplicit Expansion EnabledImplicit Expansion Disabled
function out = vector_sum(a,b)
out = a+b;
end

Generate C code for this function and specify that both arguments are variable-length vectors.

static void plus(emxArray_real_T *in1, const emxArray_real_T *in2,
                 const emxArray_real_T *in3)
{
  const double *in2_data;
  const double *in3_data;
  double *in1_data;
  int i;
  int loop_ub;
  int stride_0_1;
  int stride_1_1;
  in3_data = in3->data;
  in2_data = in2->data;
  stride_0_1 = in1->size[0] * in1->size[1];
  in1->size[0] = 1;
  emxEnsureCapacity_real_T(in1, stride_0_1);
  if (in3->size[1] == 1) {
    loop_ub = in2->size[1];
  } else {
    loop_ub = in3->size[1];
  }
  stride_0_1 = in1->size[0] * in1->size[1];
  in1->size[1] = loop_ub;
  emxEnsureCapacity_real_T(in1, stride_0_1);
  in1_data = in1->data;
  stride_0_1 = (in2->size[1] != 1);
  stride_1_1 = (in3->size[1] != 1);
  if (loop_ub < 400) {
    for (i = 0; i < loop_ub; i++) {
      in1_data[i] = in2_data[i * stride_0_1] + in3_data[i * stride_1_1];
    }
  } else {
#pragma omp parallel for num_threads(omp_get_max_threads())

    for (i = 0; i < loop_ub; i++) {
      in1_data[i] = in2_data[i * stride_0_1] + in3_data[i * stride_1_1];
    }
  }
}
...
void vector_sum(const emxArray_real_T *a, const emxArray_real_T *b,
                emxArray_real_T *out)
{
  const double *a_data;
  const double *b_data;
  double *out_data;
  int i;
  if (!isInitialized_vector_sum) {
    vector_sum_initialize();
  }
  b_data = b->data;
  a_data = a->data;
  if (a->size[1] == b->size[1]) {
    int loop_ub;
    int scalarLB;
    int vectorUB;
    scalarLB = out->size[0] * out->size[1];
    out->size[0] = 1;
    loop_ub = a->size[1];
    out->size[1] = a->size[1];
    emxEnsureCapacity_real_T(out, scalarLB);
    out_data = out->data;
    scalarLB = (a->size[1] / 2) << 1;
    vectorUB = scalarLB - 2;
    for (i = 0; i <= vectorUB; i += 2) {
      _mm_storeu_pd(&out_data[i], _mm_add_pd(_mm_loadu_pd(&a_data[i]),
                                             _mm_loadu_pd(&b_data[i])));
    }
    for (i = scalarLB; i < loop_ub; i++) {
      out_data[i] = a_data[i] + b_data[i];
    }
  } else {
    plus(out, a, b);
  }
}
void vector_sum(const emxArray_real_T *a, const emxArray_real_T *b,
                emxArray_real_T *out)
{
  const double *a_data;
  const double *b_data;
  double *out_data;
  int i;
  int loop_ub;
  int scalarLB;
  int vectorUB;
  b_data = b->data;
  a_data = a->data;
  scalarLB = out->size[0] * out->size[1];
  out->size[0] = 1;
  loop_ub = a->size[1];
  out->size[1] = a->size[1];
  emxEnsureCapacity_real_T(out, scalarLB);
  out_data = out->data;
  scalarLB = (a->size[1] / 2) << 1;
  vectorUB = scalarLB - 2;
  for (i = 0; i <= vectorUB; i += 2) {
    _mm_storeu_pd(&out_data[i], _mm_add_pd(_mm_loadu_pd(&a_data[i]),
                                           _mm_loadu_pd(&b_data[i])));
  }
  for (i = scalarLB; i < loop_ub; i++) {
    out_data[i] = a_data[i] + b_data[i];
  }
}

When implicit expansion is enabled and operand sizes differ, the code generator can create a supporting function that implements binary operations using expanded indexing. The supporting function performs implicit expansion using loops that read from the operands multiple times. Typically, the code generator names the supporting function after the binary operation it performs, such as plus, minus, or expand.

Effect of Implicit Expansion on Performance of Generated Code

If the generated code uses implicit expansion, it might perform differently than code that does not use implicit expansion. For example, depending on the inputs that require implicit expansion, the code might take longer to calculate the results of binary operations.

See Also

Topics