ecosystem_speed.sh (21712B)
1 #!/usr/bin/env bash 2 # Compare ecosystem build-phase speed across kit, clang, and tcc. 3 # 4 # Phases are measured separately: 5 # compile: compile library TUs plus the driver TU to objects. 6 # archive: archive the kit-compiled library objects with kit ar vs llvm-ar. 7 # link: link precompiled driver object plus static library into an app. 8 # 9 # Correctness remains owned by test/ecosystem/run.sh; this script is a timing 10 # report over the same pinned project recipes. 11 12 set -euo pipefail 13 14 SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) 15 ROOT=$(cd "$SCRIPT_DIR/.." && pwd) 16 SELF="$ROOT/scripts/ecosystem_speed.sh" 17 ECO_DIR="$ROOT/test/ecosystem" 18 RECIPE_DIR="$ECO_DIR/recipes" 19 ECOSYS="$ROOT/scripts/ecosystem.sh" 20 21 KIT="${KIT:-$ROOT/build/release/kit}" 22 CLANG="${CLANG:-clang}" 23 TCC="${TCC:-tcc}" 24 HOST_AR="${AR:-llvm-ar}" 25 RUNS="${ECO_SPEED_RUNS:-${RUNS:-5}}" 26 TIMEOUT_S="${ECO_SPEED_TIMEOUT:-${TIMEOUT:-20}}" 27 OPTS="${ECO_SPEED_OPTS:-O0 O1}" 28 OUT_ROOT="${ECO_SPEED_OUT:-$ROOT/build/ecosystem-speed}" 29 QUIET="${ECO_SPEED_QUIET:-0}" 30 31 die() { 32 printf 'ecosystem-speed: %s\n' "$*" >&2 33 exit 2 34 } 35 36 progress() { 37 [ "$QUIET" = 1 ] && return 0 38 printf 'ecosystem-speed: %s\n' "$*" >&2 39 } 40 41 usage() { 42 cat <<EOF 43 usage: scripts/ecosystem_speed.sh [project ...] 44 45 env: 46 KIT kit binary (default: build/release/kit) 47 CLANG clang binary (default: clang) 48 TCC tcc binary (default: tcc; skipped if unavailable) 49 AR host archive binary (default: llvm-ar) 50 ECO_SPEED_RUNS best-of-N runs per row (default: 5) 51 ECO_SPEED_TIMEOUT phase timeout seconds per run (default: 20) 52 ECO_SPEED_OPTS opt levels, e.g. "O0 O1" (default: "O0 O1") 53 ECO_SPEED_PROJECTS project list if no argv is passed 54 ECO_SPEED_OUT output dir for logs/results (default: build/ecosystem-speed) 55 ECO_SPEED_QUIET set to 1 to suppress progress lines 56 EOF 57 } 58 59 recipe_reset() { 60 ECO_VERSION= 61 ECO_URL= 62 ECO_SHA256= 63 ECO_SRCDIR=. 64 ECO_INCLUDES="." 65 ECO_DEFINES= 66 ECO_LIBS= 67 ECO_DRIVER= 68 ECO_BUG= 69 eco_lib_srcs() { :; } 70 eco_run() { :; } 71 } 72 73 load_recipe() { 74 local name=$1 75 local recipe="$RECIPE_DIR/$name.recipe" 76 [ -f "$recipe" ] || die "unknown project '$name' (see: scripts/ecosystem.sh list)" 77 recipe_reset 78 # shellcheck disable=SC1090 79 . "$recipe" 80 } 81 82 srcdir_for() { 83 "$ECOSYS" srcdir "$1" 2>/dev/null || true 84 } 85 86 driver_for() { 87 case "$ECO_DRIVER" in 88 use/*) printf '%s/%s\n' "$ECO_DIR" "$ECO_DRIVER" ;; 89 *) printf '%s/%s\n' "$ECO_SRC" "$ECO_DRIVER" ;; 90 esac 91 } 92 93 host_sdk() { 94 case "$(uname -s)" in 95 Darwin) xcrun --sdk macosx --show-sdk-path 2>/dev/null || true ;; 96 *) printf '\n' ;; 97 esac 98 } 99 100 append_sysroot_args() { 101 local compiler=$1 102 local sdk=$2 103 case "$(uname -s)" in 104 Darwin) 105 [ -n "$sdk" ] || return 0 106 case "$compiler" in 107 kit) printf '%s\n' --sysroot "$sdk" ;; 108 clang) printf '%s\n' -isysroot "$sdk" ;; 109 tcc) printf '%s\n' -isysroot "$sdk" ;; 110 esac 111 ;; 112 esac 113 } 114 115 quote_cmd() { 116 local first=1 117 for arg in "$@"; do 118 if [ "$first" = 0 ]; then printf ' '; fi 119 first=0 120 printf '%q' "$arg" 121 done 122 printf '\n' 123 } 124 125 compiler_available() { 126 local compiler=$1 127 case "$compiler" in 128 kit) [ -x "$KIT" ] ;; 129 clang) command -v "$CLANG" >/dev/null 2>&1 ;; 130 tcc) 131 if [ -x "$TCC" ]; then return 0; fi 132 command -v "$TCC" >/dev/null 2>&1 133 ;; 134 *) return 1 ;; 135 esac 136 } 137 138 archiver_available() { 139 local archiver=$1 140 case "$archiver" in 141 kit) [ -x "$KIT" ] ;; 142 host) command -v "$HOST_AR" >/dev/null 2>&1 ;; 143 *) return 1 ;; 144 esac 145 } 146 147 compile_one() { 148 local compiler=$1 149 local opt=$2 150 local project=$3 151 local out_dir=$4 152 local src_list=$5 153 local sdk=$6 154 155 load_recipe "$project" 156 ECO_SRC="$(srcdir_for "$project")" 157 export ECO_SRC ECO_DIR 158 [ -n "$ECO_SRC" ] && [ -d "$ECO_SRC" ] || die "$project is not provisioned" 159 160 mkdir -p "$out_dir/obj" 161 : > "$out_dir/build.err" 162 : > "$out_dir/cmds.sh" 163 : > "$out_dir/objects" 164 : > "$out_dir/lib_objs" 165 : > "$out_dir/driver_obj" 166 167 local inc_args=() 168 local def_args=() 169 local sys_args=() 170 local inc def 171 for inc in $ECO_INCLUDES; do inc_args+=("-I$ECO_SRC/$inc"); done 172 for def in $ECO_DEFINES; do def_args+=("$def"); done 173 while IFS= read -r a; do sys_args+=("$a"); done \ 174 < <(append_sysroot_args "$compiler" "$sdk") 175 176 local total=0 177 total=$(awk 'NF {n++} END {print n + 0}' "$src_list") 178 [ "$total" -gt 0 ] || die "$project has no sources" 179 180 local idx=0 181 local src obj 182 while IFS= read -r src; do 183 [ -n "$src" ] || continue 184 idx=$((idx + 1)) 185 obj="$out_dir/obj/$idx.o" 186 187 local cmd=() 188 case "$compiler" in 189 kit) 190 cmd=("$KIT" cc "-$opt" "${sys_args[@]}" "${def_args[@]}" 191 "${inc_args[@]}" -c "$src" -o "$obj") 192 ;; 193 clang) 194 cmd=("$CLANG" "-$opt" "${sys_args[@]}" "${def_args[@]}" 195 "${inc_args[@]}" -c "$src" -o "$obj") 196 ;; 197 tcc) 198 cmd=("$TCC" "-$opt" "${sys_args[@]}" "${def_args[@]}" 199 "${inc_args[@]}" -c "$src" -o "$obj") 200 ;; 201 *) die "unknown compiler '$compiler'" ;; 202 esac 203 204 quote_cmd "${cmd[@]}" >> "$out_dir/cmds.sh" 205 if ! "${cmd[@]}" 2>>"$out_dir/build.err"; then 206 return 1 207 fi 208 printf '%s\n' "$obj" >> "$out_dir/objects" 209 if [ "$idx" -lt "$total" ]; then 210 printf '%s\n' "$obj" >> "$out_dir/lib_objs" 211 else 212 printf '%s\n' "$obj" > "$out_dir/driver_obj" 213 fi 214 done < "$src_list" 215 } 216 217 archive_one() { 218 local archiver=$1 219 local out_dir=$2 220 local obj_list=$3 221 222 mkdir -p "$out_dir" 223 : > "$out_dir/build.err" 224 : > "$out_dir/cmds.sh" 225 226 local objs=() 227 local obj 228 while IFS= read -r obj; do 229 [ -n "$obj" ] || continue 230 objs+=("$obj") 231 done < "$obj_list" 232 [ "${#objs[@]}" -gt 0 ] || die "archive has no objects: $obj_list" 233 234 local cmd=() 235 case "$archiver" in 236 kit) cmd=("$KIT" ar rcs "$out_dir/lib.a" "${objs[@]}") ;; 237 host) cmd=("$HOST_AR" rcs "$out_dir/lib.a" "${objs[@]}") ;; 238 *) die "unknown archiver '$archiver'" ;; 239 esac 240 quote_cmd "${cmd[@]}" >> "$out_dir/cmds.sh" 241 "${cmd[@]}" 2>>"$out_dir/build.err" 242 } 243 244 link_one() { 245 local compiler=$1 246 local opt=$2 247 local project=$3 248 local out_dir=$4 249 local fixture_dir=$5 250 local sdk=$6 251 252 load_recipe "$project" 253 ECO_SRC="$(srcdir_for "$project")" 254 export ECO_SRC ECO_DIR 255 [ -n "$ECO_SRC" ] && [ -d "$ECO_SRC" ] || die "$project is not provisioned" 256 257 mkdir -p "$out_dir" 258 : > "$out_dir/build.err" 259 : > "$out_dir/cmds.sh" 260 261 local driver_obj lib 262 driver_obj=$(cat "$fixture_dir/driver_obj") 263 lib="$fixture_dir/lib.a" 264 [ -f "$driver_obj" ] || die "missing driver object: $driver_obj" 265 [ -f "$lib" ] || die "missing archive: $lib" 266 267 local sys_args=() 268 local lib_args=() 269 local a 270 while IFS= read -r a; do sys_args+=("$a"); done \ 271 < <(append_sysroot_args "$compiler" "$sdk") 272 for a in $ECO_LIBS; do lib_args+=("$a"); done 273 274 local cmd=() 275 case "$compiler" in 276 kit) 277 cmd=("$KIT" cc "-$opt" "${sys_args[@]}" "$driver_obj" "$lib" 278 "${lib_args[@]}" -lc -o "$out_dir/app") 279 ;; 280 clang) 281 cmd=("$CLANG" "-$opt" "${sys_args[@]}" "$driver_obj" "$lib" 282 "${lib_args[@]}" -o "$out_dir/app") 283 ;; 284 tcc) 285 cmd=("$TCC" "-$opt" "${sys_args[@]}" "$driver_obj" "$lib" 286 "${lib_args[@]}" -o "$out_dir/app") 287 ;; 288 *) die "unknown compiler '$compiler'" ;; 289 esac 290 quote_cmd "${cmd[@]}" >> "$out_dir/cmds.sh" 291 "${cmd[@]}" 2>>"$out_dir/build.err" 292 } 293 294 run_with_timeout() { 295 local timeout_s=$1 296 shift 297 perl -MTime::HiRes=time,sleep -MPOSIX=':sys_wait_h,setpgid' -e ' 298 my ($timeout, @cmd) = @ARGV; 299 my $start = time; 300 my $pid = fork(); 301 die "fork failed\n" unless defined $pid; 302 if ($pid == 0) { 303 setpgid(0, 0); 304 exec @cmd; 305 print STDERR "exec failed: $!\n"; 306 exit 127; 307 } 308 setpgid($pid, $pid); 309 my $timed_out = 0; 310 my $status; 311 while (1) { 312 my $r = waitpid($pid, WNOHANG); 313 if ($r == $pid) { 314 $status = $?; 315 last; 316 } 317 if (time - $start >= $timeout) { 318 $timed_out = 1; 319 kill "TERM", -$pid; 320 my $until = time + 2.0; 321 while (time < $until) { 322 $r = waitpid($pid, WNOHANG); 323 if ($r == $pid) { 324 $status = $?; 325 last; 326 } 327 sleep 0.02; 328 } 329 if (!defined $status) { 330 kill "KILL", -$pid; 331 waitpid($pid, 0); 332 $status = $?; 333 } 334 last; 335 } 336 sleep 0.01; 337 } 338 my $elapsed = time - $start; 339 my $rc; 340 if ($timed_out) { 341 $rc = 124; 342 } elsif ($status & 127) { 343 $rc = 128 + ($status & 127); 344 } else { 345 $rc = ($status >> 8) & 255; 346 } 347 printf "elapsed %.6f\nrc %d\n", $elapsed, $rc; 348 exit $rc; 349 ' "$timeout_s" "$@" 350 } 351 352 timed_phase() { 353 local phase=$1 354 local tool=$2 355 local run_dir=$3 356 shift 3 357 358 mkdir -p "$run_dir" 359 local cmd=() 360 case "$phase" in 361 compile) 362 [ $# -eq 4 ] || die "compile phase expects opt project src_list sdk" 363 cmd=(bash "$SELF" --compile-one "$tool" "$1" "$2" "$run_dir" "$3" "$4") 364 ;; 365 archive) 366 [ $# -eq 1 ] || die "archive phase expects obj_list" 367 cmd=(bash "$SELF" --archive-one "$tool" "$run_dir" "$1") 368 ;; 369 link) 370 [ $# -eq 4 ] || die "link phase expects opt project fixture_dir sdk" 371 cmd=(bash "$SELF" --link-one "$tool" "$1" "$2" "$run_dir" "$3" "$4") 372 ;; 373 *) die "unknown phase '$phase'" ;; 374 esac 375 set +e 376 run_with_timeout "$TIMEOUT_S" "${cmd[@]}" \ 377 > "$run_dir/time.txt" 2> "$run_dir/time.err" 378 local rc=$? 379 set -e 380 381 local elapsed 382 elapsed=$(awk '/^elapsed / {print $2}' "$run_dir/time.txt" | tail -1) 383 [ -n "$elapsed" ] || elapsed=0 384 printf '%s,%s\n' "$rc" "$elapsed" 385 } 386 387 record_best() { 388 local phase=$1 389 local tool=$2 390 local project=$3 391 local opt=$4 392 local row_dir=$5 393 shift 5 394 395 local best="" 396 local best_rc=125 397 local run 398 for run in $(seq 1 "$RUNS"); do 399 progress "$project $opt $phase $tool run $run/$RUNS" 400 local run_dir="$row_dir/$phase-$tool-$run" 401 local got rc elapsed 402 got=$(timed_phase "$phase" "$tool" "$run_dir" "$@") 403 rc=${got%,*} 404 elapsed=${got#*,} 405 printf '%s,%s,%s,%s,%s,%s,%s\n' "$project" "$opt" "$phase" "$tool" \ 406 "$run" "$rc" "$elapsed" >> "$OUT_ROOT/runs.csv" 407 if [ "$rc" -eq 0 ]; then 408 local ms 409 ms=$(awk -v s="$elapsed" 'BEGIN { printf "%.3f", s * 1000.0 }') 410 if [ -z "$best" ] || awk -v a="$ms" -v b="$best" 'BEGIN { exit !(a < b) }'; then 411 best=$ms 412 best_rc=0 413 fi 414 else 415 best_rc=$rc 416 fi 417 done 418 419 if [ -n "$best" ]; then 420 printf '%s\n' "$best" 421 elif [ "$best_rc" -eq 124 ]; then 422 printf 'timeout\n' 423 else 424 printf 'fail\n' 425 fi 426 } 427 428 best_compile_ms_for() { 429 local compiler=$1 430 local project=$2 431 local opt=$3 432 local src_list=$4 433 local sdk=$5 434 local row_dir=$6 435 436 if ! compiler_available "$compiler"; then 437 printf 'missing\n' 438 return 0 439 fi 440 record_best compile "$compiler" "$project" "$opt" "$row_dir" \ 441 "$opt" "$project" "$src_list" "$sdk" 442 } 443 444 best_archive_ms_for() { 445 local archiver=$1 446 local project=$2 447 local opt=$3 448 local fixture_dir=$4 449 local row_dir=$5 450 451 if ! archiver_available "$archiver"; then 452 printf 'missing\n' 453 return 0 454 fi 455 [ -f "$fixture_dir/lib_objs" ] || { printf 'fail\n'; return 0; } 456 record_best archive "$archiver" "$project" "$opt" "$row_dir" \ 457 "$fixture_dir/lib_objs" 458 } 459 460 best_link_ms_for() { 461 local compiler=$1 462 local project=$2 463 local opt=$3 464 local fixture_dir=$4 465 local sdk=$5 466 local row_dir=$6 467 468 if ! compiler_available "$compiler"; then 469 printf 'missing\n' 470 return 0 471 fi 472 [ -f "$fixture_dir/lib.a" ] || { printf 'fail\n'; return 0; } 473 record_best link "$compiler" "$project" "$opt" "$row_dir" \ 474 "$opt" "$project" "$fixture_dir" "$sdk" 475 } 476 477 make_fixture() { 478 local compiler=$1 479 local project=$2 480 local opt=$3 481 local src_list=$4 482 local sdk=$5 483 local fixture_dir=$6 484 485 if ! compiler_available "$compiler"; then 486 return 1 487 fi 488 rm -rf "$fixture_dir" 489 mkdir -p "$fixture_dir" 490 local got rc 491 got=$(timed_phase compile "$compiler" "$fixture_dir" \ 492 "$opt" "$project" "$src_list" "$sdk") 493 rc=${got%,*} 494 [ "$rc" -eq 0 ] || return 1 495 496 local archiver=host 497 [ "$compiler" = kit ] && archiver=kit 498 got=$(timed_phase archive "$archiver" "$fixture_dir/archive" \ 499 "$fixture_dir/lib_objs") 500 rc=${got%,*} 501 [ "$rc" -eq 0 ] || return 1 502 mv "$fixture_dir/archive/lib.a" "$fixture_dir/lib.a" 503 return 0 504 } 505 506 prepare_project() { 507 local project=$1 508 local out_dir=$2 509 local src_list="$out_dir/srcs" 510 local lib_src_list="$out_dir/lib_srcs" 511 load_recipe "$project" 512 ECO_SRC="$(srcdir_for "$project")" 513 export ECO_SRC ECO_DIR 514 if [ -z "$ECO_SRC" ] || [ ! -d "$ECO_SRC" ]; then 515 return 1 516 fi 517 mkdir -p "$out_dir" 518 eco_lib_srcs > "$lib_src_list" 519 cp "$lib_src_list" "$src_list" 520 driver_for >> "$src_list" 521 return 0 522 } 523 524 print_toolchain_phase() { 525 local results=$1 526 local phase=$2 527 local units_label=$3 528 529 awk -F, -v phase_name="$phase" -v units_label="$units_label" \ 530 -v opts_list="$OPTS" ' 531 function isnum(x) { return x ~ /^[0-9]+([.][0-9]+)?$/ } 532 function cell(x) { return isnum(x) ? sprintf("%8.1f", x) : sprintf("%8s", x) } 533 function speed(ref, kit) { 534 return (isnum(ref) && isnum(kit) && kit > 0) ? sprintf("%7.3fx", ref / kit) : sprintf("%7s", "-") 535 } 536 function add_geo(tag, ref, kit) { 537 if (isnum(ref) && isnum(kit) && kit > 0 && ref > 0) { 538 geo_sum[tag] += log(ref / kit) 539 geo_n[tag]++ 540 } 541 } 542 BEGIN { 543 nopts = split(opts_list, opts, /[ \t]+/) 544 printf "\n[%s]\n", phase_name 545 printf "%-10s %-3s %4s %8s %8s %7s %8s %7s\n", \ 546 "project", "opt", units_label, "kit_ms", "clang_ms", "k/clang", "tcc_ms", "k/tcc" 547 printf "%-10s %-3s %4s %8s %8s %7s %8s %7s\n", \ 548 "----------", "---", "----", "--------", "--------", "-------", "--------", "-------" 549 } 550 NR == 1 { next } 551 $1 == phase_name { 552 opt=$3; units=$4; kit=$5; clang=$6; tcc=$7 553 printf "%-10s %-3s %4s %s %s %s %s %s\n", \ 554 $2, opt, units, cell(kit), cell(clang), speed(clang, kit), cell(tcc), speed(tcc, kit) 555 add_geo(opt ":clang", clang, kit) 556 add_geo(opt ":tcc", tcc, kit) 557 add_geo("all:clang", clang, kit) 558 add_geo("all:tcc", tcc, kit) 559 } 560 END { 561 printf "%-10s %-3s %4s %8s %8s %7s %8s %7s\n", \ 562 "----------", "---", "----", "--------", "--------", "-------", "--------", "-------" 563 for (i = 1; i <= nopts; ++i) { 564 opt = opts[i] 565 kc = opt ":clang" 566 kt = opt ":tcc" 567 cnt_clang = geo_n[kc] + 0 568 cnt_tcc = geo_n[kt] + 0 569 clang_geo = geo_n[kc] ? sprintf("%7.3fx", exp(geo_sum[kc] / geo_n[kc])) : sprintf("%7s", "-") 570 tcc_geo = geo_n[kt] ? sprintf("%7.3fx", exp(geo_sum[kt] / geo_n[kt])) : sprintf("%7s", "-") 571 printf "%-10s %-3s %4s %8s %8s %7s %8s %7s\n", \ 572 "geomean", opt, cnt_clang "/" cnt_tcc, "", "", clang_geo, "", tcc_geo 573 } 574 cnt_clang = geo_n["all:clang"] + 0 575 cnt_tcc = geo_n["all:tcc"] + 0 576 clang_geo = geo_n["all:clang"] ? sprintf("%7.3fx", exp(geo_sum["all:clang"] / geo_n["all:clang"])) : sprintf("%7s", "-") 577 tcc_geo = geo_n["all:tcc"] ? sprintf("%7.3fx", exp(geo_sum["all:tcc"] / geo_n["all:tcc"])) : sprintf("%7s", "-") 578 printf "%-10s %-3s %4s %8s %8s %7s %8s %7s\n", \ 579 "geomean", "all", cnt_clang "/" cnt_tcc, "", "", clang_geo, "", tcc_geo 580 } 581 ' "$results" 582 } 583 584 print_archive_phase() { 585 local results=$1 586 587 awk -F, -v opts_list="$OPTS" ' 588 function isnum(x) { return x ~ /^[0-9]+([.][0-9]+)?$/ } 589 function cell(x) { return isnum(x) ? sprintf("%8.1f", x) : sprintf("%8s", x) } 590 function speed(ref, kit) { 591 return (isnum(ref) && isnum(kit) && kit > 0) ? sprintf("%7.3fx", ref / kit) : sprintf("%7s", "-") 592 } 593 function add_geo(tag, ref, kit) { 594 if (isnum(ref) && isnum(kit) && kit > 0 && ref > 0) { 595 geo_sum[tag] += log(ref / kit) 596 geo_n[tag]++ 597 } 598 } 599 BEGIN { 600 nopts = split(opts_list, opts, /[ \t]+/) 601 printf "\n[archive]\n" 602 printf "%-10s %-3s %4s %8s %8s %7s\n", \ 603 "project", "opt", "objs", "kit_ar", "host_ar", "k/host" 604 printf "%-10s %-3s %4s %8s %8s %7s\n", \ 605 "----------", "---", "----", "--------", "--------", "-------" 606 } 607 NR == 1 { next } 608 $1 == "archive" { 609 opt=$3; units=$4; kit=$5; host=$8 610 printf "%-10s %-3s %4s %s %s %s\n", \ 611 $2, opt, units, cell(kit), cell(host), speed(host, kit) 612 add_geo(opt ":host", host, kit) 613 add_geo("all:host", host, kit) 614 } 615 END { 616 printf "%-10s %-3s %4s %8s %8s %7s\n", \ 617 "----------", "---", "----", "--------", "--------", "-------" 618 for (i = 1; i <= nopts; ++i) { 619 opt = opts[i] 620 key = opt ":host" 621 host_geo = geo_n[key] ? sprintf("%7.3fx", exp(geo_sum[key] / geo_n[key])) : sprintf("%7s", "-") 622 printf "%-10s %-3s %4s %8s %8s %7s\n", \ 623 "geomean", opt, geo_n[key], "", "", host_geo 624 } 625 host_geo = geo_n["all:host"] ? sprintf("%7.3fx", exp(geo_sum["all:host"] / geo_n["all:host"])) : sprintf("%7s", "-") 626 printf "%-10s %-3s %4s %8s %8s %7s\n", \ 627 "geomean", "all", geo_n["all:host"], "", "", host_geo 628 } 629 ' "$results" 630 } 631 632 print_report() { 633 local results=$1 634 printf 'ecosystem speed: best-of-%s, timeout=%ss\n' "$RUNS" "$TIMEOUT_S" 635 printf 'kit=%s\n' "$KIT" 636 printf 'clang=%s\n' "$CLANG" 637 if compiler_available tcc; then 638 printf 'tcc=%s\n' "$TCC" 639 else 640 printf 'tcc=%s (missing; rows skipped)\n' "$TCC" 641 fi 642 printf 'host_ar=%s\n' "$HOST_AR" 643 printf 'archive_input=kit-compiled library objects\n' 644 print_toolchain_phase "$results" compile srcs 645 print_archive_phase "$results" 646 print_toolchain_phase "$results" link apps 647 printf '\nlogs=%s\n' "$OUT_ROOT" 648 } 649 650 main() { 651 if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then 652 usage 653 exit 0 654 fi 655 case "${1:-}" in 656 --compile-one) 657 shift 658 [ $# -eq 6 ] || die "--compile-one expects compiler opt project out_dir src_list sdk" 659 compile_one "$@" 660 exit $? 661 ;; 662 --archive-one) 663 shift 664 [ $# -eq 3 ] || die "--archive-one expects archiver out_dir obj_list" 665 archive_one "$@" 666 exit $? 667 ;; 668 --link-one) 669 shift 670 [ $# -eq 6 ] || die "--link-one expects compiler opt project out_dir fixture_dir sdk" 671 link_one "$@" 672 exit $? 673 ;; 674 esac 675 676 [ -x "$KIT" ] || die "kit binary not found at $KIT (run: make RELEASE=1 bin)" 677 command -v "$CLANG" >/dev/null 2>&1 || die "clang not found: $CLANG" 678 command -v "$HOST_AR" >/dev/null 2>&1 || die "host ar not found: $HOST_AR" 679 680 mkdir -p "$OUT_ROOT" 681 printf 'project,opt,phase,tool,run,rc,seconds\n' > "$OUT_ROOT/runs.csv" 682 local results="$OUT_ROOT/results.csv" 683 printf 'phase,project,opt,units,kit_ms,clang_ms,tcc_ms,host_ms\n' > "$results" 684 685 local sdk 686 sdk="$(host_sdk)" 687 688 local projects 689 if [ "$#" -gt 0 ]; then 690 projects="$*" 691 elif [ -n "${ECO_SPEED_PROJECTS:-}" ]; then 692 projects="$ECO_SPEED_PROJECTS" 693 else 694 projects="$("$ECOSYS" list)" 695 fi 696 697 local any=0 698 local project opt 699 for project in $projects; do 700 local project_dir="$OUT_ROOT/$project" 701 if ! prepare_project "$project" "$project_dir"; then 702 printf 'ecosystem-speed: skip %-10s not provisioned (run: make provision-ecosystem)\n' "$project" >&2 703 continue 704 fi 705 any=1 706 707 local src_list="$project_dir/srcs" 708 local lib_src_list="$project_dir/lib_srcs" 709 local srcs lib_objs 710 srcs=$(awk 'NF {n++} END {print n + 0}' "$src_list") 711 lib_objs=$(awk 'NF {n++} END {print n + 0}' "$lib_src_list") 712 713 for opt in $OPTS; do 714 local row_dir="$project_dir/$opt" 715 mkdir -p "$row_dir" 716 717 local kit_compile clang_compile tcc_compile 718 progress "$project $opt compile" 719 kit_compile=$(best_compile_ms_for kit "$project" "$opt" "$src_list" "$sdk" "$row_dir") 720 clang_compile=$(best_compile_ms_for clang "$project" "$opt" "$src_list" "$sdk" "$row_dir") 721 tcc_compile=$(best_compile_ms_for tcc "$project" "$opt" "$src_list" "$sdk" "$row_dir") 722 printf 'compile,%s,%s,%s,%s,%s,%s,\n' "$project" "$opt" "$srcs" \ 723 "$kit_compile" "$clang_compile" "$tcc_compile" >> "$results" 724 725 local kit_fixture="$row_dir/fixture-kit" 726 local clang_fixture="$row_dir/fixture-clang" 727 local tcc_fixture="$row_dir/fixture-tcc" 728 progress "$project $opt fixtures" 729 make_fixture kit "$project" "$opt" "$src_list" "$sdk" "$kit_fixture" || true 730 make_fixture clang "$project" "$opt" "$src_list" "$sdk" "$clang_fixture" || true 731 make_fixture tcc "$project" "$opt" "$src_list" "$sdk" "$tcc_fixture" || true 732 733 local kit_ar host_ar 734 progress "$project $opt archive" 735 kit_ar=$(best_archive_ms_for kit "$project" "$opt" "$kit_fixture" "$row_dir") 736 host_ar=$(best_archive_ms_for host "$project" "$opt" "$kit_fixture" "$row_dir") 737 printf 'archive,%s,%s,%s,%s,,,%s\n' "$project" "$opt" "$lib_objs" \ 738 "$kit_ar" "$host_ar" >> "$results" 739 740 local kit_link clang_link tcc_link 741 progress "$project $opt link" 742 kit_link=$(best_link_ms_for kit "$project" "$opt" "$kit_fixture" "$sdk" "$row_dir") 743 clang_link=$(best_link_ms_for clang "$project" "$opt" "$clang_fixture" "$sdk" "$row_dir") 744 tcc_link=$(best_link_ms_for tcc "$project" "$opt" "$tcc_fixture" "$sdk" "$row_dir") 745 printf 'link,%s,%s,1,%s,%s,%s,\n' "$project" "$opt" \ 746 "$kit_link" "$clang_link" "$tcc_link" >> "$results" 747 done 748 done 749 750 [ "$any" = 1 ] || die "no provisioned ecosystem projects found" 751 print_report "$results" 752 } 753 754 main "$@"