richards.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. // Copyright 2013 the V8 project authors. All rights reserved.
  2. // Redistribution and use in source and binary forms, with or without
  3. // modification, are permitted provided that the following conditions are
  4. // met:
  5. //
  6. // * Redistributions of source code must retain the above copyright
  7. // notice, this list of conditions and the following disclaimer.
  8. // * Redistributions in binary form must reproduce the above
  9. // copyright notice, this list of conditions and the following
  10. // disclaimer in the documentation and/or other materials provided
  11. // with the distribution.
  12. // * Neither the name of Google Inc. nor the names of its
  13. // contributors may be used to endorse or promote products derived
  14. // from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. // Performance.now is used in latency benchmarks, the fallback is Date.now.
  28. var performance = performance || {};
  29. performance.now = (function() {
  30. return performance.now ||
  31. performance.mozNow ||
  32. performance.msNow ||
  33. performance.oNow ||
  34. performance.webkitNow ||
  35. Date.now;
  36. })();
  37. // Simple framework for running the benchmark suites and
  38. // computing a score based on the timing measurements.
  39. // A benchmark has a name (string) and a function that will be run to
  40. // do the performance measurement. The optional setup and tearDown
  41. // arguments are functions that will be invoked before and after
  42. // running the benchmark, but the running time of these functions will
  43. // not be accounted for in the benchmark score.
  44. function Benchmark(name, doWarmup, doDeterministic, deterministicIterations,
  45. run, setup, tearDown, rmsResult, minIterations) {
  46. this.name = name;
  47. this.doWarmup = doWarmup;
  48. this.doDeterministic = doDeterministic;
  49. this.deterministicIterations = deterministicIterations;
  50. this.run = run;
  51. this.Setup = setup ? setup : function() { };
  52. this.TearDown = tearDown ? tearDown : function() { };
  53. this.rmsResult = rmsResult ? rmsResult : null;
  54. this.minIterations = minIterations ? minIterations : 32;
  55. }
  56. // Benchmark results hold the benchmark and the measured time used to
  57. // run the benchmark. The benchmark score is computed later once a
  58. // full benchmark suite has run to completion. If latency is set to 0
  59. // then there is no latency score for this benchmark.
  60. function BenchmarkResult(benchmark, time, latency) {
  61. this.benchmark = benchmark;
  62. this.time = time;
  63. this.latency = latency;
  64. }
  65. // Automatically convert results to numbers. Used by the geometric
  66. // mean computation.
  67. BenchmarkResult.prototype.valueOf = function() {
  68. return this.time;
  69. }
  70. // Suites of benchmarks consist of a name and the set of benchmarks in
  71. // addition to the reference timing that the final score will be based
  72. // on. This way, all scores are relative to a reference run and higher
  73. // scores implies better performance.
  74. function BenchmarkSuite(name, reference, benchmarks) {
  75. this.name = name;
  76. this.reference = reference;
  77. this.benchmarks = benchmarks;
  78. BenchmarkSuite.suites.push(this);
  79. }
  80. // Keep track of all declared benchmark suites.
  81. BenchmarkSuite.suites = [];
  82. // Scores are not comparable across versions. Bump the version if
  83. // you're making changes that will affect that scores, e.g. if you add
  84. // a new benchmark or change an existing one.
  85. BenchmarkSuite.version = '9';
  86. // Defines global benchsuite running mode that overrides benchmark suite
  87. // behavior. Intended to be set by the benchmark driver. Undefined
  88. // values here allow a benchmark to define behaviour itself.
  89. BenchmarkSuite.config = {
  90. doWarmup: undefined,
  91. doDeterministic: undefined
  92. };
  93. // Override the alert function to throw an exception instead.
  94. alert = function(s) {
  95. throw "Alert called with argument: " + s;
  96. };
  97. // To make the benchmark results predictable, we replace Math.random
  98. // with a 100% deterministic alternative.
  99. BenchmarkSuite.ResetRNG = function() {
  100. Math.random = (function() {
  101. var seed = 49734321;
  102. return function() {
  103. // Robert Jenkins' 32 bit integer hash function.
  104. seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
  105. seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
  106. seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
  107. seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
  108. seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
  109. seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
  110. return (seed & 0xfffffff) / 0x10000000;
  111. };
  112. })();
  113. }
  114. // Runs all registered benchmark suites and optionally yields between
  115. // each individual benchmark to avoid running for too long in the
  116. // context of browsers. Once done, the final score is reported to the
  117. // runner.
  118. BenchmarkSuite.RunSuites = function(runner, skipBenchmarks) {
  119. skipBenchmarks = typeof skipBenchmarks === 'undefined' ? [] : skipBenchmarks;
  120. var continuation = null;
  121. var suites = BenchmarkSuite.suites;
  122. var length = suites.length;
  123. BenchmarkSuite.scores = [];
  124. var index = 0;
  125. function RunStep() {
  126. while (continuation || index < length) {
  127. if (continuation) {
  128. continuation = continuation();
  129. } else {
  130. var suite = suites[index++];
  131. if (runner.NotifyStart) runner.NotifyStart(suite.name);
  132. if (skipBenchmarks.indexOf(suite.name) > -1) {
  133. suite.NotifySkipped(runner);
  134. } else {
  135. continuation = suite.RunStep(runner);
  136. }
  137. }
  138. if (continuation && typeof window != 'undefined' && window.setTimeout) {
  139. window.setTimeout(RunStep, 25);
  140. return;
  141. }
  142. }
  143. // show final result
  144. if (runner.NotifyScore) {
  145. var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);
  146. var formatted = BenchmarkSuite.FormatScore(100 * score);
  147. runner.NotifyScore(formatted);
  148. }
  149. }
  150. RunStep();
  151. }
  152. // Counts the total number of registered benchmarks. Useful for
  153. // showing progress as a percentage.
  154. BenchmarkSuite.CountBenchmarks = function() {
  155. var result = 0;
  156. var suites = BenchmarkSuite.suites;
  157. for (var i = 0; i < suites.length; i++) {
  158. result += suites[i].benchmarks.length;
  159. }
  160. return result;
  161. }
  162. // Computes the geometric mean of a set of numbers.
  163. BenchmarkSuite.GeometricMean = function(numbers) {
  164. var log = 0;
  165. for (var i = 0; i < numbers.length; i++) {
  166. log += Math.log(numbers[i]);
  167. }
  168. return Math.pow(Math.E, log / numbers.length);
  169. }
  170. // Computes the geometric mean of a set of throughput time measurements.
  171. BenchmarkSuite.GeometricMeanTime = function(measurements) {
  172. var log = 0;
  173. for (var i = 0; i < measurements.length; i++) {
  174. log += Math.log(measurements[i].time);
  175. }
  176. return Math.pow(Math.E, log / measurements.length);
  177. }
  178. // Computes the geometric mean of a set of rms measurements.
  179. BenchmarkSuite.GeometricMeanLatency = function(measurements) {
  180. var log = 0;
  181. var hasLatencyResult = false;
  182. for (var i = 0; i < measurements.length; i++) {
  183. if (measurements[i].latency != 0) {
  184. log += Math.log(measurements[i].latency);
  185. hasLatencyResult = true;
  186. }
  187. }
  188. if (hasLatencyResult) {
  189. return Math.pow(Math.E, log / measurements.length);
  190. } else {
  191. return 0;
  192. }
  193. }
  194. // Converts a score value to a string with at least three significant
  195. // digits.
  196. BenchmarkSuite.FormatScore = function(value) {
  197. if (value > 100) {
  198. return value.toFixed(0);
  199. } else {
  200. return value.toPrecision(3);
  201. }
  202. }
  203. // Notifies the runner that we're done running a single benchmark in
  204. // the benchmark suite. This can be useful to report progress.
  205. BenchmarkSuite.prototype.NotifyStep = function(result) {
  206. this.results.push(result);
  207. if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);
  208. }
  209. // Notifies the runner that we're done with running a suite and that
  210. // we have a result which can be reported to the user if needed.
  211. BenchmarkSuite.prototype.NotifyResult = function() {
  212. var mean = BenchmarkSuite.GeometricMeanTime(this.results);
  213. var score = this.reference[0] / mean;
  214. BenchmarkSuite.scores.push(score);
  215. if (this.runner.NotifyResult) {
  216. var formatted = BenchmarkSuite.FormatScore(100 * score);
  217. this.runner.NotifyResult(this.name, formatted);
  218. }
  219. if (this.reference.length == 2) {
  220. var meanLatency = BenchmarkSuite.GeometricMeanLatency(this.results);
  221. if (meanLatency != 0) {
  222. var scoreLatency = this.reference[1] / meanLatency;
  223. BenchmarkSuite.scores.push(scoreLatency);
  224. if (this.runner.NotifyResult) {
  225. var formattedLatency = BenchmarkSuite.FormatScore(100 * scoreLatency)
  226. this.runner.NotifyResult(this.name + "Latency", formattedLatency);
  227. }
  228. }
  229. }
  230. }
  231. BenchmarkSuite.prototype.NotifySkipped = function(runner) {
  232. BenchmarkSuite.scores.push(1); // push default reference score.
  233. if (runner.NotifyResult) {
  234. runner.NotifyResult(this.name, "Skipped");
  235. }
  236. }
  237. // Notifies the runner that running a benchmark resulted in an error.
  238. BenchmarkSuite.prototype.NotifyError = function(error) {
  239. if (this.runner.NotifyError) {
  240. this.runner.NotifyError(this.name, error);
  241. }
  242. if (this.runner.NotifyStep) {
  243. this.runner.NotifyStep(this.name);
  244. }
  245. }
  246. // Runs a single benchmark for at least a second and computes the
  247. // average time it takes to run a single iteration.
  248. BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark, data) {
  249. var config = BenchmarkSuite.config;
  250. var doWarmup = config.doWarmup !== undefined
  251. ? config.doWarmup
  252. : benchmark.doWarmup;
  253. var doDeterministic = config.doDeterministic !== undefined
  254. ? config.doDeterministic
  255. : benchmark.doDeterministic;
  256. function Measure(data) {
  257. var elapsed = 0;
  258. var start = new Date();
  259. // Run either for 1 second or for the number of iterations specified
  260. // by minIterations, depending on the config flag doDeterministic.
  261. for (var i = 0; (doDeterministic ?
  262. i<benchmark.deterministicIterations : elapsed < 1000); i++) {
  263. benchmark.run();
  264. elapsed = new Date() - start;
  265. }
  266. if (data != null) {
  267. data.runs += i;
  268. data.elapsed += elapsed;
  269. }
  270. }
  271. // Sets up data in order to skip or not the warmup phase.
  272. if (!doWarmup && data == null) {
  273. data = { runs: 0, elapsed: 0 };
  274. }
  275. if (data == null) {
  276. Measure(null);
  277. return { runs: 0, elapsed: 0 };
  278. } else {
  279. Measure(data);
  280. // If we've run too few iterations, we continue for another second.
  281. if (data.runs < benchmark.minIterations) return data;
  282. var usec = (data.elapsed * 1000) / data.runs;
  283. var rms = (benchmark.rmsResult != null) ? benchmark.rmsResult() : 0;
  284. this.NotifyStep(new BenchmarkResult(benchmark, usec, rms));
  285. return null;
  286. }
  287. }
  288. // This function starts running a suite, but stops between each
  289. // individual benchmark in the suite and returns a continuation
  290. // function which can be invoked to run the next benchmark. Once the
  291. // last benchmark has been executed, null is returned.
  292. BenchmarkSuite.prototype.RunStep = function(runner) {
  293. BenchmarkSuite.ResetRNG();
  294. this.results = [];
  295. this.runner = runner;
  296. var length = this.benchmarks.length;
  297. var index = 0;
  298. var suite = this;
  299. var data;
  300. // Run the setup, the actual benchmark, and the tear down in three
  301. // separate steps to allow the framework to yield between any of the
  302. // steps.
  303. function RunNextSetup() {
  304. if (index < length) {
  305. try {
  306. suite.benchmarks[index].Setup();
  307. } catch (e) {
  308. suite.NotifyError(e);
  309. return null;
  310. }
  311. return RunNextBenchmark;
  312. }
  313. suite.NotifyResult();
  314. return null;
  315. }
  316. function RunNextBenchmark() {
  317. try {
  318. data = suite.RunSingleBenchmark(suite.benchmarks[index], data);
  319. } catch (e) {
  320. suite.NotifyError(e);
  321. return null;
  322. }
  323. // If data is null, we're done with this benchmark.
  324. return (data == null) ? RunNextTearDown : RunNextBenchmark();
  325. }
  326. function RunNextTearDown() {
  327. try {
  328. suite.benchmarks[index++].TearDown();
  329. } catch (e) {
  330. suite.NotifyError(e);
  331. return null;
  332. }
  333. return RunNextSetup;
  334. }
  335. // Start out running the setup.
  336. return RunNextSetup();
  337. }
  338. // Copyright 2006-2008 the V8 project authors. All rights reserved.
  339. // Redistribution and use in source and binary forms, with or without
  340. // modification, are permitted provided that the following conditions are
  341. // met:
  342. //
  343. // * Redistributions of source code must retain the above copyright
  344. // notice, this list of conditions and the following disclaimer.
  345. // * Redistributions in binary form must reproduce the above
  346. // copyright notice, this list of conditions and the following
  347. // disclaimer in the documentation and/or other materials provided
  348. // with the distribution.
  349. // * Neither the name of Google Inc. nor the names of its
  350. // contributors may be used to endorse or promote products derived
  351. // from this software without specific prior written permission.
  352. //
  353. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  354. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  355. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  356. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  357. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  358. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  359. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  360. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  361. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  362. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  363. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  364. // This is a JavaScript implementation of the Richards
  365. // benchmark from:
  366. //
  367. // http://www.cl.cam.ac.uk/~mr10/Bench.html
  368. //
  369. // The benchmark was originally implemented in BCPL by
  370. // Martin Richards.
  371. var Richards = new BenchmarkSuite('Richards', [35302], [
  372. new Benchmark("Richards", true, false, 8200, runRichards)
  373. ]);
  374. /**
  375. * The Richards benchmark simulates the task dispatcher of an
  376. * operating system.
  377. **/
  378. function runRichards() {
  379. var scheduler = new Scheduler();
  380. scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
  381. var queue = new Packet(null, ID_WORKER, KIND_WORK);
  382. queue = new Packet(queue, ID_WORKER, KIND_WORK);
  383. scheduler.addWorkerTask(ID_WORKER, 1000, queue);
  384. queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
  385. queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
  386. queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
  387. scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
  388. queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
  389. queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
  390. queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
  391. scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
  392. scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
  393. scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
  394. scheduler.schedule();
  395. if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||
  396. scheduler.holdCount != EXPECTED_HOLD_COUNT) {
  397. var msg =
  398. "Error during execution: queueCount = " + scheduler.queueCount +
  399. ", holdCount = " + scheduler.holdCount + ".";
  400. throw new Error(msg);
  401. }
  402. }
  403. var COUNT = 1000;
  404. /**
  405. * These two constants specify how many times a packet is queued and
  406. * how many times a task is put on hold in a correct run of richards.
  407. * They don't have any meaning a such but are characteristic of a
  408. * correct run so if the actual queue or hold count is different from
  409. * the expected there must be a bug in the implementation.
  410. **/
  411. var EXPECTED_QUEUE_COUNT = 2322;
  412. var EXPECTED_HOLD_COUNT = 928;
  413. /**
  414. * A scheduler can be used to schedule a set of tasks based on their relative
  415. * priorities. Scheduling is done by maintaining a list of task control blocks
  416. * which holds tasks and the data queue they are processing.
  417. * @constructor
  418. */
  419. function Scheduler() {
  420. this.queueCount = 0;
  421. this.holdCount = 0;
  422. this.blocks = new Array(NUMBER_OF_IDS);
  423. this.list = null;
  424. this.currentTcb = null;
  425. this.currentId = null;
  426. }
  427. var ID_IDLE = 0;
  428. var ID_WORKER = 1;
  429. var ID_HANDLER_A = 2;
  430. var ID_HANDLER_B = 3;
  431. var ID_DEVICE_A = 4;
  432. var ID_DEVICE_B = 5;
  433. var NUMBER_OF_IDS = 6;
  434. var KIND_DEVICE = 0;
  435. var KIND_WORK = 1;
  436. /**
  437. * Add an idle task to this scheduler.
  438. * @param {int} id the identity of the task
  439. * @param {int} priority the task's priority
  440. * @param {Packet} queue the queue of work to be processed by the task
  441. * @param {int} count the number of times to schedule the task
  442. */
  443. Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
  444. this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
  445. };
  446. /**
  447. * Add a work task to this scheduler.
  448. * @param {int} id the identity of the task
  449. * @param {int} priority the task's priority
  450. * @param {Packet} queue the queue of work to be processed by the task
  451. */
  452. Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
  453. this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
  454. };
  455. /**
  456. * Add a handler task to this scheduler.
  457. * @param {int} id the identity of the task
  458. * @param {int} priority the task's priority
  459. * @param {Packet} queue the queue of work to be processed by the task
  460. */
  461. Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
  462. this.addTask(id, priority, queue, new HandlerTask(this));
  463. };
  464. /**
  465. * Add a handler task to this scheduler.
  466. * @param {int} id the identity of the task
  467. * @param {int} priority the task's priority
  468. * @param {Packet} queue the queue of work to be processed by the task
  469. */
  470. Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
  471. this.addTask(id, priority, queue, new DeviceTask(this))
  472. };
  473. /**
  474. * Add the specified task and mark it as running.
  475. * @param {int} id the identity of the task
  476. * @param {int} priority the task's priority
  477. * @param {Packet} queue the queue of work to be processed by the task
  478. * @param {Task} task the task to add
  479. */
  480. Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
  481. this.addTask(id, priority, queue, task);
  482. this.currentTcb.setRunning();
  483. };
  484. /**
  485. * Add the specified task to this scheduler.
  486. * @param {int} id the identity of the task
  487. * @param {int} priority the task's priority
  488. * @param {Packet} queue the queue of work to be processed by the task
  489. * @param {Task} task the task to add
  490. */
  491. Scheduler.prototype.addTask = function (id, priority, queue, task) {
  492. this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
  493. this.list = this.currentTcb;
  494. this.blocks[id] = this.currentTcb;
  495. };
  496. /**
  497. * Execute the tasks managed by this scheduler.
  498. */
  499. Scheduler.prototype.schedule = function () {
  500. this.currentTcb = this.list;
  501. while (this.currentTcb != null) {
  502. if (this.currentTcb.isHeldOrSuspended()) {
  503. this.currentTcb = this.currentTcb.link;
  504. } else {
  505. this.currentId = this.currentTcb.id;
  506. this.currentTcb = this.currentTcb.run();
  507. }
  508. }
  509. };
  510. /**
  511. * Release a task that is currently blocked and return the next block to run.
  512. * @param {int} id the id of the task to suspend
  513. */
  514. Scheduler.prototype.release = function (id) {
  515. var tcb = this.blocks[id];
  516. if (tcb == null) return tcb;
  517. tcb.markAsNotHeld();
  518. if (tcb.priority > this.currentTcb.priority) {
  519. return tcb;
  520. } else {
  521. return this.currentTcb;
  522. }
  523. };
  524. /**
  525. * Block the currently executing task and return the next task control block
  526. * to run. The blocked task will not be made runnable until it is explicitly
  527. * released, even if new work is added to it.
  528. */
  529. Scheduler.prototype.holdCurrent = function () {
  530. this.holdCount++;
  531. this.currentTcb.markAsHeld();
  532. return this.currentTcb.link;
  533. };
  534. /**
  535. * Suspend the currently executing task and return the next task control block
  536. * to run. If new work is added to the suspended task it will be made runnable.
  537. */
  538. Scheduler.prototype.suspendCurrent = function () {
  539. this.currentTcb.markAsSuspended();
  540. return this.currentTcb;
  541. };
  542. /**
  543. * Add the specified packet to the end of the worklist used by the task
  544. * associated with the packet and make the task runnable if it is currently
  545. * suspended.
  546. * @param {Packet} packet the packet to add
  547. */
  548. Scheduler.prototype.queue = function (packet) {
  549. var t = this.blocks[packet.id];
  550. if (t == null) return t;
  551. this.queueCount++;
  552. packet.link = null;
  553. packet.id = this.currentId;
  554. return t.checkPriorityAdd(this.currentTcb, packet);
  555. };
  556. /**
  557. * A task control block manages a task and the queue of work packages associated
  558. * with it.
  559. * @param {TaskControlBlock} link the preceding block in the linked block list
  560. * @param {int} id the id of this block
  561. * @param {int} priority the priority of this block
  562. * @param {Packet} queue the queue of packages to be processed by the task
  563. * @param {Task} task the task
  564. * @constructor
  565. */
  566. function TaskControlBlock(link, id, priority, queue, task) {
  567. this.link = link;
  568. this.id = id;
  569. this.priority = priority;
  570. this.queue = queue;
  571. this.task = task;
  572. if (queue == null) {
  573. this.state = STATE_SUSPENDED;
  574. } else {
  575. this.state = STATE_SUSPENDED_RUNNABLE;
  576. }
  577. }
  578. /**
  579. * The task is running and is currently scheduled.
  580. */
  581. var STATE_RUNNING = 0;
  582. /**
  583. * The task has packets left to process.
  584. */
  585. var STATE_RUNNABLE = 1;
  586. /**
  587. * The task is not currently running. The task is not blocked as such and may
  588. * be started by the scheduler.
  589. */
  590. var STATE_SUSPENDED = 2;
  591. /**
  592. * The task is blocked and cannot be run until it is explicitly released.
  593. */
  594. var STATE_HELD = 4;
  595. var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
  596. var STATE_NOT_HELD = ~STATE_HELD;
  597. TaskControlBlock.prototype.setRunning = function () {
  598. this.state = STATE_RUNNING;
  599. };
  600. TaskControlBlock.prototype.markAsNotHeld = function () {
  601. this.state = this.state & STATE_NOT_HELD;
  602. };
  603. TaskControlBlock.prototype.markAsHeld = function () {
  604. this.state = this.state | STATE_HELD;
  605. };
  606. TaskControlBlock.prototype.isHeldOrSuspended = function () {
  607. return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
  608. };
  609. TaskControlBlock.prototype.markAsSuspended = function () {
  610. this.state = this.state | STATE_SUSPENDED;
  611. };
  612. TaskControlBlock.prototype.markAsRunnable = function () {
  613. this.state = this.state | STATE_RUNNABLE;
  614. };
  615. /**
  616. * Runs this task, if it is ready to be run, and returns the next task to run.
  617. */
  618. TaskControlBlock.prototype.run = function () {
  619. var packet;
  620. if (this.state == STATE_SUSPENDED_RUNNABLE) {
  621. packet = this.queue;
  622. this.queue = packet.link;
  623. if (this.queue == null) {
  624. this.state = STATE_RUNNING;
  625. } else {
  626. this.state = STATE_RUNNABLE;
  627. }
  628. } else {
  629. packet = null;
  630. }
  631. return this.task.run(packet);
  632. };
  633. /**
  634. * Adds a packet to the worklist of this block's task, marks this as runnable if
  635. * necessary, and returns the next runnable object to run (the one
  636. * with the highest priority).
  637. */
  638. TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
  639. if (this.queue == null) {
  640. this.queue = packet;
  641. this.markAsRunnable();
  642. if (this.priority > task.priority) return this;
  643. } else {
  644. this.queue = packet.addTo(this.queue);
  645. }
  646. return task;
  647. };
  648. TaskControlBlock.prototype.toString = function () {
  649. return "tcb { " + this.task + "@" + this.state + " }";
  650. };
  651. /**
  652. * An idle task doesn't do any work itself but cycles control between the two
  653. * device tasks.
  654. * @param {Scheduler} scheduler the scheduler that manages this task
  655. * @param {int} v1 a seed value that controls how the device tasks are scheduled
  656. * @param {int} count the number of times this task should be scheduled
  657. * @constructor
  658. */
  659. function IdleTask(scheduler, v1, count) {
  660. this.scheduler = scheduler;
  661. this.v1 = v1;
  662. this.count = count;
  663. }
  664. IdleTask.prototype.run = function (packet) {
  665. this.count--;
  666. if (this.count == 0) return this.scheduler.holdCurrent();
  667. if ((this.v1 & 1) == 0) {
  668. this.v1 = this.v1 >> 1;
  669. return this.scheduler.release(ID_DEVICE_A);
  670. } else {
  671. this.v1 = (this.v1 >> 1) ^ 0xD008;
  672. return this.scheduler.release(ID_DEVICE_B);
  673. }
  674. };
  675. IdleTask.prototype.toString = function () {
  676. return "IdleTask"
  677. };
  678. /**
  679. * A task that suspends itself after each time it has been run to simulate
  680. * waiting for data from an external device.
  681. * @param {Scheduler} scheduler the scheduler that manages this task
  682. * @constructor
  683. */
  684. function DeviceTask(scheduler) {
  685. this.scheduler = scheduler;
  686. this.v1 = null;
  687. }
  688. DeviceTask.prototype.run = function (packet) {
  689. if (packet == null) {
  690. if (this.v1 == null) return this.scheduler.suspendCurrent();
  691. var v = this.v1;
  692. this.v1 = null;
  693. return this.scheduler.queue(v);
  694. } else {
  695. this.v1 = packet;
  696. return this.scheduler.holdCurrent();
  697. }
  698. };
  699. DeviceTask.prototype.toString = function () {
  700. return "DeviceTask";
  701. };
  702. /**
  703. * A task that manipulates work packets.
  704. * @param {Scheduler} scheduler the scheduler that manages this task
  705. * @param {int} v1 a seed used to specify how work packets are manipulated
  706. * @param {int} v2 another seed used to specify how work packets are manipulated
  707. * @constructor
  708. */
  709. function WorkerTask(scheduler, v1, v2) {
  710. this.scheduler = scheduler;
  711. this.v1 = v1;
  712. this.v2 = v2;
  713. }
  714. WorkerTask.prototype.run = function (packet) {
  715. if (packet == null) {
  716. return this.scheduler.suspendCurrent();
  717. } else {
  718. if (this.v1 == ID_HANDLER_A) {
  719. this.v1 = ID_HANDLER_B;
  720. } else {
  721. this.v1 = ID_HANDLER_A;
  722. }
  723. packet.id = this.v1;
  724. packet.a1 = 0;
  725. for (var i = 0; i < DATA_SIZE; i++) {
  726. this.v2++;
  727. if (this.v2 > 26) this.v2 = 1;
  728. packet.a2[i] = this.v2;
  729. }
  730. return this.scheduler.queue(packet);
  731. }
  732. };
  733. WorkerTask.prototype.toString = function () {
  734. return "WorkerTask";
  735. };
  736. /**
  737. * A task that manipulates work packets and then suspends itself.
  738. * @param {Scheduler} scheduler the scheduler that manages this task
  739. * @constructor
  740. */
  741. function HandlerTask(scheduler) {
  742. this.scheduler = scheduler;
  743. this.v1 = null;
  744. this.v2 = null;
  745. }
  746. HandlerTask.prototype.run = function (packet) {
  747. if (packet != null) {
  748. if (packet.kind == KIND_WORK) {
  749. this.v1 = packet.addTo(this.v1);
  750. } else {
  751. this.v2 = packet.addTo(this.v2);
  752. }
  753. }
  754. if (this.v1 != null) {
  755. var count = this.v1.a1;
  756. var v;
  757. if (count < DATA_SIZE) {
  758. if (this.v2 != null) {
  759. v = this.v2;
  760. this.v2 = this.v2.link;
  761. v.a1 = this.v1.a2[count];
  762. this.v1.a1 = count + 1;
  763. return this.scheduler.queue(v);
  764. }
  765. } else {
  766. v = this.v1;
  767. this.v1 = this.v1.link;
  768. return this.scheduler.queue(v);
  769. }
  770. }
  771. return this.scheduler.suspendCurrent();
  772. };
  773. HandlerTask.prototype.toString = function () {
  774. return "HandlerTask";
  775. };
  776. /* --- *
  777. * P a c k e t
  778. * --- */
  779. var DATA_SIZE = 4;
  780. /**
  781. * A simple package of data that is manipulated by the tasks. The exact layout
  782. * of the payload data carried by a packet is not importaint, and neither is the
  783. * nature of the work performed on packets by the tasks.
  784. *
  785. * Besides carrying data, packets form linked lists and are hence used both as
  786. * data and worklists.
  787. * @param {Packet} link the tail of the linked list of packets
  788. * @param {int} id an ID for this packet
  789. * @param {int} kind the type of this packet
  790. * @constructor
  791. */
  792. function Packet(link, id, kind) {
  793. this.link = link;
  794. this.id = id;
  795. this.kind = kind;
  796. this.a1 = 0;
  797. this.a2 = new Array(DATA_SIZE);
  798. }
  799. /**
  800. * Add this packet to the end of a worklist, and return the worklist.
  801. * @param {Packet} queue the worklist to add this packet to
  802. */
  803. Packet.prototype.addTo = function (queue) {
  804. this.link = null;
  805. if (queue == null) return this;
  806. var peek, next = queue;
  807. while ((peek = next.link) != null)
  808. next = peek;
  809. next.link = this;
  810. return queue;
  811. };
  812. Packet.prototype.toString = function () {
  813. return "Packet";
  814. };
  815. ////////////////////////////////////////////////////////////////////////////////
  816. // Runner
  817. ////////////////////////////////////////////////////////////////////////////////
  818. var success = true;
  819. function NotifyStart(name) {
  820. }
  821. function NotifyError(name, error) {
  822. WScript.Echo(name + " : ERROR : " + error.stack);
  823. success = false;
  824. }
  825. function NotifyResult(name, score) {
  826. if (success) {
  827. WScript.Echo("### SCORE:", score);
  828. }
  829. }
  830. function NotifyScore(score) {
  831. }
  832. BenchmarkSuite.RunSuites({
  833. NotifyStart: NotifyStart,
  834. NotifyError: NotifyError,
  835. NotifyResult: NotifyResult,
  836. NotifyScore: NotifyScore
  837. });