Control Inlining to Fine-Tune Performance and Readability of Generated Code
Inlining is an optimization technique that replaces a function call with the body of that function. Inlining eliminates the overhead of a function call, which can improve speed. Inlining can also create opportunities to further optimize the generated C/C++ code.
However, depending on your application, too much code inlining can also have disadvantages:
Inlining can increase the size of the generated C/C++ code and reduce code readability. For example, suppose that you call a certain function
foo
many times in your source MATLAB® code. If the code generator always inlinesfoo
, the generated code size increases becausefoo
is inlined every time it is called.When you inline a function, the variables defined in the body of the function are part of the scope of the calling function. Therefore, the space allocated to these variables is not released from the stack when the inlined function completes its execution. When you do not inline a function, the allocated stack space is released when the function returns.
By default, the code generator prioritizes speed when generating MEX functions and standalone code. However, when standalone code contains user-written functions that call MathWorks® functions or MathWorks functions that call user-written functions, the code generator prioritizes code readability. In these cases, except for very small functions, the code generator usually does not inline calls between MathWorks code and user-written code. This behavior can keep the generated code cleaner by preserving the separation between user-written code and MathWorks code. You can change these defaults to fine-tune the inlining settings and generate code that meets the speed, readability, and stack space requirements of your application.
When inlining instructions conflict, the code generator follows a hierarchy of inlining controls to determine whether to inline a function in the generated code:
Calling a function using
coder.inlineCall
orcoder.nonInlineCall
overrides all other inlining controls.The
coder.inline('always')
orcoder.inline('never')
directive inside the body of a MATLAB function overrides global inlining settings and code configuration settings.Global inlining settings override code configuration settings.
If no other inlining controls conflict, the code generator exhibits the inlining behavior dictated by code configuration settings.
Control Inlining of a Specific MATLAB Function at the Call Site
To control the inlining of a specific MATLAB function at the call site, call the function using
coder.inlineCall
or coder.nonInlineCall
.
These functions override all other inlining
controls, including coder.inline
directives in the body of the
called function, global inlining settings, and code configuration settings.
Control Inlining of a Specific MATLAB Function Within the Function
To instruct the code generator to either always
or never inline a certain MATLAB function, use the coder.inline("always")
or
coder.inline("never")
directives inside the body of the
function. To learn more about these directives, see coder.inline
.
The coder.inline("always")
or
coder.inline("never")
directive inside the body of a
MATLAB function overrides global inlining settings and code configuration
settings. Some MathWorks functions include a call to the coder.inline
directive. This directive thus overrides your global inlining settings and code
configuration settings. To force or prevent inlining of such a function, use
coder.inlineCall
or
coder.nonInlineCall
.
Control Inlining Globally
If you generate code at the command line using the codegen
command, you can use the
option -O enable:inline
to instruct the code generator to inline
all functions, or use the option -O
disable:inline
to instruct the code generator not to inline
any functions. For individual functions, you can
override this option by using the coder.inline
directive in the
body of the function or by calling the function using
coder.inlineCall
or
coder.nonInlineCall
.
If you generate code using codegen
with the -O
disable:inline
or -O enable:inline
option, these
options override the code configuration settings
InlineBetweenUserFunctions
,
InlineBetweenMathWorksFunctions
, and
InlineBetweenUserAndMathWorksFunctions
.
Control Inlining Using Code Configuration Settings
Your speed and readability requirements for the code generated for user-written functions and the code generated for MathWorks functions might differ from the code generation defaults. You can use code configuration settings to control inlining behavior when MathWorks functions call other MathWorks functions, when user-written functions call other user-written functions, and when user-written functions call or are called by MathWorks functions. The settings you use for MEX function generation can differ from those you use for standalone code generation.
The table shows the settings corresponding to different inlining goals.
Inlining Goal | Code Configuration Object Property | MATLAB Coder™ App Parameter |
---|---|---|
Control inlining behavior at all call sites where a user-written function calls another user-written function |
InlineBetweenUserFunctions | On the All Settings tab, Inline between user functions |
Control inlining behavior at all call sites where a MathWorks function calls another MathWorks function |
InlineBetweenMathWorksFunctions | On the All Settings tab, Inline between MathWorks functions |
Control inlining behavior at all call sites where user-written function calls a MathWorks function or a MathWorks function calls a user-written function |
InlineBetweenUserAndMathWorksFunctions | On the All Settings tab, Inline between user and MathWorks functions |
Set these parameters depending on the needs of your application:
"Always"
– Inline all functions."Speed"
– Use internal heuristics to determine whether to inline a function. This setting prioritizes optimized code."Readability"
– Prioritize readability by only inlining very small functions. This setting balances code modularity and speed to produce readable code without negatively impacting performance."Never"
– Never inline functions. This setting can negatively impact the performance of the generated code.
Global inlining controls or inlining controls that apply to specific functions override code configuration settings.
Example Inlining Strategy
To balance the speed and readability of the generated code, you can instruct the code generator to perform these actions:
Preserve the modularity of the user-written code for better readability, even if doing so reduces the speed of the generated code. For this behavior, set
InlineBetweenUserFunctions
to"Readability"
.Generate highly optimized code for MathWorks functions, even if doing so results in less readable code, because you are less likely to inspect this part of your code base. For this behavior, set
InlineBetweenMathWorksFunctions
to"Speed"
.Preserve modularity between user-written code and MathWorks code. For this behavior, set
InlineBetweenUserAndMathWorksFunctions
to"Readability"
. Because only very small MathWorks functions are inlined when inlining is set to"Readability"
, the generated code looks similar to your MATLAB code.
Example: Control Inlining Between User-Written Functions and MathWorks Functions
This example shows how to control inlining behavior at all call sites where a user-written function calls a MathWorks function.
Define a Function That Calls a MathWorks Function
Define a MATLAB function named useBessely
that accepts a double
array x
as input, processes the input array by using the bessely
function, and returns an array that has the same type and size as x
.
type useBessely.m
function out = useBessely(x) out = x + bessely(3,x); end
Generate Standalone Code with Default Inlining Settings
Define a code configuration object named cfg
to generate source code for a static C++ library. By default, the code generator optimizes readability when a user-written function calls or is called by a MathWorks function in standalone code. To maintain code readability, the code generator generally does not inline MathWorks functions called by user-written functions.
cfg = coder.config("lib"); cfg.TargetLang = "C++"; cfg.GenCodeOnly = true;
Generate code for the useBessely
function by using the cfg
object and specify the input as a 1-by-100 double
type. Generate a report using the -report
option, and use the -d
option to create a folder readable_version
for the generated code.
codegen -config cfg useBessely -args {zeros(1,100)} -report -d readable_version
Code generation successful: To view the report, open('readable_version/html/report.mldatx')
Inspect the generated C++ code in the file useBessely.cpp
. Observe that the C++ function useBessely
calls another C++ function coder::bessely
that contains the code generated for the MathWorks function bessely
. This behavior keeps the code generated from the MATLAB function you wrote separate from the code generated from the MathWorks function.
type(fullfile("readable_version","useBessely.cpp"))
// // Prerelease License - for engineering feedback and testing purposes // only. Not for sale. // File: useBessely.cpp // // MATLAB Coder version : 24.2 // C/C++ source code generated on : 20-Jul-2024 12:23:53 // // Include Files #include "useBessely.h" #include "bessely.h" #include "rt_nonfinite.h" // Function Definitions // // Arguments : const double x[100] // creal_T out[100] // Return Type : void // void useBessely(const double x[100], creal_T out[100]) { coder::bessely(x, out); for (int i{0}; i < 100; i++) { out[i].re += x[i]; } } // // File trailer for useBessely.cpp // // [EOF] //
Generate Code with Modified Inlining Settings
To instruct the code generator to use internal heuristics to determine whether to inline MathWorks functions called by user-written functions, set the code configuration object property InlineBetweenUserAndMathWorksFunctions
to 'Speed'
. This setting can generate C++ code that is more efficient but less readable than highly modularized code.
cfg.InlineBetweenUserAndMathWorksFunctions = 'Speed';
Generate code using the cfg
code configuration object. Specify the input as a 1-by-100 double
type. Create a report using the -report
option, and use the -d
option to create a folder speed_version
for the generated code.
codegen -config cfg useBessely -args {zeros(1,100)} -report -d speed_version
Code generation successful: To view the report, open('speed_version/html/report.mldatx')
Inspect the generated C++ code in the file useBessely.cpp
. Observe that a separate C++ function has not been generated for the MathWorks function bessely
. Instead, the code generator has inlined the code for the MathWorks bessely
function into the useBessely
function that you wrote.
type(fullfile("speed_version","useBessely.cpp"))
// // Prerelease License - for engineering feedback and testing purposes // only. Not for sale. // File: useBessely.cpp // // MATLAB Coder version : 24.2 // C/C++ source code generated on : 20-Jul-2024 12:24:17 // // Include Files #include "useBessely.h" #include "cbinu.h" #include "cbknu.h" #include "cospiAndSinpi.h" #include "cuoik.h" #include "rt_nonfinite.h" #include <cmath> // Function Definitions // // Arguments : const double x[100] // creal_T out[100] // Return Type : void // void useBessely(const double x[100], creal_T out[100]) { creal_T CY; creal_T b_CY; creal_T cy; double spn; for (int k{0}; k < 100; k++) { double d; int ierr; d = x[k]; ierr = 0; if (std::isnan(d)) { CY.re = rtNaN; CY.im = 0.0; } else { double AZ; if (d == 0.0) { CY.re = rtMinusInf; CY.im = 0.0; } else if (d == 0.0) { CY.re = 0.0; CY.im = 0.0; ierr = 1; } else { creal_T ZN; double im; double re; int NUF; CY.re = 0.0; CY.im = 0.0; if (d == 0.0) { ierr = 1; } else { ierr = 0; ZN.re = d * 0.0; ZN.im = -d; AZ = std::abs(d); if (!(AZ > 0.0)) { AZ = 0.0; } if (AZ > 1.0737418235E+9) { ierr = 4; } else if (AZ > 32767.999992370605) { ierr = 3; } if (AZ < 2.2250738585072014E-305) { ierr = 2; } else { NUF = coder::cuoik(ZN, 2, CY); if (NUF < 0) { ierr = 2; } else if (NUF <= 0) { coder::cbknu(ZN, CY); ZN = CY; AZ = 1.0; if (std::fmax(std::abs(CY.re), std::abs(CY.im)) <= 1.0020841800044864E-289) { ZN.re = 4.503599627370496E+15 * CY.re; ZN.im = 4.503599627370496E+15 * CY.im; AZ = 2.2204460492503131E-16; } re = ZN.re * 0.63661977236758138 - ZN.im * 0.0; im = ZN.re * 0.0 + ZN.im * 0.63661977236758138; CY.re = AZ * re; CY.im = AZ * im; } } } if ((ierr == 0) || (ierr == 3)) { b_CY.re = 0.0; b_CY.im = 0.0; if (d == 0.0) { ierr = 1; } else { ierr = 0; ZN.re = d * 0.0; ZN.im = d; AZ = std::abs(d); if (!(AZ > 0.0)) { AZ = 0.0; } if (AZ > 1.0737418235E+9) { ierr = 4; } else if (AZ > 32767.999992370605) { ierr = 3; } if (AZ < 2.2250738585072014E-305) { ierr = 2; } else { NUF = coder::cuoik(ZN, 2, b_CY); if (NUF < 0) { ierr = 2; } else if (NUF <= 0) { boolean_T guard1; boolean_T guard2; guard1 = false; guard2 = false; if ((ZN.re == 0.0) && (d < 0.0)) { int nw; ZN.re = -ZN.re; ZN.im = -d; nw = coder::cbinu(ZN, b_CY); if (nw < 0) { NUF = -1; if (nw == -2) { NUF = -2; } guard2 = true; } else { cy.re = 0.0; cy.im = 0.0; nw = coder::cbknu(ZN, cy); if (nw != 0) { NUF = -1; if (nw == -2) { NUF = -2; } guard2 = true; } else { AZ = coder::internal::scalar::cospiAndSinpi(-0.0, spn); re = 0.0 * b_CY.re - -3.1415926535897931 * b_CY.im; im = 0.0 * b_CY.im + -3.1415926535897931 * b_CY.re; b_CY.re = re + (-AZ * cy.re - -spn * cy.im); b_CY.im = im + (-AZ * cy.im + -spn * cy.re); guard1 = true; } } } else { coder::cbknu(ZN, b_CY); guard1 = true; } if (guard2) { if (NUF == -1) { ierr = 2; } else { ierr = 5; } } if (guard1) { ZN = b_CY; AZ = 1.0; if (std::fmax(std::abs(b_CY.re), std::abs(b_CY.im)) <= 1.0020841800044864E-289) { ZN.re = 4.503599627370496E+15 * b_CY.re; ZN.im = 4.503599627370496E+15 * b_CY.im; AZ = 2.2204460492503131E-16; } re = ZN.re * 0.63661977236758138 - ZN.im * -0.0; im = ZN.re * -0.0 + ZN.im * 0.63661977236758138; b_CY.re = AZ * re; b_CY.im = AZ * im; } } } } if ((ierr == 0) || (ierr == 3)) { CY.re = b_CY.re - CY.re; CY.im = b_CY.im - CY.im; re = 0.0 * CY.re - 0.5 * CY.im; im = 0.0 * CY.im + 0.5 * CY.re; CY.re = re; CY.im = im; } } } if (ierr == 5) { CY.re = rtNaN; CY.im = 0.0; } else if (ierr == 2) { CY.re = rtInf; CY.im = 0.0; } if (d > 0.0) { AZ = CY.re; CY.re = AZ; CY.im = 0.0; } } out[k].re = d + CY.re; out[k].im = CY.im; } } // // File trailer for useBessely.cpp // // [EOF] //
See Also
coder.inline
| coder.inlineCall
| coder.nonInlineCall
| codegen
| coder.CodeConfig
| coder.EmbeddedCodeConfig
| coder.MexCodeConfig