carlopi / js-opt-benchmark

Benchmarks for "A JavaScript optimizing compiler"

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is the compiler released?

aminya opened this issue · comments

I wanted to try the benchmarks, but I can't find the compiler. Isn't it released yet?

Hi @aminya, the compiler is currently 2 tools:

For now there is not much to be tried at this stage, did you had any specific idea on what to try this on?

Thanks! I am really excited to try this. There are many projects that could benefit from the potential speed-ups.

This can also be a new backend for AssemblyScript. AssemblyScript compiler has a JS output option, but the output usually has a lot of excess things that are useful for WASM but not JavaScript.

This is what AssemblyScript generates when the --jsfile is used for the following simple code:

export function for_array64(arr: Float64Array): f64 {
  let sum = 0.0
  for (let i = 0, l = arr.length; i < l; ++i) {
    sum += arr[i]
  }
  return sum
}

Output with the following scripts

  "scripts": {
    "jsbuild": "asc assembly/index.ts --jsFile build/index.js -O --noAssert --runtime=stub --noValidate --noExportMemory --disable",
    "terser": "terser --config-file ./.terserrc.json --output ./build/index.tersed.js -- build/index.js",
    "format.output": "prettier --write build"
  },

is:

import { abort } from "env"

var bufferView
var base64ReverseLookup = new Uint8Array(123 /*'z'+1*/)
for (var i = 25; i >= 0; --i) {
  base64ReverseLookup[48 + i] = 52 + i // '0-9'
  base64ReverseLookup[65 + i] = i // 'A-Z'
  base64ReverseLookup[97 + i] = 26 + i // 'a-z'
}
base64ReverseLookup[43] = 62 // '+'
base64ReverseLookup[47] = 63 // '/'
/** @noinline Inlining this function would mean expanding the base64 string 4x times in the source code, which Closure seems to be happy to do. */
function base64DecodeToExistingUint8Array(uint8Array, offset, b64) {
  var b1,
    b2,
    i = 0,
    j = offset,
    bLength = b64.length,
    end = offset + ((bLength * 3) >> 2) - (b64[bLength - 2] == "=") - (b64[bLength - 1] == "=")
  for (; i < bLength; i += 4) {
    b1 = base64ReverseLookup[b64.charCodeAt(i + 1)]
    b2 = base64ReverseLookup[b64.charCodeAt(i + 2)]
    uint8Array[j++] = (base64ReverseLookup[b64.charCodeAt(i)] << 2) | (b1 >> 4)
    if (j < end) uint8Array[j++] = (b1 << 4) | (b2 >> 2)
    if (j < end) uint8Array[j++] = (b2 << 6) | base64ReverseLookup[b64.charCodeAt(i + 3)]
  }
}
function initActiveSegments(imports) {
  base64DecodeToExistingUint8Array(bufferView, 1036, "PA==")
  base64DecodeToExistingUint8Array(bufferView, 1048, "AQAAACQAAABJAG4AZABlAHgAIABvAHUAdAAgAG8AZgAgAHIAYQBuAGcAZQ==")
  base64DecodeToExistingUint8Array(bufferView, 1100, "PA==")
  base64DecodeToExistingUint8Array(bufferView, 1112, "AQAAACQAAAB+AGwAaQBiAC8AdAB5AHAAZQBkAGEAcgByAGEAeQAuAHQAcw==")
}
function asmFunc(env) {
  var buffer = new ArrayBuffer(65536)
  var HEAP8 = new Int8Array(buffer)
  var HEAP16 = new Int16Array(buffer)
  var HEAP32 = new Int32Array(buffer)
  var HEAPU8 = new Uint8Array(buffer)
  var HEAPU16 = new Uint16Array(buffer)
  var HEAPU32 = new Uint32Array(buffer)
  var HEAPF32 = new Float32Array(buffer)
  var HEAPF64 = new Float64Array(buffer)
  var Math_imul = Math.imul
  var Math_fround = Math.fround
  var Math_abs = Math.abs
  var Math_clz32 = Math.clz32
  var Math_min = Math.min
  var Math_max = Math.max
  var Math_floor = Math.floor
  var Math_ceil = Math.ceil
  var Math_trunc = Math.trunc
  var Math_sqrt = Math.sqrt
  var abort = env.abort
  var nan = NaN
  var infinity = Infinity
  var $lib_builtins_abort = env.abort
  function assembly_index_for_array64($0) {
    $0 = $0 | 0
    var $1 = 0,
      $2 = 0.0,
      $3 = 0
    $3 = (HEAP32[(($0 + 8) | 0) >> 2] >>> 3) | 0
    for_loop_0: while (1) {
      if (($1 | 0) < ($3 | 0)) {
        if (((HEAP32[(($0 + 8) | 0) >> 2] >>> 3) | 0) >>> 0 <= $1 >>> 0) {
          $lib_builtins_abort(1056 | 0, 1120 | 0, 1374 | 0, 64 | 0)
          abort()
        }
        $2 = $2 + HEAPF64[((HEAP32[(($0 + 4) | 0) >> 2] + (($1 << 3) | 0)) | 0) >> 3]
        $1 = ($1 + 1) | 0
        continue for_loop_0
      }
      break for_loop_0
    }
    return +$2
  }

  bufferView = HEAPU8
  initActiveSegments(env)
  function __wasm_memory_size() {
    return (buffer.byteLength / 65536) | 0
  }

  function __wasm_memory_grow(pagesToAdd) {
    pagesToAdd = pagesToAdd | 0
    var oldPages = __wasm_memory_size() | 0
    var newPages = (oldPages + pagesToAdd) | 0
    if (oldPages < newPages && newPages < 65536) {
      var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536))
      var newHEAP8 = new Int8Array(newBuffer)
      newHEAP8.set(HEAP8)
      HEAP8 = new Int8Array(newBuffer)
      HEAP16 = new Int16Array(newBuffer)
      HEAP32 = new Int32Array(newBuffer)
      HEAPU8 = new Uint8Array(newBuffer)
      HEAPU16 = new Uint16Array(newBuffer)
      HEAPU32 = new Uint32Array(newBuffer)
      HEAPF32 = new Float32Array(newBuffer)
      HEAPF64 = new Float64Array(newBuffer)
      buffer = newBuffer
      bufferView = HEAPU8
    }
    return oldPages
  }

  return {
    for_array64: assembly_index_for_array64,
  }
}

