1*44a87f08Sstephan<!doctype html>
2*44a87f08Sstephan<html lang="en-us">
3*44a87f08Sstephan  <head>
4*44a87f08Sstephan    <meta charset="utf-8">
5*44a87f08Sstephan    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6*44a87f08Sstephan    <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
7*44a87f08Sstephan    <link rel="stylesheet" href="common/emscripten.css"/>
8*44a87f08Sstephan    <link rel="stylesheet" href="common/testing.css"/>
9*44a87f08Sstephan    <title>speedtest1.wasm Worker</title>
10*44a87f08Sstephan  </head>
11*44a87f08Sstephan  <body>
12*44a87f08Sstephan    <header id='titlebar'>speedtest1.wasm Worker</header>
13*44a87f08Sstephan    <div>See also: <a href='speedtest1.html'>A main-thread variant of this page.</a></div>
14*44a87f08Sstephan    <!-- emscripten bits -->
15*44a87f08Sstephan    <figure id="module-spinner">
16*44a87f08Sstephan      <div class="spinner"></div>
17*44a87f08Sstephan      <div class='center'><strong>Initializing app...</strong></div>
18*44a87f08Sstephan      <div class='center'>
19*44a87f08Sstephan        On a slow internet connection this may take a moment.  If this
20*44a87f08Sstephan        message displays for "a long time", intialization may have
21*44a87f08Sstephan        failed and the JavaScript console may contain clues as to why.
22*44a87f08Sstephan      </div>
23*44a87f08Sstephan    </figure>
24*44a87f08Sstephan    <div class="emscripten" id="module-status">Downloading...</div>
25*44a87f08Sstephan    <div class="emscripten">
26*44a87f08Sstephan      <progress value="0" max="100" id="module-progress" hidden='1'></progress>
27*44a87f08Sstephan    </div><!-- /emscripten bits -->
28*44a87f08Sstephan    <fieldset id='ui-controls' class='hidden'>
29*44a87f08Sstephan      <legend>Options</legend>
30*44a87f08Sstephan      <div id='toolbar'>
31*44a87f08Sstephan        <div id='toolbar-select'>
32*44a87f08Sstephan          <select id='select-flags' size='10' multiple></select>
33*44a87f08Sstephan          <div>TODO? Options which require values are not represented here.</div>
34*44a87f08Sstephan        </div>
35*44a87f08Sstephan        <div class='toolbar-inner-vertical' id='toolbar-selected-flags'>
36*44a87f08Sstephan          <button id='btn-reset-flags'>Reset Flags</button>
37*44a87f08Sstephan          <button id='btn-output-clear'>Clear output</button>
38*44a87f08Sstephan          <button id='btn-run'>Run</button>
39*44a87f08Sstephan        </div>
40*44a87f08Sstephan        <div class='toolbar-inner-vertical' id='toolbar-runner-controls'>
41*44a87f08Sstephan          <button id='btn-reset-flags'>Reset Flags</button>
42*44a87f08Sstephan          <button id='btn-output-clear'>Clear output</button>
43*44a87f08Sstephan          <button id='btn-run'>Run</button>
44*44a87f08Sstephan        </div>
45*44a87f08Sstephan      </div>
46*44a87f08Sstephan    </fieldset>
47*44a87f08Sstephan    <div>
48*44a87f08Sstephan      <span class='input-wrapper'>
49*44a87f08Sstephan        <input type='checkbox' class='disable-during-eval' id='cb-reverse-log-order' checked></input>
50*44a87f08Sstephan        <label for='cb-reverse-log-order' id='lbl-reverse-log-order'>Reverse log order</label>
51*44a87f08Sstephan      </span>
52*44a87f08Sstephan    </div>
53*44a87f08Sstephan    <div id='test-output'></div>
54*44a87f08Sstephan    <style>
55*44a87f08Sstephan      #test-output {
56*44a87f08Sstephan          white-space: break-spaces;
57*44a87f08Sstephan          overflow: auto;
58*44a87f08Sstephan      }
59*44a87f08Sstephan      #toolbar {
60*44a87f08Sstephan          display: flex;
61*44a87f08Sstephan          flex-direction: row;
62*44a87f08Sstephan          flex-wrap: wrap;
63*44a87f08Sstephan      }
64*44a87f08Sstephan      #toolbar > * {
65*44a87f08Sstephan          margin: 0 0.5em;
66*44a87f08Sstephan      }
67*44a87f08Sstephan      .toolbar-inner-vertical {
68*44a87f08Sstephan          display: flex;
69*44a87f08Sstephan          flex-direction: column;
70*44a87f08Sstephan          justify-content: space-between;
71*44a87f08Sstephan      }
72*44a87f08Sstephan      #toolbar-select {
73*44a87f08Sstephan          display: flex;
74*44a87f08Sstephan          flex-direction: column;
75*44a87f08Sstephan      }
76*44a87f08Sstephan      .toolbar-inner-vertical > *, #toolbar-select > * {
77*44a87f08Sstephan          margin: 0.2em 0;
78*44a87f08Sstephan      }
79*44a87f08Sstephan      #select-flags > option {
80*44a87f08Sstephan          white-space: pre;
81*44a87f08Sstephan          font-family: monospace;
82*44a87f08Sstephan      }
83*44a87f08Sstephan      fieldset {
84*44a87f08Sstephan          border-radius: 0.5em;
85*44a87f08Sstephan      }
86*44a87f08Sstephan      #toolbar-runner-controls { flex-grow: 1 }
87*44a87f08Sstephan      #toolbar-runner-controls > * { flex: 1 0 auto }
88*44a87f08Sstephan      #toolbar-selected-flags::before {
89*44a87f08Sstephan        font-family: initial;
90*44a87f08Sstephan        content:"Selected flags: ";
91*44a87f08Sstephan      }
92*44a87f08Sstephan      #toolbar-selected-flags {
93*44a87f08Sstephan        font-family: monospace;
94*44a87f08Sstephan        justify-content: flex-start;
95*44a87f08Sstephan      }
96*44a87f08Sstephan    </style>
97*44a87f08Sstephan    <script>(function(){
98*44a87f08Sstephan      'use strict';
99*44a87f08Sstephan      const E = (sel)=>document.querySelector(sel);
100*44a87f08Sstephan      const eOut = E('#test-output');
101*44a87f08Sstephan      const log2 = function(cssClass,...args){
102*44a87f08Sstephan        let ln;
103*44a87f08Sstephan        if(1 || cssClass){
104*44a87f08Sstephan          ln = document.createElement('div');
105*44a87f08Sstephan          if(cssClass) ln.classList.add(cssClass);
106*44a87f08Sstephan          ln.append(document.createTextNode(args.join(' ')));
107*44a87f08Sstephan        }else{
108*44a87f08Sstephan          // This doesn't work with the "reverse order" option!
109*44a87f08Sstephan          ln = document.createTextNode(args.join(' ')+'\n');
110*44a87f08Sstephan        }
111*44a87f08Sstephan        eOut.append(ln);
112*44a87f08Sstephan      };
113*44a87f08Sstephan      const log = (...args)=>{
114*44a87f08Sstephan        //console.log(...args);
115*44a87f08Sstephan        log2('', ...args);
116*44a87f08Sstephan      };
117*44a87f08Sstephan      const logErr = function(...args){
118*44a87f08Sstephan        //console.error(...args);
119*44a87f08Sstephan        log2('error', ...args);
120*44a87f08Sstephan      };
121*44a87f08Sstephan      const logWarn = function(...args){
122*44a87f08Sstephan        //console.warn(...args);
123*44a87f08Sstephan        log2('warning', ...args);
124*44a87f08Sstephan      };
125*44a87f08Sstephan
126*44a87f08Sstephan      const spacePad = function(str,len=18){
127*44a87f08Sstephan        if(str.length===len) return str;
128*44a87f08Sstephan        else if(str.length>len) return str.substr(0,len);
129*44a87f08Sstephan        const a = []; a.length = len - str.length;
130*44a87f08Sstephan        return str+a.join(' ');
131*44a87f08Sstephan      };
132*44a87f08Sstephan      // OPTION elements seem to ignore white-space:pre, so do this the hard way...
133*44a87f08Sstephan      const nbspPad = function(str,len=18){
134*44a87f08Sstephan        if(str.length===len) return str;
135*44a87f08Sstephan        else if(str.length>len) return str.substr(0,len);
136*44a87f08Sstephan        const a = []; a.length = len - str.length;
137*44a87f08Sstephan        return str+a.join('&nbsp;');
138*44a87f08Sstephan      };
139*44a87f08Sstephan
140*44a87f08Sstephan      const W = new Worker("speedtest1-worker.js");
141*44a87f08Sstephan      const mPost = function(msgType,payload){
142*44a87f08Sstephan        W.postMessage({type: msgType, data: payload});
143*44a87f08Sstephan      };
144*44a87f08Sstephan
145*44a87f08Sstephan      const eFlags = E('#select-flags');
146*44a87f08Sstephan      const eSelectedFlags = E('#toolbar-selected-flags');
147*44a87f08Sstephan
148*44a87f08Sstephan      const getSelectedFlags = ()=>Array.prototype.map.call(eFlags.selectedOptions, (v)=>v.value);
149*44a87f08Sstephan      const updateSelectedFlags = function(){
150*44a87f08Sstephan        eSelectedFlags.innerText = '';
151*44a87f08Sstephan        getSelectedFlags().forEach(function(f){
152*44a87f08Sstephan          const e = document.createElement('span');
153*44a87f08Sstephan          e.innerText = f;
154*44a87f08Sstephan          eSelectedFlags.appendChild(e);
155*44a87f08Sstephan        });
156*44a87f08Sstephan      };
157*44a87f08Sstephan      eFlags.addEventListener('change', updateSelectedFlags );
158*44a87f08Sstephan      {
159*44a87f08Sstephan        const flags = Object.create(null);
160*44a87f08Sstephan        /* TODO? Flags which require values need custom UI
161*44a87f08Sstephan           controls and some of them make little sense here
162*44a87f08Sstephan           (e.g. --script FILE). */
163*44a87f08Sstephan        flags["autovacuum"] = "Enable AUTOVACUUM mode";
164*44a87f08Sstephan        //flags["cachesize"] = "N       Set the cache size to N";
165*44a87f08Sstephan        flags["checkpoint"] = "Run PRAGMA wal_checkpoint after each test case";
166*44a87f08Sstephan        flags["exclusive"] = "Enable locking_mode=EXCLUSIVE";
167*44a87f08Sstephan        flags["explain"] = "Like --sqlonly but with added EXPLAIN keywords";
168*44a87f08Sstephan        //flags["heap"] = "SZ MIN       Memory allocator uses SZ bytes & min allocation MIN";
169*44a87f08Sstephan        flags["incrvacuum"] = "Enable incremenatal vacuum mode";
170*44a87f08Sstephan        //flags["journal"] = "M         Set the journal_mode to M";
171*44a87f08Sstephan        //flags["key"] = "KEY           Set the encryption key to KEY";
172*44a87f08Sstephan        //flags["lookaside"] = "N SZ    Configure lookaside for N slots of SZ bytes each";
173*44a87f08Sstephan        flags["memdb"] = "Use an in-memory database";
174*44a87f08Sstephan        //flags["mmap"] = "SZ           MMAP the first SZ bytes of the database file";
175*44a87f08Sstephan        flags["multithread"] = "Set multithreaded mode";
176*44a87f08Sstephan        flags["nomemstat"] = "Disable memory statistics";
177*44a87f08Sstephan        flags["nosync"] = "Set PRAGMA synchronous=OFF";
178*44a87f08Sstephan        flags["notnull"] = "Add NOT NULL constraints to table columns";
179*44a87f08Sstephan        //flags["output"] = "FILE       Store SQL output in FILE";
180*44a87f08Sstephan        //flags["pagesize"] = "N        Set the page size to N";
181*44a87f08Sstephan        //flags["pcache"] = "N SZ       Configure N pages of pagecache each of size SZ bytes";
182*44a87f08Sstephan        //flags["primarykey"] = "Use PRIMARY KEY instead of UNIQUE where appropriate";
183*44a87f08Sstephan        //flags["repeat"] = "N          Repeat each SELECT N times (default: 1)";
184*44a87f08Sstephan        flags["reprepare"] = "Reprepare each statement upon every invocation";
185*44a87f08Sstephan        //flags["reserve"] = "N         Reserve N bytes on each database page";
186*44a87f08Sstephan        //flags["script"] = "FILE       Write an SQL script for the test into FILE";
187*44a87f08Sstephan        flags["serialized"] = "Set serialized threading mode";
188*44a87f08Sstephan        flags["singlethread"] = "Set single-threaded mode - disables all mutexing";
189*44a87f08Sstephan        flags["sqlonly"] = "No-op.  Only show the SQL that would have been run.";
190*44a87f08Sstephan        flags["shrink"] = "memory     Invoke sqlite3_db_release_memory() frequently.";
191*44a87f08Sstephan        //flags["size"] = "N            Relative test size.  Default=100";
192*44a87f08Sstephan        flags["strict"] = "Use STRICT table where appropriate";
193*44a87f08Sstephan        flags["stats"] = "Show statistics at the end";
194*44a87f08Sstephan        //flags["temp"] = "N            N from 0 to 9.  0: no temp table. 9: all temp tables";
195*44a87f08Sstephan        //flags["testset"] = "T         Run test-set T (main, cte, rtree, orm, fp, debug)";
196*44a87f08Sstephan        flags["trace"] = "Turn on SQL tracing";
197*44a87f08Sstephan        //flags["threads"] = "N         Use up to N threads for sorting";
198*44a87f08Sstephan        /*
199*44a87f08Sstephan          The core API's WASM build does not support UTF16, but in
200*44a87f08Sstephan          this app it's not an issue because the data are not crossing
201*44a87f08Sstephan          JS/WASM boundaries.
202*44a87f08Sstephan        */
203*44a87f08Sstephan        flags["utf16be"] = "Set text encoding to UTF-16BE";
204*44a87f08Sstephan        flags["utf16le"] = "Set text encoding to UTF-16LE";
205*44a87f08Sstephan        flags["verify"] = "Run additional verification steps.";
206*44a87f08Sstephan        flags["without"] = "rowid     Use WITHOUT ROWID where appropriate";
207*44a87f08Sstephan        const preselectedFlags = [
208*44a87f08Sstephan          'singlethread',
209*44a87f08Sstephan          'memdb'
210*44a87f08Sstephan        ];
211*44a87f08Sstephan        Object.keys(flags).sort().forEach(function(f){
212*44a87f08Sstephan          const opt = document.createElement('option');
213*44a87f08Sstephan          eFlags.appendChild(opt);
214*44a87f08Sstephan          const lbl = nbspPad('--'+f)+flags[f];
215*44a87f08Sstephan          //opt.innerText = lbl;
216*44a87f08Sstephan          opt.innerHTML = lbl;
217*44a87f08Sstephan          opt.value = '--'+f;
218*44a87f08Sstephan          if(preselectedFlags.indexOf(f) >= 0) opt.selected = true;
219*44a87f08Sstephan        });
220*44a87f08Sstephan
221*44a87f08Sstephan        const cbReverseLog = E('#cb-reverse-log-order');
222*44a87f08Sstephan        const lblReverseLog = E('#lbl-reverse-log-order');
223*44a87f08Sstephan        if(cbReverseLog.checked){
224*44a87f08Sstephan          lblReverseLog.classList.add('warning');
225*44a87f08Sstephan          eOut.classList.add('reverse');
226*44a87f08Sstephan        }
227*44a87f08Sstephan        cbReverseLog.addEventListener('change', function(){
228*44a87f08Sstephan          if(this.checked){
229*44a87f08Sstephan            eOut.classList.add('reverse');
230*44a87f08Sstephan            lblReverseLog.classList.add('warning');
231*44a87f08Sstephan          }else{
232*44a87f08Sstephan            eOut.classList.remove('reverse');
233*44a87f08Sstephan            lblReverseLog.classList.remove('warning');
234*44a87f08Sstephan          }
235*44a87f08Sstephan        }, false);
236*44a87f08Sstephan        updateSelectedFlags();
237*44a87f08Sstephan      }
238*44a87f08Sstephan      E('#btn-output-clear').addEventListener('click', ()=>{
239*44a87f08Sstephan        eOut.innerText = '';
240*44a87f08Sstephan      });
241*44a87f08Sstephan      E('#btn-reset-flags').addEventListener('click',()=>{
242*44a87f08Sstephan        eFlags.value = '';
243*44a87f08Sstephan        updateSelectedFlags();
244*44a87f08Sstephan      });
245*44a87f08Sstephan      E('#btn-run').addEventListener('click',function(){
246*44a87f08Sstephan        log("Running speedtest1. UI controls will be disabled until it completes.");
247*44a87f08Sstephan        mPost('run', getSelectedFlags());
248*44a87f08Sstephan      });
249*44a87f08Sstephan
250*44a87f08Sstephan      const eControls = E('#ui-controls');
251*44a87f08Sstephan      /** Update Emscripten-related UI elements while loading the module. */
252*44a87f08Sstephan      const updateLoadStatus = function f(text){
253*44a87f08Sstephan        if(!f.last){
254*44a87f08Sstephan          f.last = { text: '', step: 0 };
255*44a87f08Sstephan          const E = (cssSelector)=>document.querySelector(cssSelector);
256*44a87f08Sstephan          f.ui = {
257*44a87f08Sstephan            status: E('#module-status'),
258*44a87f08Sstephan            progress: E('#module-progress'),
259*44a87f08Sstephan            spinner: E('#module-spinner')
260*44a87f08Sstephan          };
261*44a87f08Sstephan        }
262*44a87f08Sstephan        if(text === f.last.text) return;
263*44a87f08Sstephan        f.last.text = text;
264*44a87f08Sstephan        if(f.ui.progress){
265*44a87f08Sstephan          f.ui.progress.value = f.last.step;
266*44a87f08Sstephan          f.ui.progress.max = f.last.step + 1;
267*44a87f08Sstephan        }
268*44a87f08Sstephan        ++f.last.step;
269*44a87f08Sstephan        if(text) {
270*44a87f08Sstephan          f.ui.status.classList.remove('hidden');
271*44a87f08Sstephan          f.ui.status.innerText = text;
272*44a87f08Sstephan        }else{
273*44a87f08Sstephan          if(f.ui.progress){
274*44a87f08Sstephan            f.ui.progress.remove();
275*44a87f08Sstephan            f.ui.spinner.remove();
276*44a87f08Sstephan            delete f.ui.progress;
277*44a87f08Sstephan            delete f.ui.spinner;
278*44a87f08Sstephan          }
279*44a87f08Sstephan          f.ui.status.classList.add('hidden');
280*44a87f08Sstephan        }
281*44a87f08Sstephan      };
282*44a87f08Sstephan
283*44a87f08Sstephan      log("Control-click the flags to (de)select multiple flags.");
284*44a87f08Sstephan      logWarn("\nThe easiest way to try different optimization levels is, from this directory:\n"+
285*44a87f08Sstephan              "  $ make clean; make -e emcc_opt='-O2' speedtest1\n"+
286*44a87f08Sstephan              "Then reload this page. -O2 seems to consistently produce the fastest results.\n");
287*44a87f08Sstephan      logWarn('\nAchtung: the Worker thread overhead slightly reduces the speed',
288*44a87f08Sstephan              'compared to running the same options via speedtest1.html.\n'+
289*44a87f08Sstephan              'TODO: add a link in this app which launches the main-thread',
290*44a87f08Sstephan              'version with the same flags.\n');
291*44a87f08Sstephan
292*44a87f08Sstephan      W.onmessage = function(msg){
293*44a87f08Sstephan        msg = msg.data;
294*44a87f08Sstephan        switch(msg.type){
295*44a87f08Sstephan            case 'ready': log("Worker is ready."); eControls.classList.remove('hidden'); break;
296*44a87f08Sstephan            case 'stdout': log(msg.data); break;
297*44a87f08Sstephan            case 'stdout': logErr(msg.data); break;
298*44a87f08Sstephan            case 'run-start':
299*44a87f08Sstephan              eControls.disabled = true;
300*44a87f08Sstephan              log("Running speedtest1 with argv =",msg.data.join(' '));
301*44a87f08Sstephan              break;
302*44a87f08Sstephan            case 'run-end': log("speedtest1 finished.");
303*44a87f08Sstephan              eControls.disabled = false;
304*44a87f08Sstephan              // app output is in msg.data
305*44a87f08Sstephan              break;
306*44a87f08Sstephan            case 'error': logErr(msg.data); break;
307*44a87f08Sstephan            case 'load-status': updateLoadStatus(msg.data); break;
308*44a87f08Sstephan            default:
309*44a87f08Sstephan              logErr("Unhandled worker message type:",arguments[0]);
310*44a87f08Sstephan              break;
311*44a87f08Sstephan        }
312*44a87f08Sstephan      };
313*44a87f08Sstephan    })();</script>
314*44a87f08Sstephan  </body>
315*44a87f08Sstephan</html>
316