diff --git a/profile_subchunk_agg_check_maxazi_rev11_real.m b/profile_subchunk_agg_check_maxazi_rev11_real.m new file mode 100644 index 0000000..eaa744b --- /dev/null +++ b/profile_subchunk_agg_check_maxazi_rev11_real.m @@ -0,0 +1,207 @@ +function results = profile_subchunk_agg_check_maxazi_rev11_real( ... + app, ... + cell_aas_dist_data, ... + array_bs_azi_data, ... + radar_beamwidth, ... + min_azimuth, ... + max_azimuth, ... + base_protection_pts, ... + point_idx, ... + on_list_bs, ... + cell_sim_chunk_idx, ... + rand_seed1, ... + agg_check_reliability, ... + on_full_Pr_dBm, ... + clutter_loss, ... + custom_antenna_pattern, ... + sub_point_idx) +%PROFILE_SUBCHUNK_AGG_CHECK_MAXAZI_REV11_REAL +% Profile rev11 on exact real inputs and report dominant runtime contributors. + +if exist('subchunk_agg_check_maxazi_rev11','file')~=2 + error('profile_subchunk_agg_check_maxazi_rev11_real:MissingRev11', ... + 'subchunk_agg_check_maxazi_rev11.m was not found on MATLAB path.'); +end + +opts = struct(); +opts.AziChunkRev11 = 128; +opts.TopN = 15; +opts.EnableDetailBuiltin = true; + +fprintf('\n=== PROFILE REV11 (REAL INPUTS) ===\n'); +fprintf('AZI_CHUNK rev11: %d\n',opts.AziChunkRev11); + +profile off; +profile clear; +if opts.EnableDetailBuiltin + profile('-memory','off','-detail','builtin'); +end +profile on; + +runtime_tic=tic; +out = subchunk_agg_check_maxazi_rev11(app,cell_aas_dist_data,array_bs_azi_data, ... + radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs, ... + cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss, ... + custom_antenna_pattern,sub_point_idx,opts.AziChunkRev11); %#ok +wall_runtime_s=toc(runtime_tic); + +profile off; +pinfo=profile('info'); + +if ~isfield(pinfo,'FunctionTable') || isempty(pinfo.FunctionTable) + error('profile_subchunk_agg_check_maxazi_rev11_real:EmptyProfile', ... + 'MATLAB profile did not return function timing data.'); +end + +ft=pinfo.FunctionTable; +name_col=cell(numel(ft),1); +total_col=zeros(numel(ft),1); +self_col=zeros(numel(ft),1); +calls_col=zeros(numel(ft),1); +for i=1:numel(ft) + name_col{i}=safe_get(ft(i),{'FunctionName','CompleteName','FileName'},''); + total_col(i)=safe_get(ft(i),{'TotalTime'},NaN); + self_col(i)=safe_get(ft(i),{'SelfTime'},NaN); + calls_col(i)=safe_get(ft(i),{'NumCalls'},NaN); +end + +tbl=table(name_col,total_col,self_col,calls_col, ... + 'VariableNames',{'Function','TotalTime_s','SelfTime_s','NumCalls'}); + +[~,idx_total]=sort(tbl.TotalTime_s,'descend','MissingPlacement','last'); +[~,idx_self]=sort(tbl.SelfTime_s,'descend','MissingPlacement','last'); + +top_n=min(opts.TopN,height(tbl)); +top_total=tbl(idx_total(1:top_n),:); +top_self=tbl(idx_self(1:top_n),:); + +fprintf('\nTop contributors by total time:\n'); +disp(top_total); + +fprintf('\nTop contributors by self time:\n'); +disp(top_self); + +key_names = { ... + 'subchunk_agg_check_maxazi_rev11', ... + 'monte_carlo_Pr_dBm_rev2_app', ... + 'monte_carlo_super_bs_eirp_dist_rev5', ... + 'monte_carlo_clutter_rev3_app'}; + +key_times = struct(); +for i=1:numel(key_names) + key=key_names{i}; + row=match_rows(tbl,key); + key_times.(matlab.lang.makeValidName(key))=summarize_rows(tbl,row,wall_runtime_s); +end + +% Off-axis gain build path proxy: nearestpoint + azimuth contributions. +off_axis_parts={'nearestpoint_app','azimuth'}; +off_axis_rows=false(height(tbl),1); +for i=1:numel(off_axis_parts) + off_axis_rows=off_axis_rows | match_rows(tbl,off_axis_parts{i}); +end +off_axis_summary=summarize_rows(tbl,off_axis_rows,wall_runtime_s); + +% Aggregation path proxy: db2pow/pow2db/sum/max inside rev11 aggregation loop. +agg_parts={'db2pow','pow2db','sum','max'}; +agg_rows=false(height(tbl),1); +for i=1:numel(agg_parts) + agg_rows=agg_rows | match_rows(tbl,agg_parts{i}); +end +agg_summary=summarize_rows(tbl,agg_rows,wall_runtime_s); + +fprintf('\nExplicit target function timings:\n'); +print_key('subchunk_agg_check_maxazi_rev11',key_times.subchunk_agg_check_maxazi_rev11); +print_key('monte_carlo_Pr_dBm_rev2_app',key_times.monte_carlo_Pr_dBm_rev2_app); +print_key('monte_carlo_super_bs_eirp_dist_rev5',key_times.monte_carlo_super_bs_eirp_dist_rev5); +print_key('monte_carlo_clutter_rev3_app',key_times.monte_carlo_clutter_rev3_app); + +fprintf('\nPath proxies (if visible in profiler):\n'); +print_key('off-axis gain build path proxy',off_axis_summary); +print_key('aggregation path proxy',agg_summary); + +% Select highest-value optimization target from measured contributors. +focus_labels={ ... + 'aggregation_path_proxy', ... + 'monte_carlo_Pr_dBm_rev2_app', ... + 'monte_carlo_super_bs_eirp_dist_rev5', ... + 'monte_carlo_clutter_rev3_app', ... + 'off_axis_gain_build_proxy'}; +focus_times=[ ... + agg_summary.total_time_s, ... + key_times.monte_carlo_Pr_dBm_rev2_app.total_time_s, ... + key_times.monte_carlo_super_bs_eirp_dist_rev5.total_time_s, ... + key_times.monte_carlo_clutter_rev3_app.total_time_s, ... + off_axis_summary.total_time_s]; + +[best_time,best_idx]=max(focus_times); +best_label=focus_labels{best_idx}; +if ~isfinite(best_time) || best_time<=0 + recommendation='Insufficient profiler signal; rerun with detail builtin enabled and larger workload.'; +else + recommendation=sprintf('Optimize %s first (largest measured contributor: %.6f s).',best_label,best_time); +end + +fprintf('\nRecommendation: %s\n',recommendation); + +results=struct(); +results.options=opts; +results.wall_runtime_s=wall_runtime_s; +results.top_by_total=top_total; +results.top_by_self=top_self; +results.key_timings=key_times; +results.off_axis_gain_build_path=off_axis_summary; +results.aggregation_path=agg_summary; +results.recommended_target=best_label; +results.recommendation_text=recommendation; +results.full_profile_table=tbl; + +end + +function val=safe_get(s,keys,default_val) +val=default_val; +for k=1:numel(keys) + if isfield(s,keys{k}) + val=s.(keys{k}); + return; + end +end +end + +function rows=match_rows(tbl,pattern) +rows=false(height(tbl),1); +for i=1:height(tbl) + fn=tbl.Function{i}; + if contains(fn,pattern,'IgnoreCase',true) + rows(i)=true; + end +end +end + +function s=summarize_rows(tbl,rows,wall_runtime_s) +if ~any(rows) + s=struct('visible',false,'num_rows',0,'total_time_s',0,'self_time_s',0, ... + 'pct_of_wall',0,'pct_of_profile_total',0,'calls',0,'matches',{{}}); + return; +end + +total_profile_time=sum(tbl.TotalTime_s,'omitnan'); +s=struct(); +s.visible=true; +s.num_rows=nnz(rows); +s.total_time_s=sum(tbl.TotalTime_s(rows),'omitnan'); +s.self_time_s=sum(tbl.SelfTime_s(rows),'omitnan'); +s.pct_of_wall=100*s.total_time_s/max(wall_runtime_s,eps); +s.pct_of_profile_total=100*s.total_time_s/max(total_profile_time,eps); +s.calls=sum(tbl.NumCalls(rows),'omitnan'); +s.matches=tbl.Function(rows); +end + +function print_key(label,s) +if s.visible + fprintf(' %-38s total=%10.6f s | self=%10.6f s | wall%%=%6.2f%% | calls=%g\n', ... + label,s.total_time_s,s.self_time_s,s.pct_of_wall,s.calls); +else + fprintf(' %-38s not visible in current profiler table\n',label); +end +end diff --git a/subchunk_agg_check_maxazi_rev14.m b/subchunk_agg_check_maxazi_rev14.m new file mode 100644 index 0000000..8165455 --- /dev/null +++ b/subchunk_agg_check_maxazi_rev14.m @@ -0,0 +1,120 @@ +function [sub_array_agg_check_mc_dBm]=subchunk_agg_check_maxazi_rev14(app,cell_aas_dist_data,array_bs_azi_data,radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs,cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss,custom_antenna_pattern,sub_point_idx,varargin) +%SUBCHUNK_AGG_CHECK_MAXAZI_REV14 +% Focused optimization pass over rev11. +% Optimization target: aggregation path in STEP 4. +% Strategy: avoid repeated per-chunk db2pow/pow2db conversion over full BSxAZI matrix. +% Preserve rev11 RNG behavior and output contract. + +AZI_CHUNK_DEFAULT=128; +DEBUG_CHECKS=false; +azi_chunk=AZI_CHUNK_DEFAULT; +if ~isempty(varargin) + azi_chunk=varargin{1}; +end +azi_chunk=max(1,round(azi_chunk)); + +array_aas_dist_data=cell_aas_dist_data{2}; +aas_dist_azimuth=cell_aas_dist_data{1}; +mod_azi_diff_bs=array_bs_azi_data(:,4); + +% Off-axis EIRP lookup at BS-relative azimuth. +nn_azi_idx=nearestpoint_app(app,mod_azi_diff_bs,aas_dist_azimuth); +super_array_bs_eirp_dist=array_aas_dist_data(nn_azi_idx,:); + +% Simulation azimuth grid. +[array_sim_azimuth,num_sim_azi]=calc_sim_azimuths_rev3_360_azimuths_app(app,radar_beamwidth,min_azimuth,max_azimuth); + +% BS->point azimuths. +sim_pt=base_protection_pts(point_idx,:); +bs_azimuth=azimuth(sim_pt(1),sim_pt(2),on_list_bs(:,1),on_list_bs(:,2)); + +% MC iteration indices for this sub-point. +sub_mc_idx=cell_sim_chunk_idx{sub_point_idx}; %#ok +num_mc_idx=length(sub_mc_idx); +num_bs=length(bs_azimuth); +sub_array_agg_check_mc_dBm=NaN(num_mc_idx,1); + +% ------------------------------------------------------------------------- +% STEP 1: MC random pre-generation using a single RNG seeding call. +% ------------------------------------------------------------------------- +rel_min=min(agg_check_reliability); +rel_max=max(agg_check_reliability); + +if rel_min==rel_max + rand_pr_all=repmat(rel_min,num_bs,num_mc_idx); + rand_eirp_all=rand_pr_all; + rand_clutter_all=rand_pr_all; +else + rng(rand_seed1); + rel_span=(rel_max-rel_min); + rand_pr_all=rel_min+rel_span.*rand(num_bs,num_mc_idx); + rand_eirp_all=rel_min+rel_span.*rand(num_bs,num_mc_idx); + rand_clutter_all=rel_min+rel_span.*rand(num_bs,num_mc_idx); +end + +% ------------------------------------------------------------------------- +% STEP 2: Precompute off-axis gain matrix once for all (bs,sim_azimuth). +% ------------------------------------------------------------------------- +pat_az=mod(custom_antenna_pattern(:,1),360); +pat_gain=custom_antenna_pattern(:,2); +[pat_az_unique,ia_unique]=unique(pat_az,'stable'); +pat_gain_unique=pat_gain(ia_unique); + +off_axis_gain_matrix=NaN(num_bs,num_sim_azi); +for azimuth_idx=1:1:num_sim_azi + sim_azimuth=array_sim_azimuth(azimuth_idx); + rel_az=mod(bs_azimuth-sim_azimuth,360); + ant_deg_idx=nearestpoint_app(app,rel_az,pat_az_unique); + off_axis_gain_matrix(:,azimuth_idx)=pat_gain_unique(ant_deg_idx); +end + +% Focused optimization input for STEP 4: +% convert off-axis gain once to multiplicative mW-domain factor. +off_axis_gain_linear=db2pow(off_axis_gain_matrix); + +% ------------------------------------------------------------------------- +% STEP 3: RNG-free MC pathloss terms for each MC realization. +% ------------------------------------------------------------------------- +sort_monte_carlo_pr_dBm_all=NaN(num_bs,num_mc_idx); +for loop_idx=1:1:num_mc_idx + pre_sort_monte_carlo_pr_dBm=monte_carlo_Pr_dBm_rev2_app(app,agg_check_reliability,on_full_Pr_dBm,rand_pr_all(:,loop_idx)); + rand_norm_eirp=monte_carlo_super_bs_eirp_dist_rev5(app,super_array_bs_eirp_dist,agg_check_reliability,rand_eirp_all(:,loop_idx)); + monte_carlo_clutter_loss=monte_carlo_clutter_rev3_app(app,agg_check_reliability,clutter_loss,rand_clutter_all(:,loop_idx)); + + sort_monte_carlo_pr_dBm_all(:,loop_idx)=pre_sort_monte_carlo_pr_dBm+rand_norm_eirp-monte_carlo_clutter_loss; +end + +% ------------------------------------------------------------------------- +% STEP 4: Aggregate over BS in linear mW domain, max over azimuth. +% rev11 used db2pow(base+gain) inside each chunk; rev14 factors that as: +% 10^((base+gain)/10) = 10^(base/10) .* 10^(gain/10) +% so base conversion is once per MC realization and gain conversion is once globally. +% ------------------------------------------------------------------------- +for loop_idx=1:1:num_mc_idx + base_mc=sort_monte_carlo_pr_dBm_all(:,loop_idx); + base_mc_linear=db2pow(base_mc); % mW + max_azi_agg=-Inf; + + for azi_start=1:azi_chunk:num_sim_azi + azi_end=min(azi_start+azi_chunk-1,num_sim_azi); + chunk_gain_linear=off_axis_gain_linear(:,azi_start:azi_end); + + chunk_mW=sum(base_mc_linear.*chunk_gain_linear,1,'omitnan'); + azimuth_agg_dBm_chunk=pow2db(chunk_mW); + + if DEBUG_CHECKS + if any(isnan(azimuth_agg_dBm_chunk),'all') + error('subchunk_agg_check_maxazi_rev14:NaNChunkAgg','NaN detected in chunk aggregate output'); + end + end + + chunk_max=max(azimuth_agg_dBm_chunk,[],'omitnan'); + if chunk_max>max_azi_agg + max_azi_agg=chunk_max; + end + end + + sub_array_agg_check_mc_dBm(loop_idx,1)=max_azi_agg; +end + +end diff --git a/validate_subchunk_agg_check_maxazi_rev11_rev14_statistical.m b/validate_subchunk_agg_check_maxazi_rev11_rev14_statistical.m new file mode 100644 index 0000000..4eb3620 --- /dev/null +++ b/validate_subchunk_agg_check_maxazi_rev11_rev14_statistical.m @@ -0,0 +1,204 @@ +function results = validate_subchunk_agg_check_maxazi_rev11_rev14_statistical( ... + app, ... + cell_aas_dist_data, ... + array_bs_azi_data, ... + radar_beamwidth, ... + min_azimuth, ... + max_azimuth, ... + base_protection_pts, ... + point_idx, ... + on_list_bs, ... + cell_sim_chunk_idx, ... + rand_seed1, ... + agg_check_reliability, ... + on_full_Pr_dBm, ... + clutter_loss, ... + custom_antenna_pattern, ... + sub_point_idx) +%VALIDATE_SUBCHUNK_AGG_CHECK_MAXAZI_REV11_REV14_STATISTICAL +% Statistical and runtime validation for rev11 vs rev14 on identical real inputs. +% Fail-closed by default when configured thresholds are exceeded. + +if exist('subchunk_agg_check_maxazi_rev11','file')~=2 + error('validate_subchunk_agg_check_maxazi_rev11_rev14_statistical:MissingRev11', ... + 'subchunk_agg_check_maxazi_rev11.m was not found on MATLAB path.'); +end +if exist('subchunk_agg_check_maxazi_rev14','file')~=2 + error('validate_subchunk_agg_check_maxazi_rev11_rev14_statistical:MissingRev14', ... + 'subchunk_agg_check_maxazi_rev14.m was not found on MATLAB path.'); +end + +opts = struct(); +opts.AziChunkRev11 = 128; +opts.AziChunkRev14 = 128; +opts.NumTimingRuns = 3; +opts.AbsDiffThreshold_dB = 0.50; +opts.RelDiffThreshold = 0.05; +opts.TailAbsDiffThreshold_dB = 0.25; +opts.TailRelDiffThreshold = 0.02; +opts.EnableP999 = true; + +fprintf('\n=== REV11 vs REV14 STATISTICAL VALIDATION ===\n'); +fprintf('AZI_CHUNK rev11: %d\n',opts.AziChunkRev11); +fprintf('AZI_CHUNK rev14: %d\n',opts.AziChunkRev14); +fprintf('Timing runs each: %d\n',opts.NumTimingRuns); + +runtime11=zeros(opts.NumTimingRuns,1); +runtime14=zeros(opts.NumTimingRuns,1); +out11=[]; +out14=[]; + +for run_idx=1:opts.NumTimingRuns + t11=tic; + out11=subchunk_agg_check_maxazi_rev11(app,cell_aas_dist_data,array_bs_azi_data, ... + radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs, ... + cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss, ... + custom_antenna_pattern,sub_point_idx,opts.AziChunkRev11); + runtime11(run_idx)=toc(t11); + + t14=tic; + out14=subchunk_agg_check_maxazi_rev14(app,cell_aas_dist_data,array_bs_azi_data, ... + radar_beamwidth,min_azimuth,max_azimuth,base_protection_pts,point_idx,on_list_bs, ... + cell_sim_chunk_idx,rand_seed1,agg_check_reliability,on_full_Pr_dBm,clutter_loss, ... + custom_antenna_pattern,sub_point_idx,opts.AziChunkRev14); + runtime14(run_idx)=toc(t14); +end + +speedup_runs=runtime11./runtime14; + +x11=out11(:); +x14=out14(:); +finite_mask=isfinite(x11) & isfinite(x14); +x11=x11(finite_mask); +x14=x14(finite_mask); + +if isempty(x11) + error('validate_subchunk_agg_check_maxazi_rev11_rev14_statistical:NoFiniteSamples', ... + 'No finite paired samples available for statistical comparison.'); +end + +metrics={'mean','std','min','max','median','p90','p95','p99'}; +q=[0.90 0.95 0.99]; + +s11=build_summary(x11,q,opts.EnableP999); +s14=build_summary(x14,q,opts.EnableP999); +if isfield(s11,'p99_9') + metrics=[metrics {'p99_9'}]; %#ok +end + +diff_table=struct(); +pass_flags=true(1,numel(metrics)); +for k=1:1:numel(metrics) + m=metrics{k}; + v11=s11.(m); + v14=s14.(m); + abs_diff=abs(v14-v11); + rel_diff=abs_diff/max(abs(v11),eps); + + is_tail=ismember(m,{'p95','p99','p99_9'}); + if is_tail + allowed_abs=max(opts.TailAbsDiffThreshold_dB,opts.TailRelDiffThreshold*max(abs(v11),1)); + else + allowed_abs=max(opts.AbsDiffThreshold_dB,opts.RelDiffThreshold*max(abs(v11),1)); + end + + diff_table.(m)=struct( ... + 'rev11',v11, ... + 'rev14',v14, ... + 'abs_diff',abs_diff, ... + 'rel_diff',rel_diff, ... + 'allowed_abs',allowed_abs, ... + 'pass',abs_diff<=allowed_abs, ... + 'is_tail_metric',is_tail); + + pass_flags(k)=diff_table.(m).pass; +end + +tail_fields=intersect({'p95','p99','p99_9'},metrics,'stable'); +tail_check=struct(); +tail_pass=true; +for k=1:numel(tail_fields) + tf=tail_fields{k}; + tail_check.(tf)=struct( ... + 'abs_diff',diff_table.(tf).abs_diff, ... + 'allowed_abs',diff_table.(tf).allowed_abs, ... + 'pass',diff_table.(tf).pass); + tail_pass=tail_pass && tail_check.(tf).pass; +end + +overall_pass=all(pass_flags) && tail_pass; + +fprintf('\nRuntime (seconds)\n'); +fprintf(' rev11 runs: [%s]\n',num_vec_to_text(runtime11)); +fprintf(' rev14 runs: [%s]\n',num_vec_to_text(runtime14)); +fprintf(' rev11 mean: %.6f s\n',mean(runtime11)); +fprintf(' rev14 mean: %.6f s\n',mean(runtime14)); +fprintf(' speedup rev11/rev14 (mean): %.3fx\n',mean(runtime11)/mean(runtime14)); + +fprintf('\nMetric comparison (rev14 - rev11):\n'); +for k=1:numel(metrics) + m=metrics{k}; + fprintf(' %-7s | rev11=%10.4f | rev14=%10.4f | abs=%.4f | allow=%.4f | %s\n', ... + m,diff_table.(m).rev11,diff_table.(m).rev14,diff_table.(m).abs_diff, ... + diff_table.(m).allowed_abs,passfail(diff_table.(m).pass)); +end + +fprintf('\nUpper-tail checks:\n'); +for k=1:numel(tail_fields) + tf=tail_fields{k}; + fprintf(' %-7s | abs=%.4f | allow=%.4f | %s\n',tf,tail_check.(tf).abs_diff, ... + tail_check.(tf).allowed_abs,passfail(tail_check.(tf).pass)); +end + +if overall_pass + fprintf('\nPASS: rev14 is statistically equivalent to rev11 under configured thresholds.\n'); +else + fprintf('\nFAIL: rev14 drift exceeded configured thresholds (fail-closed).\n'); + error('validate_subchunk_agg_check_maxazi_rev11_rev14_statistical:DriftExceeded', ... + 'Fail-closed: statistical drift exceeded configured thresholds.'); +end + +results=struct(); +results.options=opts; +results.runtime_rev11_s=runtime11; +results.runtime_rev14_s=runtime14; +results.speedup_rev11_over_rev14=speedup_runs; +results.speedup_mean_rev11_over_rev14=mean(runtime11)/mean(runtime14); +results.n_samples=numel(x11); +results.metrics=metrics; +results.summary_rev11=s11; +results.summary_rev14=s14; +results.diffs=diff_table; +results.upper_tail=tail_check; +results.pass=overall_pass; + +end + +function s=build_summary(x,q,enable_p999) +s=struct(); +s.mean=mean(x,'omitnan'); +s.std=std(x,0,'omitnan'); +s.min=min(x,[],'omitnan'); +s.max=max(x,[],'omitnan'); +s.median=median(x,'omitnan'); +qx=quantile(x,q); +s.p90=qx(1); +s.p95=qx(2); +s.p99=qx(3); +if enable_p999 && numel(x)>=1000 + s.p99_9=quantile(x,0.999); +end +end + +function txt=passfail(tf) +if tf + txt='PASS'; +else + txt='FAIL'; +end +end + +function txt=num_vec_to_text(v) +txt=sprintf('%.6f ',v); +txt=strtrim(txt); +end