Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 88 additions & 11 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,101 @@ on:
branches: [main]

jobs:
publish:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- uses: mymindstorm/setup-emsdk@v14

- name: Setup EMSDK
uses: mymindstorm/setup-emsdk@v14
with:
version: 4.0.1

- name: Use Node.js 22.x
uses: actions/setup-node@v4
with:
node-version: 22.x
- run: npm ci
- run: npm run lint:nofix
- run: npm run build:wasm
- run: npm run build
- run: npm test
- run: npm run luatests
- uses: JS-DevTools/npm-publish@v3

- name: Install dependencies
run: npm ci

- name: Run lint (no fix)
run: npm run lint:nofix

- name: Build WASM
run: npm run build:wasm

- name: Build project
run: npm run build

- name: Run tests
run: npm test

- name: Run Lua tests
run: npm run luatests

- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build-artifact
path: |
package.json
package-lock.json
dist/
bin/
LICENSE

publish_npm:
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-artifact
path: build-artifact

- name: Restore build artifact
run: cp -r build-artifact/* .

- name: Publish to npm
uses: JS-DevTools/npm-publish@v3
with:
token: ${{ secrets.NPM_TOKEN }}

# Emscripten still doesn't support deno
# publish_jsr:
# runs-on: ubuntu-latest
# needs: build
# permissions:
# contents: read
# id-token: write
# steps:
# - name: Download build artifact
# uses: actions/download-artifact@v4
# with:
# name: build-artifact
# path: build-artifact

# - name: Restore build artifact
# run: cp -r build-artifact/* .

# - name: Generate jsr.json from package.json
# run: |
# node -e "const pkg = require('./package.json'); \
# const jsr = { \
# name: '@ceifa/' + pkg.name, \
# version: pkg.version, \
# exports: pkg.main, \
# include: ['dist/**/*', 'bin/**/*']
# }; \
# require('fs').writeFileSync('jsr.json', JSON.stringify(jsr, null, 2));"

# - name: Publish to JSR
# run: npx jsr publish
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ jobs:

strategy:
matrix:
node-version: [18, 20, 22]
node-version: [22]

steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: mymindstorm/setup-emsdk@v12
- uses: mymindstorm/setup-emsdk@v14
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
Expand Down
74 changes: 53 additions & 21 deletions bench/comparisons.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,62 @@
const { readFileSync } = require('fs')
const path = require('path')
import { readFileSync } from 'fs'
import path from 'path'
import { performance } from 'perf_hooks'
import { Lua } from '../dist/index.js'
import fengari from 'fengari'

const fengari = require('fengari')
const wasmoon = require('../dist/index')
const heapsort = readFileSync(path.resolve(import.meta.dirname, 'heapsort.lua'), 'utf-8')

const heapsort = readFileSync(path.resolve(__dirname, 'heapsort.lua'), 'utf-8')
function calculateStats(times) {
const n = times.length
const avg = times.reduce((sum, t) => sum + t, 0) / n
const stdDev = Math.sqrt(times.reduce((sum, t) => sum + (t - avg) ** 2, 0) / n)
return { avg, stdDev }
}

