|
| 1 | +-- Annotation Manager Volume Benchmark |
| 2 | +-- 1000 generated packages, cold cache per iteration |
| 3 | + |
| 4 | +set serveroutput on size unlimited |
| 5 | +set timing off |
| 6 | +set feedback off |
| 7 | + |
| 8 | +declare |
| 9 | + c_packages constant pls_integer := 1000; |
| 10 | + c_procs_per_pkg constant pls_integer := 20; |
| 11 | + c_contexts_per_pkg constant pls_integer := 4; |
| 12 | + c_pkg_prefix constant varchar2(20) := 'TST_GEN_PKG_'; |
| 13 | + c_iterations constant pls_integer := 4; |
| 14 | + c_perf_threshold constant number := 0.80; |
| 15 | + |
| 16 | + l_baseline_total_ms number := 0; |
| 17 | + l_new_total_ms number := 0; |
| 18 | + l_start timestamp; |
| 19 | + l_elapsed interval day to second; |
| 20 | + |
| 21 | + -- Source holders |
| 22 | + l_source_lines dbms_preprocessor.source_lines_t; |
| 23 | + l_source_clob clob; |
| 24 | + l_result ut_annotations; |
| 25 | + |
| 26 | + -- Correctness tracking |
| 27 | + l_missing_from_cache pls_integer := 0; |
| 28 | + l_count_mismatches pls_integer := 0; |
| 29 | + l_mismatch_detail varchar2(32767); |
| 30 | + |
| 31 | + -- Cache check |
| 32 | + l_cursor sys_refcursor; |
| 33 | + l_annotated_obj ut_annotated_object; |
| 34 | + l_name varchar2(128); |
| 35 | + l_annotations ut_annotations; |
| 36 | + l_cache_count pls_integer := 0; |
| 37 | + |
| 38 | + -- Per-package timing |
| 39 | + l_pkg_baseline_ms number; |
| 40 | + l_pkg_new_ms number; |
| 41 | + l_pkg varchar2(30); |
| 42 | + l_ratio number; |
| 43 | + |
| 44 | + -- Expected annotation count — mirrors generator logic exactly |
| 45 | + function expected_annotation_count( |
| 46 | + a_pkg_number pls_integer, |
| 47 | + a_procs pls_integer, |
| 48 | + a_contexts pls_integer |
| 49 | + ) return pls_integer is |
| 50 | + l_count pls_integer := 0; |
| 51 | + begin |
| 52 | + l_count := l_count + 2; -- %suite + %suitepath |
| 53 | + l_count := l_count + 2; -- %beforeall + %afterall |
| 54 | + l_count := l_count + 2; -- %beforeeach + %aftereach |
| 55 | + |
| 56 | + -- package level conditional |
| 57 | + if mod(a_pkg_number, 3) = 0 then l_count := l_count + 1; end if; -- %displayname |
| 58 | + if mod(a_pkg_number, 5) = 0 then l_count := l_count + 1; end if; -- %rollback |
| 59 | + |
| 60 | + -- contexts: each contributes %context + %endcontext |
| 61 | + l_count := l_count + (a_contexts * 2); |
| 62 | + |
| 63 | + -- per procedure |
| 64 | + for i in 1 .. a_procs loop |
| 65 | + l_count := l_count + 1; -- %test |
| 66 | + if mod(i, 3) = 0 then l_count := l_count + 1; end if; -- %tags |
| 67 | + if mod(i, 6) = 0 then l_count := l_count + 1; end if; -- %throws |
| 68 | + if mod(i, 8) = 0 then l_count := l_count + 1; end if; -- %disabled |
| 69 | + if mod(i, 5) = 0 then l_count := l_count + 2; end if; -- %beforetest + %aftertest |
| 70 | + end loop; |
| 71 | + |
| 72 | + return l_count; |
| 73 | + end expected_annotation_count; |
| 74 | + |
| 75 | +begin |
| 76 | + dbms_output.put_line('Packages : ' || c_packages); |
| 77 | + dbms_output.put_line('Procs/pkg : ' || c_procs_per_pkg); |
| 78 | + dbms_output.put_line('Contexts/pkg: ' || c_contexts_per_pkg); |
| 79 | + dbms_output.put_line('Iterations : ' || c_iterations); |
| 80 | + dbms_output.put_line('Started : ' || to_char(systimestamp,'YYYY-MM-DD HH24:MI:SS.FF3')); |
| 81 | + |
| 82 | + -- Phase 1: Performance benchmark (cold cache per package per iteration) |
| 83 | + dbms_output.put_line(chr(10) || '-- Phase 1: Performance --'); |
| 84 | + dbms_output.put_line( |
| 85 | + rpad('package', 20) || ' | ' || |
| 86 | + lpad('baseline_ms', 11) || ' | ' || |
| 87 | + lpad('new_ms', 6) || ' | ' || |
| 88 | + lpad('ratio', 5) || ' | ' || |
| 89 | + 'status' |
| 90 | + ); |
| 91 | + dbms_output.put_line(rpad('-', 70, '-')); |
| 92 | + |
| 93 | + for p in 1 .. c_packages loop |
| 94 | + l_pkg := c_pkg_prefix || lpad(p, 4, '0'); |
| 95 | + |
| 96 | + -- load source once per package |
| 97 | + l_source_lines := dbms_preprocessor.get_post_processed_source( |
| 98 | + object_type => 'PACKAGE', |
| 99 | + schema_name => user, |
| 100 | + object_name => l_pkg |
| 101 | + ); |
| 102 | + |
| 103 | + dbms_lob.createtemporary(l_source_clob, true); |
| 104 | + for i in 1 .. l_source_lines.count loop |
| 105 | + dbms_lob.writeappend(l_source_clob, length(l_source_lines(i)), l_source_lines(i)); |
| 106 | + end loop; |
| 107 | + |
| 108 | + -- baseline: clob version |
| 109 | + l_pkg_baseline_ms := 0; |
| 110 | + for iter in 1 .. c_iterations loop |
| 111 | + ut_annotation_manager.purge_cache(user, 'PACKAGE'); |
| 112 | + l_start := systimestamp; |
| 113 | + l_result := ut_annotation_parser.parse_object_annotations(l_source_clob); |
| 114 | + l_elapsed := systimestamp - l_start; |
| 115 | + l_pkg_baseline_ms := l_pkg_baseline_ms + |
| 116 | + extract(second from l_elapsed) * 1000; |
| 117 | + end loop; |
| 118 | + l_pkg_baseline_ms := l_pkg_baseline_ms / c_iterations; |
| 119 | + |
| 120 | + -- new: source_lines_t version |
| 121 | + l_pkg_new_ms := 0; |
| 122 | + for iter in 1 .. c_iterations loop |
| 123 | + ut_annotation_manager.purge_cache(user, 'PACKAGE'); |
| 124 | + l_start := systimestamp; |
| 125 | + l_result := ut_annotation_parser.parse_object_annotations(l_source_lines); |
| 126 | + l_elapsed := systimestamp - l_start; |
| 127 | + l_pkg_new_ms := l_pkg_new_ms + |
| 128 | + extract(second from l_elapsed) * 1000; |
| 129 | + end loop; |
| 130 | + l_pkg_new_ms := l_pkg_new_ms / c_iterations; |
| 131 | + |
| 132 | + -- accumulate |
| 133 | + l_baseline_total_ms := l_baseline_total_ms + l_pkg_baseline_ms; |
| 134 | + l_new_total_ms := l_new_total_ms + l_pkg_new_ms; |
| 135 | + |
| 136 | + l_ratio := round(l_pkg_new_ms / nullif(l_pkg_baseline_ms, 0), 3); |
| 137 | + |
| 138 | + -- print every 100 packages to avoid flooding output |
| 139 | + if mod(p, 100) = 0 or p = 1 then |
| 140 | + dbms_output.put_line( |
| 141 | + rpad(l_pkg, 20) || ' | ' || |
| 142 | + lpad(round(l_pkg_baseline_ms, 3), 11) || ' | ' || |
| 143 | + lpad(round(l_pkg_new_ms, 3), 6) || ' | ' || |
| 144 | + lpad(l_ratio, 5) || ' | ' || |
| 145 | + case when l_ratio <= c_perf_threshold |
| 146 | + then 'PASS (' || round((1-l_ratio)*100,1) || '% faster)' |
| 147 | + else 'SLOW (ratio=' || l_ratio || ')' |
| 148 | + end |
| 149 | + ); |
| 150 | + end if; |
| 151 | + |
| 152 | + dbms_lob.freetemporary(l_source_clob); |
| 153 | + end loop; |
| 154 | + |
| 155 | + -- ------------------------------------------------------------------------- |
| 156 | + -- Phase 1 summary |
| 157 | + -- ------------------------------------------------------------------------- |
| 158 | + l_ratio := round(l_new_total_ms / nullif(l_baseline_total_ms, 0), 3); |
| 159 | + |
| 160 | + dbms_output.put_line(rpad('-', 70, '-')); |
| 161 | + dbms_output.put_line('Baseline total ms : ' || round(l_baseline_total_ms, 3)); |
| 162 | + dbms_output.put_line('New total ms : ' || round(l_new_total_ms, 3)); |
| 163 | + dbms_output.put_line('Overall ratio : ' || l_ratio); |
| 164 | + dbms_output.put_line('Improvement : ' || round((1 - l_ratio) * 100, 1) || '%'); |
| 165 | + |
| 166 | + -- Phase 2: Cache presence check |
| 167 | + dbms_output.put_line(chr(10) || '-- Phase 2: Cache presence --'); |
| 168 | + |
| 169 | + ut_annotation_manager.purge_cache(user, 'PACKAGE'); |
| 170 | + ut_annotation_manager.rebuild_annotation_cache(user, 'PACKAGE'); |
| 171 | + |
| 172 | + -- query cache tables directly to avoid validate_annotation_cache side effects |
| 173 | + open l_cursor for |
| 174 | + select ut_annotated_object( |
| 175 | + i.object_owner, i.object_name, i.object_type, i.parse_time, |
| 176 | + cast( |
| 177 | + collect( |
| 178 | + ut_annotation( |
| 179 | + c.annotation_position, c.annotation_name, |
| 180 | + c.annotation_text, c.subobject_name |
| 181 | + ) order by c.annotation_position |
| 182 | + ) as ut_annotations |
| 183 | + ) |
| 184 | + ) |
| 185 | + from ut_annotation_cache_info i |
| 186 | + join ut_annotation_cache c on i.cache_id = c.cache_id |
| 187 | + where i.object_owner = user |
| 188 | + and i.object_type = 'PACKAGE' |
| 189 | + and i.object_name like c_pkg_prefix || '%' |
| 190 | + group by i.object_owner, i.object_type, i.object_name, i.parse_time; |
| 191 | + |
| 192 | + loop |
| 193 | + fetch l_cursor into l_annotated_obj; |
| 194 | + exit when l_cursor%notfound; |
| 195 | + l_cache_count := l_cache_count + 1; |
| 196 | + end loop; |
| 197 | + close l_cursor; |
| 198 | + |
| 199 | + dbms_output.put_line('Expected in cache : ' || c_packages); |
| 200 | + dbms_output.put_line('Found in cache : ' || l_cache_count); |
| 201 | + dbms_output.put_line('Cache check : ' || |
| 202 | + case when l_cache_count = c_packages |
| 203 | + then 'PASS — all ' || c_packages || ' packages found in cache' |
| 204 | + else 'FAIL — missing ' || (c_packages - l_cache_count) || ' packages from cache' |
| 205 | + end |
| 206 | + ); |
| 207 | + |
| 208 | + -- Phase 3: Annotation count correctness |
| 209 | + dbms_output.put_line(chr(10) || '-- Phase 3: Annotation count correctness --'); |
| 210 | + |
| 211 | + -- reopen same direct cache query for correctness check |
| 212 | + open l_cursor for |
| 213 | + select ut_annotated_object( |
| 214 | + i.object_owner, i.object_name, i.object_type, i.parse_time, |
| 215 | + cast( |
| 216 | + collect( |
| 217 | + ut_annotation( |
| 218 | + c.annotation_position, c.annotation_name, |
| 219 | + c.annotation_text, c.subobject_name |
| 220 | + ) order by c.annotation_position |
| 221 | + ) as ut_annotations |
| 222 | + ) |
| 223 | + ) |
| 224 | + from ut_annotation_cache_info i |
| 225 | + join ut_annotation_cache c on i.cache_id = c.cache_id |
| 226 | + where i.object_owner = user |
| 227 | + and i.object_type = 'PACKAGE' |
| 228 | + and i.object_name like c_pkg_prefix || '%' |
| 229 | + group by i.object_owner, i.object_type, i.object_name, i.parse_time; |
| 230 | + |
| 231 | + loop |
| 232 | + fetch l_cursor into l_annotated_obj; |
| 233 | + exit when l_cursor%notfound; |
| 234 | + l_name := l_annotated_obj.object_name; |
| 235 | + l_annotations := l_annotated_obj.annotations; |
| 236 | + |
| 237 | + declare |
| 238 | + l_pkg_number pls_integer; |
| 239 | + l_expected pls_integer; |
| 240 | + begin |
| 241 | + l_pkg_number := to_number(substr(l_name, length(c_pkg_prefix) + 1)); |
| 242 | + l_expected := expected_annotation_count( |
| 243 | + l_pkg_number, |
| 244 | + c_procs_per_pkg, |
| 245 | + c_contexts_per_pkg |
| 246 | + ); |
| 247 | + |
| 248 | + if l_annotations.count != l_expected then |
| 249 | + l_count_mismatches := l_count_mismatches + 1; |
| 250 | + if l_count_mismatches <= 10 then |
| 251 | + l_mismatch_detail := l_mismatch_detail || chr(10) || |
| 252 | + ' ' || l_name || |
| 253 | + ': expected=' || l_expected || |
| 254 | + ' actual=' || l_annotations.count; |
| 255 | + end if; |
| 256 | + end if; |
| 257 | + end; |
| 258 | + end loop; |
| 259 | + close l_cursor; |
| 260 | + |
| 261 | + dbms_output.put_line('Packages checked : ' || c_packages); |
| 262 | + dbms_output.put_line('Count mismatches : ' || l_count_mismatches); |
| 263 | + if l_count_mismatches > 0 then |
| 264 | + dbms_output.put_line('Mismatch detail (first 10):' || l_mismatch_detail); |
| 265 | + end if; |
| 266 | + dbms_output.put_line('Count check : ' || |
| 267 | + case when l_count_mismatches = 0 |
| 268 | + then 'PASS — all annotation counts match expected' |
| 269 | + else 'FAIL — ' || l_count_mismatches || ' packages have wrong annotation count' |
| 270 | + end |
| 271 | + ); |
| 272 | + |
| 273 | + dbms_output.put_line(chr(10) || '================================================='); |
| 274 | + dbms_output.put_line('FINAL RESULTS'); |
| 275 | + dbms_output.put_line('================================================='); |
| 276 | + dbms_output.put_line('Performance : ' || |
| 277 | + case when l_ratio <= c_perf_threshold then 'PASS' else 'FAIL' end); |
| 278 | + dbms_output.put_line('Cache : ' || |
| 279 | + case when l_cache_count = c_packages then 'PASS' else 'FAIL' end); |
| 280 | + dbms_output.put_line('Counts : ' || |
| 281 | + case when l_count_mismatches = 0 then 'PASS' else 'FAIL' end); |
| 282 | + dbms_output.put_line('Overall : ' || |
| 283 | + case when l_ratio <= c_perf_threshold |
| 284 | + and l_cache_count = c_packages |
| 285 | + and l_count_mismatches = 0 |
| 286 | + then 'PASS' |
| 287 | + else 'FAIL' |
| 288 | + end |
| 289 | + ); |
| 290 | + dbms_output.put_line('Finished : ' || |
| 291 | + to_char(systimestamp, 'YYYY-MM-DD HH24:MI:SS.FF3')); |
| 292 | + dbms_output.put_line('================================================='); |
| 293 | + |
| 294 | + -- cleanup |
| 295 | + ut_annotation_manager.purge_cache(user, 'PACKAGE'); |
| 296 | + |
| 297 | +end; |
| 298 | +/ |
0 commit comments