%% Replacing Math Functions and Operators with Implementations that require Data Alignment - MATLAB(R)
% This example shows how to develop and use code replacement library
% entries for target-specific function implementations that require data to
% be aligned to optimize application performance.  To configure data
% alignment for a function implementation:
%
% * Specify the data alignment requirements in a table entry.  You can specify
%   alignment for implementation function arguments individually or 
%   collectively.
% * Specify the data alignment capabilities and syntax for your compiler.
%   Attach an AlignmentSpecification object to the  TargetCharacteristics 
%   object of the registry entry specified in your rtwTargetInfo.m file.
%
% If externally allocated data (e.g. entry-point function arguments) are
% used in an operation that can be replaced with an implementation that
% requires alignment, use the coder.dataAlignment directive to specify
% alignment so that replacement occurs.
%
% This example is configured to use the GCC, Clang, or MSVC compiler.

%   Copyright 2018 The MathWorks, Inc.


%% Create a New Folder and Copy Relevant Files
% The following code creates a folder in your current
% working folder (pwd). The new folder will contain the files
% that are relevant for this example. If you do not want to affect the
% current folder (or if you cannot generate files in this folder),
% you should change your working folder.
%% Run Command: Create a New Folder and Copy Relevant Files
coderdemo_setup('coderdemo_crlalign');
cleanupObj = {};
mlpath = addpath(fullfile(...
    matlabroot,'toolbox','coder','codegendemos','coderdemo_crlalign'));
cleanupObj{end+1} = onCleanup(@()path(mlpath));


%% Check selected compiler
% This example is configured to use either GCC, Clang, or MSVC to compile the
% generated code.
cc = rtwprivate('getMexCompilerInfo');
isDaDemoSupported = strcmpi(cc.comp.Manufacturer,'GNU') || ...
                    strcmpi(cc.comp.Manufacturer,'Apple') || ...
                    strcmpi(cc.comp.Manufacturer,'Microsoft');
if ~isDaDemoSupported
  recMsg = ['Use "mex -setup" to select either GCC, Clang,'...
      'or MSVC and restart this example'];
  warning(['Example %s is configured to use either GCC, Clang,'
      'or MSVC to compile the generated code. %s.'], mfilename,recMsg);
end


%% Set MATLAB Coder options
% Set up the configuration object and define the function input types.
cfg = coder.config('lib','ecoder',true);
cfg.GenerateReport = false;
cfg.LaunchReport = false;
cfg.VerificationMode = 'SIL';
cfg.CodeExecutionProfiling = true;

len = 400000;
args = {coder.typeof(single(0),[len,1]), ...
        coder.typeof(single(0))};
global g1;
g1 = single(zeros([len,1]));

%% Generate code using the SIMD Examples Code Replacement Library
% To see the code replacement table definition file, <matlab:edit(fullfile(matlabroot,'toolbox','coder','codegendemos','coderdemo_crlalign','crl_table_simd.m')) look here>.
RTW.TargetRegistry.getInstance('reset');

mcode_da16 = 'biased_sum_of_square_differences_da16';
cfg.CodeReplacementLibrary = 'SIMD Examples';
codegen('-config',cfg, mcode_da16,'-args',args,'-global',{'g1',g1});

%% Inspect the MATLAB Coder Generated Code
% After compiling, explore the generated source code.


%% Performance Gain from Data Alignment
% Compare performance of normal ANSI code against the earlier generated
% code that used SIMD intrinsics.

% Generate ansi code
mcode_noda = 'biased_sum_of_square_differences';
cfg.CodeReplacementLibrary = 'None';
codegen('-config',cfg, mcode_noda,'-args',args,'-global',{'g1',g1});

% Run SIMD executable and collect execution profile information
coder.runTest('run_biased_ssd_da16',[mcode_da16,'_sil.',mexext])
pause(120);
clear([mcode_da16,'_sil']); % stop simulation
executionProfile_simd = getCoderExecutionProfile(mcode_da16);
idx_section = find(strcmp(mcode_da16,{executionProfile_simd.Sections.Name}),1);
avg_selftime_simd = executionProfile_simd.Sections(idx_section)...
    .TotalSelfTimeInTicks/executionProfile_simd.Sections(idx_section).NumCalls;

% Run ANSI executable and collect execution profile information
coder.runTest('run_biased_ssd',[mcode_noda,'_sil.',mexext])
pause(120);
clear([mcode_noda,'_sil']); % stop simulation
executionProfile_ansi = getCoderExecutionProfile(mcode_noda);
idx_section = find(strcmp(mcode_noda,{executionProfile_ansi.Sections.Name}),1);
avg_selftime_ansi = executionProfile_ansi.Sections(idx_section)...
    .TotalSelfTimeInTicks/executionProfile_ansi.Sections(idx_section).NumCalls;

% Compare execution profile results
barObj = bar([avg_selftime_ansi; ...
    avg_selftime_simd]);
axesObj = barObj.Parent;
figObj = axesObj.Parent;
axesObj.XTickLabel = {'ANSI SSD', 'SIMD SSD'};
axesObj.YLabel.String = 'Average Execution Time (Timer Ticks)';
axesObj.YLim = [min([0,avg_selftime_ansi,avg_selftime_simd]), ...
                max(avg_selftime_ansi,avg_selftime_simd)*1.3];

percent_perf_gain = ...
    100 * (avg_selftime_ansi-avg_selftime_simd)/avg_selftime_ansi;
annotation(figObj, 'textbox',axesObj.Position, ...
    'String',sprintf(...
    'Execution speed increased by %d%% on average',percent_perf_gain), ...
    'FontWeight', 'bold', 'FontSize', 12, 'HorizontalAlignment', 'center', ...
    'FitBoxToText','On');



%% Cleanup
% Remove files and return to original folder
%% Run Command: Cleanup
RTW.TargetRegistry.getInstance('reset');
cleanup

