From: stephan Date: Tue, 30 Aug 2022 17:57:50 +0000 (+0000) Subject: batch-runner.js: add rudimentary metrics export to CSV. Add a toggle to reverse the... X-Git-Tag: version-3.40.0~169^2~143 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=53f635df55ecfce3baf9fccc1909dad53bd9643e;p=thirdparty%2Fsqlite.git batch-runner.js: add rudimentary metrics export to CSV. Add a toggle to reverse the log output mode (in normal order or most recent first). FossilOrigin-Name: b26e2bc05537a1f451db1bdd36a824d71c3ea25a130b6285f31ff9799d65fa7a --- diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 1067018d4a..39cf8b26e4 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -62,11 +62,14 @@ $(dir.top)/sqlite3.c: emcc_opt ?= -O0 .PHONY: release release: - $(MAKE) 'emcc_opt=-Os -g3' + $(MAKE) 'emcc_opt=-Os -g3 -flto' # ^^^^^ target-specific vars, e.g.: # release: emcc_opt=... # apparently only work for file targets, not PHONY targets? # +# ^^^ -flto improves runtime speed at -O0 considerably but doubles +# build time. +# # ^^^^ -O3, -Oz, -Os minify symbol names and there appears to be no # way around that except to use -g3, but -g3 causes the binary file # size to absolutely explode (approx. 5x larger). This minification @@ -284,6 +287,7 @@ endif CLEAN_FILES += $(sqlite3.js) $(sqlite3.wasm) all: $(sqlite3.js) +wasm: $(sqlite3.js) # End main Emscripten-based module build ######################################################################## diff --git a/ext/wasm/batch-runner.html b/ext/wasm/batch-runner.html index c91e591553..1b8d335ddb 100644 --- a/ext/wasm/batch-runner.html +++ b/ext/wasm/batch-runner.html @@ -42,11 +42,16 @@ + + + + +
-
(Log output is in reverse order, newest first!)
+
@@ -58,14 +63,26 @@ flex-direction: column; flex-wrap: wrap; } - .warning { color: firebrick; } + .warning { color: firebrick; } + .input-wrapper { + white-space: nowrap; + } #test-output { border: 1px inset; padding: 0.25em; /*max-height: 30em;*/ overflow: auto; white-space: break-spaces; - display: flex; flex-direction: column-reverse; + display: flex; flex-direction: column; + } + #test-output.reverse { + flex-direction: column-reverse; + } + .hidden { + position: absolute !important; + opacity: 0 !important; + pointer-events: none !important; + display: none !important; } diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js index cbd712bb91..909a140047 100644 --- a/ext/wasm/batch-runner.js +++ b/ext/wasm/batch-runner.js @@ -26,10 +26,22 @@ btnRun: document.querySelector('#sql-run'), btnRunNext: document.querySelector('#sql-run-next'), btnRunRemaining: document.querySelector('#sql-run-remaining'), + btnExportMetrics: document.querySelector('#export-metrics'), btnClear: document.querySelector('#output-clear'), - btnReset: document.querySelector('#db-reset') + btnReset: document.querySelector('#db-reset'), + cbReverseLog: document.querySelector('#cb-reverse-log-order') }, cache:{}, + metrics:{ + /** + Map of sql-file to timing metrics. We currently only store + the most recent run of each file, but we really should store + all runs so that we can average out certain values which vary + significantly across runs. e.g. a mandelbrot-generating query + will have a wide range of runtimes when run 10 times in a + row. + */ + }, log: console.log.bind(console), warn: console.warn.bind(console), cls: function(){this.e.output.innerHTML = ''}, @@ -63,8 +75,11 @@ const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; const ppDb = wasm.scopedAllocPtr(); const rc = capi.sqlite3_open_v2(fn, ppDb, oFlags, null); - if(rc) toss("sqlite3_open_v2() failed with code",rc); pDb = wasm.getPtrValue(ppDb) + if(rc){ + if(pDb) capi.sqlite3_close_v2(pDb); + toss("sqlite3_open_v2() failed with code",rc); + } }finally{ wasm.scopedAllocPop(stack); } @@ -84,6 +99,12 @@ } }, + /** + Loads batch-runner.list and populates the selection list from + it. Returns a promise which resolves to nothing in particular + when it completes. Only intended to be run once at the start + of the app. + */ loadSqlList: async function(){ const sel = this.e.selSql; sel.innerHTML = ''; @@ -156,7 +177,62 @@ document.querySelectorAll('.disable-during-eval').forEach((e)=>e.disabled = disable); }, - /** Fetch ./fn and eval it as an SQL blob. */ + /** + Converts this.metrics() to a form which is suitable for easy conversion to + CSV. It returns an array of arrays. The first sub-array is the column names. + The 2nd and subsequent are the values, one per test file (only the most recent + metrics are kept for any given file). + */ + metricsToArrays: function(){ + const rc = []; + Object.keys(this.metrics).sort().forEach((k)=>{ + const m = this.metrics[k]; + delete m.evalFileStart; + delete m.evalFileEnd; + const mk = Object.keys(m).sort(); + if(!rc.length){ + rc.push(['file', ...mk]); + } + const row = [k.split('/').pop()/*remove dir prefix from filename*/]; + rc.push(row); + mk.forEach((kk)=>row.push(m[kk])); + }); + return rc; + }, + + metricsToBlob: function(colSeparator='\t'){ + const ar = [], ma = this.metricsToArrays(); + if(!ma.length){ + this.logErr("Metrics are empty. Run something."); + return; + } + ma.forEach(function(row){ + ar.push(row.join(colSeparator),'\n'); + }); + return new Blob(ar); + }, + + downloadMetrics: function(){ + const b = this.metricsToBlob(); + if(!b) return; + const url = URL.createObjectURL(b); + const a = document.createElement('a'); + a.href = url; + a.download = 'batch-runner-metrics-'+((new Date().getTime()/1000) | 0)+'.csv'; + this.logHtml("Triggering download of",a.download); + document.body.appendChild(a); + a.click(); + setTimeout(()=>{ + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, 500); + }, + + /** + Fetch file fn and eval it as an SQL blob. This is an async + operation and returns a Promise which resolves to this + object on success. + */ evalFile: async function(fn){ const sql = await this.fetchFile(fn); const banner = "========================================"; @@ -165,9 +241,11 @@ const capi = this.sqlite3.capi, wasm = capi.wasm; let pStmt = 0, pSqlBegin; const stack = wasm.scopedAllocPush(); - const metrics = Object.create(null); + const metrics = this.metrics[fn] = Object.create(null); metrics.prepTotal = metrics.stepTotal = 0; metrics.stmtCount = 0; + metrics.malloc = 0; + metrics.strcpy = 0; this.blockControls(true); if(this.gotErr){ this.logErr("Cannot run ["+fn+"]: error cleanup is pending."); @@ -180,11 +258,16 @@ let t; let sqlByteLen = sql.byteLength; const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); + t = performance.now(); pSqlBegin = wasm.alloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); + metrics.malloc = performance.now() - t; + metrics.byteLength = sqlByteLen; let pSql = pSqlBegin; const pSqlEnd = pSqlBegin + sqlByteLen; + t = performance.now(); wasm.heap8().set(sql, pSql); wasm.setMemValue(pSql + sqlByteLen, 0); + metrics.strcpy = performance.now() - t; let breaker = 0; while(pSql && wasm.getMemValue(pSql,'i8')){ wasm.setPtrValue(ppStmt, 0); @@ -261,6 +344,20 @@ } this.openDb(dbFile, !!pDir); const who = this; + const eReverseLogNotice = document.querySelector('#reverse-log-notice'); + if(this.e.cbReverseLog.checked){ + eReverseLogNotice.classList.remove('hidden'); + this.e.output.classList.add('reverse'); + } + this.e.cbReverseLog.addEventListener('change', function(){ + if(this.checked){ + who.e.output.classList.add('reverse'); + eReverseLogNotice.classList.remove('hidden'); + }else{ + who.e.output.classList.remove('reverse'); + eReverseLogNotice.classList.add('hidden'); + } + }, false); this.e.btnClear.addEventListener('click', ()=>this.cls(), false); this.e.btnRun.addEventListener('click', function(){ if(!who.e.selSql.value) return; @@ -278,6 +375,12 @@ who.openDb(fn,true); } }, false); + this.e.btnExportMetrics.addEventListener('click', function(){ + who.logHtml2('warning',"Metrics export is a Work in Progress. See output in dev console."); + who.downloadMetrics(); + const m = who.metricsToArrays(); + console.log("Metrics:",who.metrics, m); + }); this.e.btnRunRemaining.addEventListener('click', async function(){ let v = who.e.selSql.value; const timeStart = performance.now(); diff --git a/manifest b/manifest index 28e7c2d2de..d6ecf92b24 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C batch-runner.js:\sre-enable\sunlink-before-run\sso\sthat\sOPFS\sspeedtest1\sbatch\scan\swork. -D 2022-08-30T10:26:33.223 +C batch-runner.js:\sadd\srudimentary\smetrics\sexport\sto\sCSV.\sAdd\sa\stoggle\sto\sreverse\sthe\slog\soutput\smode\s(in\snormal\sorder\sor\smost\srecent\sfirst). +D 2022-08-30T17:57:50.778 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -474,7 +474,7 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile b5d1e285ed9814646e1fb12a27b7507aea2d7208ef76e486e1471c8aabac5226 +F ext/wasm/GNUmakefile fd1ba419c38d79f742849cc96a1f1e1dd63821e6173bc5727f0a5439f04b4131 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 77ef4bcf37e362b9ad61f9c175dfc0f1b3e571563fb311b96581cf422ee6a8ec F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -489,8 +489,8 @@ F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db F ext/wasm/api/sqlite3-api-worker1.js 73579555563b789785ae83724014eaf31811073aad9be6596c8336ffb51edd71 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982 -F ext/wasm/batch-runner.html e5c3edd4a6c9359f6d9e6c99cb5f87f09007d98fa1c705ed3efa370abcd4323e -F ext/wasm/batch-runner.js da471fb7b5c6c918cfe59adaebedc3af24a1268084d09d90143653c71b2493dd +F ext/wasm/batch-runner.html 439245594711981831f2775f1426fd20d73a9b1bbf53f2cbbfc1194fa069891c +F ext/wasm/batch-runner.js dfe194ff87dac20e641aeee7080b74a877ca4a24ea107d6a80ff8115b28b7f0b F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f F ext/wasm/common/testing.css 572cf1ffae0b6eb7ca63684d3392bf350217a07b90e7a896e4fa850700c989b0 @@ -2013,8 +2013,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 06c106a7d23e4486dbed092757b7588688226ad35539ecc31378a8497f59d1ec -R 4e67c8f75eba175d0066f0954616ff1b +P ba08f7a7f12601aa580a8dc493b43cdd5ab65c87d5e4b3fd2f549343e45c2d2a +R cd46865d209bc499b87d03a53afeb5f7 U stephan -Z 9d799461f6bdad041adab908a7c4bd92 +Z a638c92d95e9388486f91bc003769b2d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 50b5203520..9cdf828810 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ba08f7a7f12601aa580a8dc493b43cdd5ab65c87d5e4b3fd2f549343e45c2d2a \ No newline at end of file +b26e2bc05537a1f451db1bdd36a824d71c3ea25a130b6285f31ff9799d65fa7a \ No newline at end of file