var retasmFunc = asmFunc({
  abort: function () {
    throw new Error("abort")
  },
  abort,
})
export var for_array64 = retasmFunc.for_array64

If I run terser on the output with the following config:

{
  "module": true,
  "compress": {
    "ecma": "2018",
    "toplevel": false,
    "hoist_vars": false,
    "hoist_funs": true,
    "pure_getters": true,
    "unsafe": true,
    "unsafe_arrows": true,
    "unsafe_comps": true,
    "unsafe_Function": true,
    "unsafe_math": true,
    "unsafe_symbols": true,
    "unsafe_methods": true,
    "unsafe_proto": true,
    "unsafe_regexp": true,
    "unsafe_undefined": true,
    "passes": 2
  },
  "parse": {
    "ecma": 2020
  },
  "mangle": false
}

The output becomes:

function base64DecodeToExistingUint8Array(uint8Array, offset, b64) {
  for (
    var b1,
      b2,
      i = 0,
      j = offset,
      bLength = b64.length,
      end = offset + ((3 * bLength) >> 2) - ("=" == b64[bLength - 2]) - ("=" == b64[bLength - 1]);
    bLength > i;
    i += 4
  )
    (b1 = base64ReverseLookup[b64.charCodeAt(i + 1)]),
      (b2 = base64ReverseLookup[b64.charCodeAt(i + 2)]),
      (uint8Array[j++] = (base64ReverseLookup[b64.charCodeAt(i)] << 2) | (b1 >> 4)),
      end > j && (uint8Array[j++] = (b1 << 4) | (b2 >> 2)),
      end > j && (uint8Array[j++] = (b2 << 6) | base64ReverseLookup[b64.charCodeAt(i + 3)])
}
import { abort } from "env"
for (var bufferView, base64ReverseLookup = new Uint8Array(123), i = 25; i >= 0; --i)
  (base64ReverseLookup[48 + i] = 52 + i), (base64ReverseLookup[65 + i] = i), (base64ReverseLookup[97 + i] = 26 + i)