async function benchmark(name, iterations, warmup, fn) {
console.log(`\nBenchmarking ${name}...`)

for (let i = 0; i < warmup; i++) {
await fn()
}

const startFengari = () => {
const state = fengari.lauxlib.luaL_newstate()
fengari.lualib.luaL_openlibs(state)
const times = []
for (let i = 0; i < iterations; i++) {
const start = performance.now()
await fn()
const end = performance.now()
times.push(end - start)
}

console.time('Fengari')
fengari.lauxlib.luaL_loadstring(state, fengari.to_luastring(heapsort))
fengari.lua.lua_callk(state, 0, 1, 0, null)
fengari.lua.lua_callk(state, 0, 0, 0, null)
console.timeEnd('Fengari')
const { avg, stdDev } = calculateStats(times)
console.log(`${name}: ${iterations} iterations | avg: ${avg.toFixed(3)} ms | std dev: ${stdDev.toFixed(3)} ms`)
}

const startWasmoon = async () => {
const state = await new wasmoon.LuaFactory().createEngine()
async function benchmarkFengari(iterations, warmup) {
function runFengariIteration() {
const state = fengari.lauxlib.luaL_newstate()
fengari.lualib.luaL_openlibs(state)
fengari.lauxlib.luaL_loadstring(state, fengari.to_luastring(heapsort))
fengari.lua.lua_callk(state, 0, 1, 0, null)
fengari.lua.lua_callk(state, 0, 0, 0, null)
}
await benchmark('Fengari', iterations, warmup, runFengariIteration)
}

async function benchmarkWasmoon(iterations, warmup) {
const lua = await Lua.load()

console.time('Wasmoon')
state.global.lua.luaL_loadstring(state.global.address, heapsort)
state.global.lua.lua_callk(state.global.address, 0, 1, 0, null)
state.global.lua.lua_callk(state.global.address, 0, 0, 0, null)
console.timeEnd('Wasmoon')
async function runWasmoonIteration() {
const state = lua.createState()
state.global.lua.luaL_loadstring(state.global.address, heapsort)
state.global.lua.lua_callk(state.global.address, 0, 1, 0, null)
state.global.lua.lua_callk(state.global.address, 0, 0, 0, null)
}
await benchmark('Wasmoon', iterations, warmup, runWasmoonIteration)
}

Promise.resolve().then(startFengari).then(startWasmoon).catch(console.error)
const iterations = 100
const warmup = 10

await benchmarkFengari(iterations, warmup)
await benchmarkWasmoon(iterations, warmup)
2 changes: 2 additions & 0 deletions bench/heapsort.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,6 @@ return function()
assert(a[i] <= a[i + 1])
end
end

return Num
end
62 changes: 30 additions & 32 deletions bench/steps.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
const { readFileSync } = require('fs')
const path = require('path')
import { readFileSync } from 'fs'
import path from 'path'
import { Lua } from '../dist/index.js'
import assert from 'node:assert'

const wasmoon = require('../dist/index')
const heapsort = readFileSync(path.resolve(import.meta.dirname, 'heapsort.lua'), 'utf-8')

const heapsort = readFileSync(path.resolve(__dirname, 'heapsort.lua'), 'utf-8')

const createFactory = () => {
const createFactory = async () => {
console.time('Create factory')
_ = new wasmoon.LuaFactory()
await Lua.load()
console.timeEnd('Create factory')
}

const loadWasm = async () => {
console.time('Load wasm')
await new wasmoon.LuaFactory().getLuaModule()
console.timeEnd('Load wasm')
}

const createEngine = async () => {
const factory = new wasmoon.LuaFactory()
const createState = async () => {
const lua = await Lua.load()

console.time('Create engine')
await factory.createEngine()
console.timeEnd('Create engine')
console.time('Create state')
lua.createState()
console.timeEnd('Create state')
}

const createEngineWithoutSuperpowers = async () => {
const factory = new wasmoon.LuaFactory()
const createStateWithoutSuperpowers = async () => {
const lua = await Lua.load()

console.time('Create engine without superpowers')
await factory.createEngine({
console.time('Create state without superpowers')
lua.createState({
injectObjects: false,
enableProxy: false,
openStandardLibs: false,
})
console.timeEnd('Create engine without superpowers')
console.timeEnd('Create state without superpowers')
}

const runHeapsort = async () => {
const state = await new wasmoon.LuaFactory().createEngine()
const lua = await Lua.load()
const state = lua.createState()

console.time('Run plain heapsort')
state.global.lua.luaL_loadstring(state.global.address, heapsort)
Expand All @@ -48,16 +43,18 @@ const runHeapsort = async () => {
}

const runInteropedHeapsort = async () => {
const state = await new wasmoon.LuaFactory().createEngine()
const lua = await Lua.load()
const state = lua.createState()

console.time('Run interoped heapsort')
const runHeapsort = await state.doString(heapsort)
runHeapsort()
assert(runHeapsort() === 10)
console.timeEnd('Run interoped heapsort')
}

const insertComplexObjects = async () => {
const state = await new wasmoon.LuaFactory().createEngine()
const lua = await Lua.load()
const state = lua.createState()
const obj1 = {
hello: 'world',
}
Expand All @@ -77,7 +74,8 @@ const insertComplexObjects = async () => {
}

const insertComplexObjectsWithoutProxy = async () => {
const state = await new wasmoon.LuaFactory().createEngine({
const lua = await Lua.load()
const state = lua.createState({
enableProxy: false,
})
const obj1 = {
Expand All @@ -99,7 +97,8 @@ const insertComplexObjectsWithoutProxy = async () => {
}

const getComplexObjects = async () => {
const state = await new wasmoon.LuaFactory().createEngine()
const lua = await Lua.load()
const state = lua.createState()
await state.doString(`
local obj1 = {
hello = 'world',
Expand All @@ -124,9 +123,8 @@ const getComplexObjects = async () => {

Promise.resolve()
.then(createFactory)
.then(loadWasm)
.then(createEngine)
.then(createEngineWithoutSuperpowers)
.then(createState)
.then(createStateWithoutSuperpowers)
.then(runHeapsort)
.then(runInteropedHeapsort)
.then(insertComplexObjects)
Expand Down
Loading