#!/usr/bin/env bash # bench_full.sh — полный бенчмарк: все языки × все размеры + архивы # Языки: Python, Node.js, Ruby, PHP # Размеры: tiny, small, medium, large, archive (ZIP + доп. файл) # Метрики: upload_ms, cold_ms, hot_avg_ms (3 invoke) # # Запуск: bash bench-func/bench_full.sh # На VM: bash ~/terra/fission/bench-func/bench_full.sh set -uo pipefail SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) ROOT_DIR=$(cd "${SCRIPT_DIR}/.." && pwd) source "${ROOT_DIR}/scripts/lib.sh" TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S) RESULTS_DIR="${ROOT_DIR}/test-results/bench-func" LOGFILE="${RESULTS_DIR}/bench_full_${TIMESTAMP}.log" mkdir -p "$RESULTS_DIR" SUB="bench@bench.local" RUNID=$(date +%s | tail -c 8) TMP=$(mktemp -d) echo "" | tee -a "$LOGFILE" echo "════════════════════════════════════════════════════════════════" | tee -a "$LOGFILE" echo " bench_full.sh run=${RUNID} $(date)" | tee -a "$LOGFILE" echo " API: ${BASE}" | tee -a "$LOGFILE" echo " SUB: ${SUB}" | tee -a "$LOGFILE" echo "════════════════════════════════════════════════════════════════" | tee -a "$LOGFILE" declare -a ALL_FNS=() declare -a SUMMARY_ROWS=() PASS_CNT=0 FAIL_CNT=0 # ── Cleanup ─────────────────────────────────────────────────────────────── cleanup() { rm -rf "$TMP" 2>/dev/null || true for fn in "${ALL_FNS[@]:-}"; do curl -s -X DELETE "${BASE}/functions/${fn}" \ -H "X-Auth-Token: ${SUB}" >/dev/null 2>&1 || true done } trap cleanup EXIT # ── Helpers ─────────────────────────────────────────────────────────────── log() { echo "$@" | tee -a "$LOGFILE"; } escape_code() { python3 -c 'import json, sys; print(json.dumps(sys.stdin.read()))' } get_status() { python3 -c "import json,sys try: d=json.load(sys.stdin) print(d.get('status','')) except: print('')" 2>/dev/null } get_field() { local field="$1" python3 -c "import json,sys try: d=json.load(sys.stdin) print(d.get('$field','')) except: print('')" 2>/dev/null } # bench_code LANG SIZE FN CODEFILE EXPECTED_TAG bench_code() { local lang="$1" size="$2" fn="$3" codefile="$4" expected="${5:-}" ALL_FNS+=("$fn") local code_esc code_esc=$(cat "$codefile" | escape_code) # Upload local t0 t1 R CODE BODY t0=$(date +%s%3N) R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d "{\"name\":\"${fn}\",\"language\":\"${lang}\",\"code\":${code_esc}}" 2>/dev/null) t1=$(date +%s%3N) CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | head -n -1) local UPLOAD_MS=$((t1 - t0)) if [ "$CODE" != "201" ]; then log " FAIL [${lang}/${size}] upload HTTP=$CODE | $(printf '%s' "$BODY" | head -c 150)" SUMMARY_ROWS+=("$(printf '%-8s %-8s %8s %10s %10s %s' "$lang" "$size" "-" "-" "-" "UPLOAD-FAIL-${CODE}")") FAIL_CNT=$((FAIL_CNT+1)) return 1 fi # Cold start (retry 20×5s = 100s max) local tc0 COLD_MS=0 cold_ok=0 tc0=$(date +%s%3N) for i in $(seq 1 20); do local resp s raw resp=$(curl -s --max-time 35 -X POST "${BASE}/functions/${fn}/invoke" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" -d '{}' 2>/dev/null) s=$(echo "$resp" | get_status) raw=$(echo "$resp" | get_field "response_raw") if [ "$s" = "200" ]; then if [ -z "$expected" ] || echo "$raw" | grep -qF "$expected"; then local tc1 tc1=$(date +%s%3N) COLD_MS=$((tc1 - tc0)) cold_ok=1 break fi fi sleep 5 done if [ $cold_ok -eq 0 ]; then log " FAIL [${lang}/${size}] cold start timeout" SUMMARY_ROWS+=("$(printf '%-8s %-8s %8s %10s %10s %s' "$lang" "$size" "$UPLOAD_MS" "-" "-" "COLD-TIMEOUT")") FAIL_CNT=$((FAIL_CNT+1)) return 1 fi # Hot start ×3 local HOT_TOTAL=0 hot_ok=0 for i in 1 2 3; do local th0 th1 resp s th0=$(date +%s%3N) resp=$(curl -s --max-time 35 -X POST "${BASE}/functions/${fn}/invoke" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" -d '{}' 2>/dev/null) th1=$(date +%s%3N) s=$(echo "$resp" | get_status) if [ "$s" = "200" ]; then HOT_TOTAL=$((HOT_TOTAL + th1 - th0)) hot_ok=$((hot_ok+1)) fi done local HOT_AVG_MS=0 [ $hot_ok -gt 0 ] && HOT_AVG_MS=$((HOT_TOTAL / hot_ok)) log " OK [${lang}/${size}] upload=${UPLOAD_MS}ms cold=${COLD_MS}ms hot_avg=${HOT_AVG_MS}ms (${hot_ok}/3)" SUMMARY_ROWS+=("$(printf '%-8s %-8s %8s %10s %10s %s' "$lang" "$size" "${UPLOAD_MS}" "${COLD_MS}" "${HOT_AVG_MS}" "OK(hot=${hot_ok}/3)")") PASS_CNT=$((PASS_CNT+1)) } # bench_archive LANG_SHORT LANG FN ZIP EXPECTED_TAG bench_archive() { local lang_short="$1" lang="$2" fn="$3" zipfile="$4" expected="$5" ALL_FNS+=("$fn") local t0 t1 R CODE BODY t0=$(date +%s%3N) R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" \ -F "name=${fn}" \ -F "language=${lang}" \ -F "archive=@${zipfile};type=application/zip" 2>/dev/null) t1=$(date +%s%3N) CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | head -n -1) local UPLOAD_MS=$((t1 - t0)) if [ "$CODE" != "201" ]; then log " FAIL [${lang}/archive] upload HTTP=$CODE | $(printf '%s' "$BODY" | head -c 150)" SUMMARY_ROWS+=("$(printf '%-8s %-8s %8s %10s %10s %s' "$lang" "archive" "-" "-" "-" "UPLOAD-FAIL-${CODE}")") FAIL_CNT=$((FAIL_CNT+1)) return 1 fi # Cold start (retry 24×5s = 120s — архивам нужна сборка) local tc0 COLD_MS=0 cold_ok=0 tc0=$(date +%s%3N) for i in $(seq 1 24); do local resp s raw resp=$(curl -s --max-time 35 -X POST "${BASE}/functions/${fn}/invoke" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" -d '{}' 2>/dev/null) s=$(echo "$resp" | get_status) raw=$(echo "$resp" | get_field "response_raw") if [ "$s" = "200" ] && echo "$raw" | grep -qF "$expected"; then local tc1 tc1=$(date +%s%3N) COLD_MS=$((tc1 - tc0)) cold_ok=1 break fi sleep 5 done if [ $cold_ok -eq 0 ]; then log " FAIL [${lang}/archive] cold start timeout" SUMMARY_ROWS+=("$(printf '%-8s %-8s %8s %10s %10s %s' "$lang" "archive" "$UPLOAD_MS" "-" "-" "COLD-TIMEOUT")") FAIL_CNT=$((FAIL_CNT+1)) return 1 fi # Hot ×3 local HOT_TOTAL=0 hot_ok=0 for i in 1 2 3; do local th0 th1 resp s th0=$(date +%s%3N) resp=$(curl -s --max-time 35 -X POST "${BASE}/functions/${fn}/invoke" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" -d '{}' 2>/dev/null) th1=$(date +%s%3N) s=$(echo "$resp" | get_status) [ "$s" = "200" ] && { HOT_TOTAL=$((HOT_TOTAL + th1 - th0)); hot_ok=$((hot_ok+1)); } done local HOT_AVG_MS=0 [ $hot_ok -gt 0 ] && HOT_AVG_MS=$((HOT_TOTAL / hot_ok)) log " OK [${lang}/archive] upload=${UPLOAD_MS}ms cold=${COLD_MS}ms hot_avg=${HOT_AVG_MS}ms (${hot_ok}/3)" SUMMARY_ROWS+=("$(printf '%-8s %-8s %8s %10s %10s %s' "$lang" "archive" "${UPLOAD_MS}" "${COLD_MS}" "${HOT_AVG_MS}" "OK(hot=${hot_ok}/3)")") PASS_CNT=$((PASS_CNT+1)) } # ═══════════════════════════════════════════════════════════════════════════ # ГЕНЕРАЦИЯ КОДА ФУНКЦИЙ # ═══════════════════════════════════════════════════════════════════════════ # ── PYTHON ──────────────────────────────────────────────────────────────── cat > "${TMP}/py_tiny.py" << 'PYEOF' def main(event, context): return {"statusCode": 200, "body": "bench-py-tiny-ok"} PYEOF cat > "${TMP}/py_small.py" << 'PYEOF' import math, time def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a def sieve(n): s = [True] * (n + 1); s[0] = s[1] = False for i in range(2, int(math.sqrt(n)) + 1): if s[i]: for j in range(i*i, n+1, i): s[j] = False return [i for i in range(n+1) if s[i]] def sqrt_sum(n): return sum(math.sqrt(i) for i in range(1, n + 1)) def main(event, context): t0 = time.time() f = fib(30) primes = sieve(200) s = sqrt_sum(500) elapsed = time.time() - t0 return { "statusCode": 200, "body": f"bench-py-small-ok:fib30={f}:primes={len(primes)}:sqrt={s:.1f}:t={elapsed:.4f}s" } PYEOF cat > "${TMP}/py_medium.py" << 'PYEOF' import math, time, json, hashlib LOOKUP = {i: i*i for i in range(100)} WORDS = ["alpha","beta","gamma","delta","epsilon","zeta","eta","theta", "iota","kappa","lambda","mu","nu","xi","omicron","pi", "rho","sigma","tau","upsilon","phi","chi","psi","omega"] def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a def sieve(n): s = [True] * (n + 1); s[0] = s[1] = False for i in range(2, int(math.sqrt(n)) + 1): if s[i]: for j in range(i*i, n+1, i): s[j] = False return [i for i in range(n+1) if s[i]] def sqrt_sum(n): return sum(math.sqrt(i) for i in range(1, n + 1)) def bubble_sort(arr): arr = arr[:] n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr def mat_mul(a, b): ra, ca, cb = len(a), len(a[0]), len(b[0]) c = [[0]*cb for _ in range(ra)] for i in range(ra): for j in range(cb): for k in range(ca): c[i][j] += a[i][k] * b[k][j] return c def process_words(words): return [w.upper()[::-1] for w in words if len(w) > 3] def get_stats(lst): n = len(lst); m = sum(lst) / n v = sum((x - m)**2 for x in lst) / n return {"n": n, "mean": round(m, 4), "std": round(math.sqrt(v), 4)} def main(event, context): t0 = time.time() f = fib(35) primes = sieve(500) s = sqrt_sum(1000) nums = list(range(50, 0, -1)) sorted_nums = bubble_sort(nums) a = [[1,2,3],[4,5,6]]; b = [[7,8],[9,10],[11,12]] c = mat_mul(a, b) pw = process_words(WORDS) st = get_stats(primes) h = hashlib.sha256(b"bench-medium-python").hexdigest()[:16] elapsed = time.time() - t0 return {"statusCode": 200, "body": json.dumps({ "tag": "bench-py-medium-ok", "fib35": f, "primes": len(primes), "sqrt": round(s, 1), "sorted0": sorted_nums[0], "mat_c00": c[0][0], "words": len(pw), "stats": st, "hash": h, "elapsed": round(elapsed, 5) })} PYEOF cat > "${TMP}/py_large.py" << 'PYEOF' import math, time, json, hashlib, re, base64 ALPHABET = "abcdefghijklmnopqrstuvwxyz" LOOKUP_SQ = {i: i*i for i in range(200)} LOOKUP_CUBE = {i: i**3 for i in range(200)} WORDS = ["alpha","beta","gamma","delta","epsilon","zeta","eta","theta", "iota","kappa","lambda","mu","nu","xi","omicron","pi", "rho","sigma","tau","upsilon","phi","chi","psi","omega", "one","two","three","four","five","six","seven","eight", "nine","ten","eleven","twelve","thirteen","fourteen","fifteen"] CONSTANTS = {"pi": math.pi, "e": math.e, "phi": (1+math.sqrt(5))/2, "sqrt2": math.sqrt(2), "ln2": math.log(2)} def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a def fib_list(n): r = []; a, b = 0, 1 for _ in range(n): r.append(a); a, b = b, a + b return r def sieve(n): s = [True] * (n + 1); s[0] = s[1] = False for i in range(2, int(math.sqrt(n)) + 1): if s[i]: for j in range(i*i, n+1, i): s[j] = False return [i for i in range(n+1) if s[i]] def sqrt_sum(n): return sum(math.sqrt(i) for i in range(1, n + 1)) def log_sum(n): return sum(math.log(i) for i in range(1, n + 1)) def trig_sum(n): return sum(math.sin(i) * math.cos(i) for i in range(n)) def bubble_sort(arr): arr = arr[:] n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr def insertion_sort(arr): arr = arr[:] for i in range(1, len(arr)): key = arr[i]; j = i - 1 while j >= 0 and arr[j] > key: arr[j+1] = arr[j]; j -= 1 arr[j+1] = key return arr def mat_mul(a, b): ra, ca, cb = len(a), len(a[0]), len(b[0]) c = [[0]*cb for _ in range(ra)] for i in range(ra): for j in range(cb): for k in range(ca): c[i][j] += a[i][k] * b[k][j] return c def transpose(m): return [[m[j][i] for j in range(len(m))] for i in range(len(m[0]))] def process_words(words): return [{"original": w, "upper": w.upper(), "reversed": w[::-1], "len": len(w), "vowels": sum(1 for c in w if c in "aeiou")} for w in words if len(w) >= 3] def get_stats(lst): n = len(lst); m = sum(lst) / n v = sum((x - m)**2 for x in lst) / n s = sorted(lst) median = s[n//2] if n % 2 else (s[n//2-1] + s[n//2]) / 2 return {"n": n, "min": min(lst), "max": max(lst), "mean": round(m, 4), "median": median, "std": round(math.sqrt(v), 4)} def caesar(text, shift): r = [] for c in text: if c.isalpha(): base = ord('a') if c.islower() else ord('A') r.append(chr((ord(c) - base + shift) % 26 + base)) else: r.append(c) return ''.join(r) def run_regex(text): return {p: len(re.findall(p, text)) for p in [r'\d+', r'[a-z]+', r'[A-Z]+']} def lookup_ops(n): return sum(LOOKUP_SQ.get(i % 200, 0) + LOOKUP_CUBE.get(i % 200, 0) for i in range(n)) def main(event, context): t0 = time.time() sq = sqrt_sum(2000); lg = log_sum(1000); tr = trig_sum(500) primes = sieve(1000); f40 = fib(40); fibs = fib_list(20) nums = list(range(100, 0, -1)) bs = bubble_sort(nums[:50]); ins = insertion_sort(nums[50:]) h256 = hashlib.sha256(b"bench-large-python-2026").hexdigest() md5 = hashlib.md5(b"bench-large-python-2026").hexdigest() b64 = base64.b64encode(b"hello from bench-py-large").decode() ma = [[i+j for j in range(4)] for i in range(4)] mb = [[i*j+1 for j in range(4)] for i in range(4)] mc = mat_mul(ma, mb); mt = transpose(mc) pw = process_words(WORDS); pst = get_stats(primes[:50]) cipher = caesar("hello bench world", 13) rx = run_regex("Hello World 42 bench-py-large TEST 2026") lookup = lookup_ops(500) elapsed = time.time() - t0 return {"statusCode": 200, "body": json.dumps({ "tag": "bench-py-large-ok", "sqrt": round(sq, 2), "log": round(lg, 2), "trig": round(tr, 4), "primes": len(primes), "fib40": f40, "fibs_sum": sum(fibs), "h256": h256[:16], "md5": md5[:16], "b64_len": len(b64), "mat_trace": sum(mc[i][i] for i in range(len(mc))), "words": len(pw), "stats": pst, "cipher": cipher, "regex": rx, "lookup": lookup, "elapsed": round(elapsed, 5) })} PYEOF # ── NODE.JS ─────────────────────────────────────────────────────────────── cat > "${TMP}/node_tiny.js" << 'JSEOF' module.exports = async function(ctx) { return { status: 200, body: "bench-node-tiny-ok" }; }; JSEOF cat > "${TMP}/node_small.js" << 'JSEOF' function fib(n) { let a=0,b=1; for(let i=0;i{if(v)a.push(i);return a;},[]); } function sqrtSum(n) { let s=0; for(let i=1;i<=n;i++) s+=Math.sqrt(i); return s; } module.exports = async function(ctx) { const t0=Date.now(); const f=fib(30), primes=sieve(200), s=sqrtSum(500); const elapsed=Date.now()-t0; return { status: 200, body: `bench-node-small-ok:fib30=${f}:primes=${primes.length}:sqrt=${s.toFixed(1)}:t=${elapsed}ms` }; }; JSEOF cat > "${TMP}/node_medium.js" << 'JSEOF' const crypto = require('crypto'); const WORDS = ["alpha","beta","gamma","delta","epsilon","zeta","eta","theta", "iota","kappa","lambda","mu","nu","xi","omicron","pi", "rho","sigma","tau","upsilon","phi","chi","psi","omega"]; function fib(n) { let a=0,b=1; for(let i=0;i{if(v)a.push(i);return a;},[]); } function sqrtSum(n) { let s=0; for(let i=1;i<=n;i++) s+=Math.sqrt(i); return s; } function bubbleSort(arr) { arr=[...arr]; const n=arr.length; for(let i=0;iarr[j+1]) [arr[j],arr[j+1]]=[arr[j+1],arr[j]]; return arr; } function matMul(a,b) { const ra=a.length,ca=a[0].length,cb=b[0].length; const c=Array.from({length:ra},()=>new Array(cb).fill(0)); for(let i=0;is.length>3).map(s=>s.toUpperCase().split('').reverse().join('')); } function stats(arr) { const n=arr.length, m=arr.reduce((a,b)=>a+b,0)/n; const v=arr.reduce((a,b)=>a+(b-m)**2,0)/n; return {n, mean:+m.toFixed(4), std:+Math.sqrt(v).toFixed(4)}; } function sha256(s) { return crypto.createHash('sha256').update(s).digest('hex').slice(0,16); } module.exports = async function(ctx) { const t0=Date.now(); const f=fib(35), primes=sieve(500), s=sqrtSum(1000); const nums=Array.from({length:50},(_,i)=>50-i); const sorted=bubbleSort(nums); const a=[[1,2,3],[4,5,6]], b=[[7,8],[9,10],[11,12]], c=matMul(a,b); const pw=processWords(WORDS), st=stats(primes), h=sha256('bench-medium-node'); const elapsed=Date.now()-t0; return {status:200, body:JSON.stringify({ tag:"bench-node-medium-ok", fib35:f, primes:primes.length, sqrt:+s.toFixed(1), sorted0:sorted[0], mat_c00:c[0][0], words:pw.length, stats:st, hash:h, elapsed })}; }; JSEOF cat > "${TMP}/node_large.js" << 'JSEOF' const crypto = require('crypto'); const WORDS = ["alpha","beta","gamma","delta","epsilon","zeta","eta","theta", "iota","kappa","lambda","mu","nu","xi","omicron","pi", "rho","sigma","tau","upsilon","phi","chi","psi","omega", "one","two","three","four","five","six","seven","eight", "nine","ten","eleven","twelve","thirteen","fourteen","fifteen"]; const LOOKUP_SQ = Object.fromEntries(Array.from({length:200},(_,i)=>[i,i*i])); const LOOKUP_CUBE = Object.fromEntries(Array.from({length:200},(_,i)=>[i,i**3])); const CONSTANTS = {pi:Math.PI, e:Math.E, phi:(1+Math.sqrt(5))/2, sqrt2:Math.SQRT2, ln2:Math.LN2}; function fib(n) { let a=0,b=1; for(let i=0;i{if(v)a.push(i);return a;},[]); } function sqrtSum(n) { let s=0; for(let i=1;i<=n;i++) s+=Math.sqrt(i); return s; } function logSum(n) { let s=0; for(let i=1;i<=n;i++) s+=Math.log(i); return s; } function trigSum(n) { let s=0; for(let i=0;iarr[j+1]) [arr[j],arr[j+1]]=[arr[j+1],arr[j]]; return arr; } function insertionSort(arr) { arr=[...arr]; for(let i=1;i=0&&arr[j]>k){arr[j+1]=arr[j];j--;} arr[j+1]=k; } return arr; } function matMul(a,b) { const ra=a.length,ca=a[0].length,cb=b[0].length; const c=Array.from({length:ra},()=>new Array(cb).fill(0)); for(let i=0;im.map(r=>r[i])); } function processWords(w) { return w.filter(s=>s.length>=3).map(s=>({ original:s, upper:s.toUpperCase(), reversed:s.split('').reverse().join(''), len:s.length, vowels:[...s].filter(c=>"aeiou".includes(c)).length })); } function stats(arr) { const n=arr.length, m=arr.reduce((a,b)=>a+b,0)/n; const v=arr.reduce((a,b)=>a+(b-m)**2,0)/n; const sorted=[...arr].sort((a,b)=>a-b); return {n, min:sorted[0], max:sorted[n-1], mean:+m.toFixed(4), std:+Math.sqrt(v).toFixed(4)}; } function sha256(s) { return crypto.createHash('sha256').update(s).digest('hex'); } function md5(s) { return crypto.createHash('md5').update(s).digest('hex'); } function caesar(t,sh) { return [...t].map(c=>{ const b=c.match(/[a-z]/)?97:c.match(/[A-Z]/)?65:null; return b===null?c:String.fromCharCode((c.charCodeAt(0)-b+sh)%26+b); }).join(''); } function lookupOps(n) { let r=0; for(let i=0;i100-i); const bs=bubbleSort(nums.slice(0,50)), ins=insertionSort(nums.slice(50)); const h256=sha256('bench-large-node-2026'), md=md5('bench-large-node-2026'); const ma=Array.from({length:4},(_,i)=>Array.from({length:4},(_,j)=>i+j)); const mb=Array.from({length:4},(_,i)=>Array.from({length:4},(_,j)=>i*j+1)); const mc=matMul(ma,mb), mt=transpose(mc); const pw=processWords(WORDS), pst=stats(primes.slice(0,50)); const cipher=caesar('hello bench world',13); const lookup=lookupOps(500); const csum=Object.values(CONSTANTS).reduce((a,b)=>a+b,0); const elapsed=Date.now()-t0; return {status:200, body:JSON.stringify({ tag:"bench-node-large-ok", sqrt:+sq.toFixed(2), log:+lg.toFixed(2), trig:+tr.toFixed(4), primes:primes.length, fib40:f40.toString(), fibsSum:fibs.reduce((a,b)=>a+b,0), h256:h256.slice(0,16), md:md.slice(0,16), matTrace:mc.reduce((s,r,i)=>s+r[i],0), words:pw.length, stats:pst, cipher, lookup, csum:+csum.toFixed(6), elapsed })}; }; JSEOF # ── RUBY ────────────────────────────────────────────────────────────────── cat > "${TMP}/ruby_tiny.rb" << 'RBEOF' def handler(ctx) { statusCode: 200, body: "bench-ruby-tiny-ok" } end RBEOF cat > "${TMP}/ruby_small.rb" << 'RBEOF' require 'json' def fib(n); a,b=0,1; n.times{a,b=b,a+b}; a; end def sieve(n) s=Array.new(n+1,true); s[0]=s[1]=false (2..Math.sqrt(n).to_i).each{|i|(i*i..n).step(i){|j|s[j]=false} if s[i]} (0..n).select{|i|s[i]} end def sqrt_sum(n); (1..n).sum{|i|Math.sqrt(i)}; end def handler(ctx) t0=Time.now f=fib(30); primes=sieve(200); s=sqrt_sum(500) elapsed=Time.now-t0 { statusCode: 200, body: "bench-ruby-small-ok:fib30=#{f}:primes=#{primes.length}:sqrt=#{s.round(1)}:t=#{elapsed.round(4)}s" } end RBEOF cat > "${TMP}/ruby_medium.rb" << 'RBEOF' require 'json' require 'digest' WORDS=%w[alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi omicron pi rho sigma tau upsilon phi chi psi omega] def fib(n); a,b=0,1; n.times{a,b=b,a+b}; a; end def sieve(n) s=Array.new(n+1,true); s[0]=s[1]=false (2..Math.sqrt(n).to_i).each{|i|(i*i..n).step(i){|j|s[j]=false} if s[i]} (0..n).select{|i|s[i]} end def sqrt_sum(n); (1..n).sum{|i|Math.sqrt(i)}; end def bubble_sort(arr) arr=arr.dup; n=arr.length n.times{(n-1).times{|j|arr[j],arr[j+1]=arr[j+1],arr[j] if arr[j]>arr[j+1]}} arr end def mat_mul(a,b) ra,ca,cb=a.length,a[0].length,b[0].length Array.new(ra){|i|Array.new(cb){|j|(0...ca).sum{|k|a[i][k]*b[k][j]}}} end def process_words(w); w.select{|s|s.length>3}.map{|s|s.upcase.reverse}; end def get_stats(arr) n=arr.length; m=arr.sum.to_f/n; v=arr.sum{|x|(x-m)**2}/n {n:n, mean:m.round(4), std:Math.sqrt(v).round(4)} end def handler(ctx) t0=Time.now f=fib(35); primes=sieve(500); s=sqrt_sum(1000) nums=(1..50).to_a.reverse; sorted=bubble_sort(nums) a=[[1,2,3],[4,5,6]]; b=[[7,8],[9,10],[11,12]]; c=mat_mul(a,b) pw=process_words(WORDS); st=get_stats(primes) h=Digest::SHA256.hexdigest('bench-medium-ruby')[0,16] elapsed=Time.now-t0 { statusCode: 200, body: JSON.generate({tag:"bench-ruby-medium-ok", fib35:f, primes:primes.length, sqrt:s.round(1), sorted0:sorted[0], mat_c00:c[0][0], words:pw.length, stats:st, hash:h, elapsed:elapsed.round(5)}) } end RBEOF cat > "${TMP}/ruby_large.rb" << 'RBEOF' require 'json' require 'digest' require 'base64' WORDS=%w[alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi omicron pi rho sigma tau upsilon phi chi psi omega one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen] LOOKUP_SQ=(0...200).each_with_object({}){|i,h|h[i]=i*i} LOOKUP_CUBE=(0...200).each_with_object({}){|i,h|h[i]=i**3} CONSTANTS={pi:Math::PI, e:Math::E, phi:(1+Math.sqrt(5))/2, sqrt2:Math.sqrt(2), ln2:Math.log(2)} def fib(n); a,b=0,1; n.times{a,b=b,a+b}; a; end def fib_list(n); r=[]; a,b=0,1; n.times{r<arr[j+1]}} arr end def insertion_sort(arr) arr=arr.dup (1...arr.length).each{|i|k=arr[i];j=i-1;while j>=0&&arr[j]>k;arr[j+1]=arr[j];j-=1;end;arr[j+1]=k} arr end def mat_mul(a,b) ra,ca,cb=a.length,a[0].length,b[0].length Array.new(ra){|i|Array.new(cb){|j|(0...ca).sum{|k|a[i][k]*b[k][j]}}} end def transpose(m); m[0].length.times.map{|j|m.map{|r|r[j]}}; end def process_words(w) w.select{|s|s.length>=3}.map{|s|{original:s,upper:s.upcase,reversed:s.reverse,len:s.length,vowels:s.count("aeiou")}} end def get_stats(arr) n=arr.length; m=arr.sum.to_f/n; v=arr.sum{|x|(x-m)**2}/n {n:n, min:arr.min, max:arr.max, mean:m.round(4), std:Math.sqrt(v).round(4)} end def caesar(t,sh) t.chars.map{|c| if c=~/[a-z]/; (('a'.ord+(c.ord-'a'.ord+sh)%26).chr) elsif c=~/[A-Z]/; (('A'.ord+(c.ord-'A'.ord+sh)%26).chr) else c end }.join end def lookup_ops(n); (0...n).sum{|i|(LOOKUP_SQ[i%200]||0)+(LOOKUP_CUBE[i%200]||0)}; end def handler(ctx) t0=Time.now sq=sqrt_sum(2000); lg=log_sum(1000); tr=trig_sum(500) primes=sieve(1000); f40=fib(40); fibs=fib_list(20) nums=(1..100).to_a.reverse bs=bubble_sort(nums[0,50]); ins=insertion_sort(nums[50,50]) h256=Digest::SHA256.hexdigest('bench-large-ruby-2026') md5=Digest::MD5.hexdigest('bench-large-ruby-2026') b64=Base64.strict_encode64('hello from bench-ruby-large') ma=Array.new(4){|i|Array.new(4){|j|i+j}} mb=Array.new(4){|i|Array.new(4){|j|i*j+1}} mc=mat_mul(ma,mb); mt=transpose(mc) pw=process_words(WORDS); pst=get_stats(primes[0,50]) cipher=caesar('hello bench world',13) lookup=lookup_ops(500) csum=CONSTANTS.values.sum elapsed=Time.now-t0 { statusCode: 200, body: JSON.generate({tag:"bench-ruby-large-ok", sqrt:sq.round(2), log:lg.round(2), trig:tr.round(4), primes:primes.length, fib40:f40, fibs_sum:fibs.sum, h256:h256[0,16], md5:md5[0,16], b64_len:b64.length, mat_trace:mc.each_with_index.sum{|r,i|r[i]}, words:pw.length, stats:pst, cipher:cipher, lookup:lookup, csum:csum.round(6), elapsed:elapsed.round(5)}) } end RBEOF # ── PHP ─────────────────────────────────────────────────────────────────── cat > "${TMP}/php_tiny.php" << 'PHPEOF' "${TMP}/php_small.php" << 'PHPEOF' "${TMP}/php_medium.php" << 'PHPEOF' $arr[$j+1])[$arr[$j],$arr[$j+1]]=[$arr[$j+1],$arr[$j]];return $arr;} function matMul($a,$b){$ra=count($a);$ca=count($a[0]);$cb=count($b[0]);$c=array_fill(0,$ra,array_fill(0,$cb,0));for($i=0;$i<$ra;$i++)for($j=0;$j<$cb;$j++)for($k=0;$k<$ca;$k++)$c[$i][$j]+=$a[$i][$k]*$b[$k][$j];return $c;} function processWords($w){$f=array_filter($w,function($s){return strlen($s)>3;});return array_values(array_map(function($s){return strrev(strtoupper($s));},$f));} function statsArr($arr){$n=count($arr);$m=array_sum($arr)/$n;$v=array_sum(array_map(function($x)use($m){return($x-$m)**2;},$arr))/$n;return['n'=>$n,'mean'=>round($m,4),'std'=>round(sqrt($v),4)];} function main($event,$context){ $t0=microtime(true); $words=["alpha","beta","gamma","delta","epsilon","zeta","eta","theta","iota","kappa","lambda","mu"]; $f=fib(35);$primes=sieve(500);$s=sqrtSum(1000); $nums=range(50,1);$sorted=bubbleSort($nums); $a=[[1,2,3],[4,5,6]];$b=[[7,8],[9,10],[11,12]];$c=matMul($a,$b); $pw=processWords($words);$st=statsArr($primes); $h=substr(hash('sha256','bench-medium-php'),0,16); $elapsed=microtime(true)-$t0; return json_encode(['tag'=>'bench-php-medium-ok','fib35'=>$f,'primes'=>count($primes), 'sqrt'=>round($s,1),'sorted0'=>$sorted[0],'mat_c00'=>$c[0][0], 'words'=>count($pw),'stats'=>$st,'hash'=>$h,'elapsed'=>round($elapsed,5)]); } PHPEOF cat > "${TMP}/php_large.php" << 'PHPEOF' $arr[$j+1])[$arr[$j],$arr[$j+1]]=[$arr[$j+1],$arr[$j]];return $arr;} function insertionSort($arr){for($i=1;$i=0&&$arr[$j]>$k){$arr[$j+1]=$arr[$j];$j--;}$arr[$j+1]=$k;}return $arr;} function matMul($a,$b){$ra=count($a);$ca=count($a[0]);$cb=count($b[0]);$c=array_fill(0,$ra,array_fill(0,$cb,0));for($i=0;$i<$ra;$i++)for($j=0;$j<$cb;$j++)for($k=0;$k<$ca;$k++)$c[$i][$j]+=$a[$i][$k]*$b[$k][$j];return $c;} function processWords($w){$f=array_filter($w,function($s){return strlen($s)>=3;});return array_values(array_map(function($s){return['original'=>$s,'upper'=>strtoupper($s),'reversed'=>strrev($s),'len'=>strlen($s),'vowels'=>preg_match_all('/[aeiou]/',$s)];},$f));} function statsArr($arr){$n=count($arr);$m=array_sum($arr)/$n;$v=array_sum(array_map(function($x)use($m){return($x-$m)**2;},$arr))/$n;return['n'=>$n,'min'=>min($arr),'max'=>max($arr),'mean'=>round($m,4),'std'=>round(sqrt($v),4)];} function caesar($t,$sh){return preg_replace_callback('/[a-zA-Z]/',function($m)use($sh){return chr((ord($m[0])-(ctype_lower($m[0])?97:65)+$sh)%26+(ctype_lower($m[0])?97:65));},$t);} function lookupOps($n){$sq=array_combine(range(0,199),array_map(function($i){return $i*$i;},range(0,199)));$cu=array_combine(range(0,199),array_map(function($i){return $i**3;},range(0,199)));$r=0;for($i=0;$i<$n;$i++)$r+=($sq[$i%200]??0)+($cu[$i%200]??0);return $r;} function main($event,$context){ $words=$GLOBALS['WORDS']; $t0=microtime(true); $sq=sqrtSum(2000);$lg=logSum(1000);$tr=trigSum(500); $primes=sieve(1000);$f40=fib(40);$fibs=fibList(20); $nums=range(100,1); $bs=bubbleSort(array_slice($nums,0,50));$ins=insertionSort(array_slice($nums,50,50)); $h256=substr(hash('sha256','bench-large-php-2026'),0,16); $md5=substr(hash('md5','bench-large-php-2026'),0,16); $b64=base64_encode('hello from bench-php-large'); $ma=array_map(function($i){return array_map(function($j)use($i){return $i+$j;},range(0,3));},range(0,3)); $mb=array_map(function($i){return array_map(function($j)use($i){return $i*$j+1;},range(0,3));},range(0,3)); $mc=matMul($ma,$mb); $pw=processWords($words);$pst=statsArr(array_slice($primes,0,50)); $cipher=caesar('hello bench world',13); $lookup=lookupOps(500); $csum=M_PI+M_E+(1+sqrt(5))/2+sqrt(2)+log(2); $elapsed=microtime(true)-$t0; return json_encode(['tag'=>'bench-php-large-ok', 'sqrt'=>round($sq,2),'log'=>round($lg,2),'trig'=>round($tr,4), 'primes'=>count($primes),'fib40'=>$f40,'fibs_sum'=>array_sum($fibs), 'h256'=>$h256,'md5'=>$md5,'b64_len'=>strlen($b64), 'mat_trace'=>array_sum(array_map(function($i)use($mc){return $mc[$i][$i];},range(0,3))), 'words'=>count($pw),'stats'=>$pst,'cipher'=>$cipher, 'lookup'=>$lookup,'csum'=>round($csum,6),'elapsed'=>round($elapsed,5)]); } PHPEOF # ═══════════════════════════════════════════════════════════════════════════ # ГЕНЕРАЦИЯ ZIP-АРХИВОВ # ═══════════════════════════════════════════════════════════════════════════ log "" log ">>> Генерация ZIP-архивов..." # Python archive: main.py + utils.py python3 - > "${TMP}/arc-py.zip" << 'PYARCEOF' import zipfile, io, sys buf = io.BytesIO() main_code = """import json from utils import compute def main(event, context): result = compute() return json.dumps({"tag": "bench-arc-py-ok", "result": result}), 200 """ utils_code = """import math, hashlib def compute(): primes = sieve(200) sq = sum(math.sqrt(i) for i in range(1, 500)) h = hashlib.sha256(b'bench-arc-python').hexdigest()[:12] return {"primes": len(primes), "sqrt_sum": round(sq, 2), "hash": h} def sieve(n): s = [True]*(n+1); s[0]=s[1]=False for i in range(2,int(math.sqrt(n))+1): if s[i]: for j in range(i*i,n+1,i): s[j]=False return [i for i in range(n+1) if s[i]] """ with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr('main.py', main_code) zf.writestr('utils.py', utils_code) sys.stdout.buffer.write(buf.getvalue()) PYARCEOF # Node.js archive: main.js + utils.js + package.json python3 - > "${TMP}/arc-node.zip" << 'NODEARCEOF' import zipfile, io, sys buf = io.BytesIO() main_code = """const { compute } = require('./utils'); module.exports = async function(ctx) { const result = compute(); return { status: 200, body: JSON.stringify({ tag: "bench-arc-node-ok", result }) }; }; """ utils_code = """const crypto = require('crypto'); function sieve(n) { const s=new Array(n+1).fill(true); s[0]=s[1]=false; for(let i=2;i*i<=n;i++) if(s[i]) for(let j=i*i;j<=n;j+=i) s[j]=false; return s.reduce((a,v,i)=>{if(v)a.push(i);return a;},[]); } function compute() { const primes=sieve(200); let sq=0; for(let i=1;i<=500;i++) sq+=Math.sqrt(i); const h=crypto.createHash('sha256').update('bench-arc-node').digest('hex').slice(0,12); return { primes: primes.length, sqrt_sum: +sq.toFixed(2), hash: h }; } module.exports = { compute }; """ pkg = '{"name":"bench-arc-node","version":"1.0.0","main":"main.js"}' with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr('main.js', main_code) zf.writestr('utils.js', utils_code) zf.writestr('package.json', pkg) sys.stdout.buffer.write(buf.getvalue()) NODEARCEOF # Ruby archive: handler.rb + utils.rb python3 - > "${TMP}/arc-ruby.zip" << 'RUBYARCEOF' import zipfile, io, sys buf = io.BytesIO() main_code = """require 'json' require_relative 'utils' def handler(ctx) result = compute() { statusCode: 200, body: JSON.generate({ tag: "bench-arc-ruby-ok", result: result }) } end """ utils_code = """require 'digest' def sieve(n) s=Array.new(n+1,true); s[0]=s[1]=false (2..Math.sqrt(n).to_i).each{|i|(i*i..n).step(i){|j|s[j]=false} if s[i]} (0..n).select{|i|s[i]} end def compute primes=sieve(200) sq=(1..500).sum{|i|Math.sqrt(i)} h=Digest::SHA256.hexdigest('bench-arc-ruby')[0,12] {primes: primes.length, sqrt_sum: sq.round(2), hash: h} end """ with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr('handler.rb', main_code) zf.writestr('utils.rb', utils_code) sys.stdout.buffer.write(buf.getvalue()) RUBYARCEOF # PHP archive: main.php + utils.php python3 - > "${TMP}/arc-php.zip" << 'PHPARCEOF' import zipfile, io, sys buf = io.BytesIO() main_code = """getBody()->write(json_encode(['tag' => 'bench-arc-php-ok', 'result' => $result])); } """ utils_code = """count($primes),'sqrt_sum'=>round($sq,2),'hash'=>$h]; } """ with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr('main.php', main_code) zf.writestr('utils.php', utils_code) sys.stdout.buffer.write(buf.getvalue()) PHPARCEOF # ═══════════════════════════════════════════════════════════════════════════ # ЗАПУСК БЕНЧМАРКОВ # ═══════════════════════════════════════════════════════════════════════════ log "" log "────────────────────────────────────────────────────────────────────────" log " LANG SIZE UPLOAD(ms) COLD(ms) HOT_AVG(ms) STATUS" log "────────────────────────────────────────────────────────────────────────" declare -A LANG_SHORT=([python]=py [nodejs]=node [ruby]=ruby [php]=php) declare -A CODE_EXT=([python]=py [nodejs]=js [ruby]=rb [php]=php) declare -A CODE_PREFIX=([python]=py [nodejs]=node [ruby]=ruby [php]=php) for lang in python nodejs ruby php; do short="${LANG_SHORT[$lang]}" prefix="${CODE_PREFIX[$lang]}" ext="${CODE_EXT[$lang]}" log "" log " === ${lang^^} ===" for size in tiny small medium large; do fn="bench-${short}-${size}-${RUNID}" codefile="${TMP}/${prefix}_${size}.${ext}" bench_code "$lang" "$size" "$fn" "$codefile" done done log "" log "────────────────────────────────────────────────────────────────────────" log " АРХИВЫ (ZIP с несколькими файлами)" log "────────────────────────────────────────────────────────────────────────" bench_archive "py" "python" "bench-arc-py-${RUNID}" "${TMP}/arc-py.zip" "bench-arc-py-ok" bench_archive "node" "nodejs" "bench-arc-node-${RUNID}" "${TMP}/arc-node.zip" "bench-arc-node-ok" bench_archive "ruby" "ruby" "bench-arc-ruby-${RUNID}" "${TMP}/arc-ruby.zip" "bench-arc-ruby-ok" bench_archive "php" "php" "bench-arc-php-${RUNID}" "${TMP}/arc-php.zip" "bench-arc-php-ok" # ═══════════════════════════════════════════════════════════════════════════ # ИТОГОВАЯ ТАБЛИЦА # ═══════════════════════════════════════════════════════════════════════════ log "" log "════════════════════════════════════════════════════════════════════════" log " ИТОГОВАЯ ТАБЛИЦА" log " LANG SIZE UPLOAD(ms) COLD(ms) HOT_AVG(ms) STATUS" log "════════════════════════════════════════════════════════════════════════" for row in "${SUMMARY_ROWS[@]}"; do log " $row" done log "════════════════════════════════════════════════════════════════════════" log " PASS: ${PASS_CNT} FAIL: ${FAIL_CNT} TOTAL: $((PASS_CNT+FAIL_CNT))" log "════════════════════════════════════════════════════════════════════════" log "" log " Лог: ${LOGFILE}" log " Готово: $(date)"