;(base64ReverseLookup[43] = 62), (base64ReverseLookup[47] = 63)
var retasmFunc = ((env) => {
  var buffer = new ArrayBuffer(65536),
    HEAP32 = (new Int8Array(buffer), new Int16Array(buffer), new Int32Array(buffer)),
    HEAPU8 = new Uint8Array(buffer),
    HEAPF64 = (new Uint16Array(buffer), new Uint32Array(buffer), new Float32Array(buffer), new Float64Array(buffer)),
    abort = env.abort,
    $lib_builtins_abort = env.abort
  return (
    base64DecodeToExistingUint8Array((bufferView = HEAPU8), 1036, "PA=="),
    base64DecodeToExistingUint8Array(bufferView, 1048, "AQAAACQAAABJAG4AZABlAHgAIABvAHUAdAAgAG8AZgAgAHIAYQBuAGcAZQ=="),
    base64DecodeToExistingUint8Array(bufferView, 1100, "PA=="),
    base64DecodeToExistingUint8Array(bufferView, 1112, "AQAAACQAAAB+AGwAaQBiAC8AdAB5AHAAZQBkAGEAcgByAGEAeQAuAHQAcw=="),
    {
      for_array64($0) {
        var $3,
          $1 = 0,
          $2 = 0
        for ($3 = (HEAP32[((8 + ($0 |= 0)) | 0) >> 2] >>> 3) | 0; (0 | $3) > (0 | $1); )
          ((HEAP32[(($0 + 8) | 0) >> 2] >>> 3) | 0) >>> 0 > $1 >>> 0 ||
            ($lib_builtins_abort(1056, 1120, 1374, 64), abort()),
            ($2 += HEAPF64[((HEAP32[(($0 + 4) | 0) >> 2] + (($1 << 3) | 0)) | 0) >> 3]),
            ($1 = ($1 + 1) | 0)
        return +$2
      },
    }
  )
})({
  abort() {
    throw Error("abort")
  },
  abort: abort,
})
export var for_array64 = retasmFunc.for_array64

Backend for AssemblyScript would be very cool, and would allow to exploit the additional type information being present.

This example you posted compiles to either to this:

function __Z11for_array64PN6client12Float64ArrayE(Larr){
        var Lcall=-0.,Li$p06=-0.,Lsum$p05=-0.;
        Lcall=+Larr.length;
        if(!(Lcall>0))return 0;
        Lsum$p05=0;
        Li$p06=0;
        while(1){
                Lsum$p05+= +Larr[0+~~Li$p06|0];
                Li$p06+=1;
                if(Li$p06<Lcall)continue;
                break;
        }
        return Lsum$p05;
}
var for_array64=__Z11for_array64PN6client12Float64ArrayE;

or, without pretty-code to

function f(g    ){var c=-0.,a=-0.,b=-0.;c=+g.length;if(!(c>0))return 0;b=0;a=0;while(1){b+= +g[0+~~a|0];a+=1;if(a<c)continue;break;}return b;}var for_array64=f;

Currently every number is mapped to a double in the C++. Once that's fixed it would be even more streamlined.

The output looks very promising! 🚀

The example code here was very simple, but I can imagine how the output would look for more realistic examples.