Skip to content

Commit

Permalink
faster itoa
Browse files Browse the repository at this point in the history
  • Loading branch information
JairusSW committed Jun 4, 2024
1 parent 0d3f604 commit 0e402ff
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 28 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,20 @@ My library beats JSON (written in C++) on all counts *and*, I see many places wh

Serialization Benchmarks:

| Value | JavaScript (ops/s) | AssemblyScript (ops/s) | % Diff |
|----------------------------|--------------------|------------------------|--------|
| "hello world" | 28,629,598 | 64,210,666 | + 124% |
| 12345 | 31,562,431 | 48,035,001 | + 52% |
| 1.2345 | 15,977,278 | 20,322,939 | + 27% |
| [[],[[]],[[],[[]]]] | 8,998,624 | 34,453,102 | + 283% |
| Value | JavaScript (ops/s) | JSON-as (ops/s) | JSON-AS with Pages |
|----------------------------|--------------------|-----------------|--------------------|
| "hello world" | 28,629,598 | 64,210,666 | + 124% |
| 12345 | 31,562,431 | 48,035,001 | 316,518,756 ops/s |
| 1.2345 | 15,977,278 | 20,322,939 | 30,307,616 ops/s |
| [[],[[]],[[],[[]]]] | 8,998,624 | 34,453,102 | + 283% |



Deserialization Benchmarks: (WIP)

| Value | JavaScript (ops/s) | AssemblyScript (ops/s) | % Diff |
|----------------------------|--------------------|------------------------|--------|
| "12345" | 34,647,886 | 254,640,930 | + 635% |
| Value | JavaScript (ops/s) | JSON-AS (ops/s) | % Diff |
|----------------------------|--------------------|-----------------|--------|
| "12345" | 34,647,886 | 254,640,930 | + 635% |


And my PC specs:
Expand Down
43 changes: 25 additions & 18 deletions assembly/bench/bench.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
import { bench, blackbox } from "as-bench/assembly/bench";
import { Sink } from "../src/sink";
import { JSON } from "..";
import { serializeString } from "../serialize/string";

const set = JSON.Value.from([
JSON.Value.from([]),
JSON.Value.from([
JSON.Value.from([])
JSON.Value.from([])
]),
JSON.Value.from([
JSON.Value.from([]),
JSON.Value.from([
JSON.Value.from([])
]),
JSON.Value.from([]),
JSON.Value.from([
JSON.Value.from([])
]),
]),
]);

const page = Sink.withCapacity(65536);
/*
bench("Serialize Integer", () => {
blackbox<Sink>(JSON.serialize(12345, page));
});
bench("Serialize Set Theoretical Representation", () => {
blackbox<Sink>(JSON.serialize(set));
blackbox<Sink>(JSON.serialize(set,page));
page.reset();
});
/*bench("Serialize Float", () => {
blackbox<Sink>(JSON.serialize<f64>(1.2345));
bench("Serialize Float", () => {
blackbox<Sink>(JSON.serialize<f64>(1.2345, page));
page.reset();
});
bench("Serialize Float32", () => {
blackbox<Sink>(JSON.serialize<f32>(1.2345));
});
blackbox<Sink>(JSON.serialize<f32>(1.2345, page));
page.reset();
});*/
bench("Serialize String", () => {
blackbox<Sink>(JSON.serialize("hello world"));
blackbox<Sink>(serializeString("hello world", page));
});
bench("Serialize Integer", () => {
blackbox<Sink>(JSON.serialize(12345));
});
bench("Parse Number: " + __atoi_fast<u32>("12345").toString(), () => {
blackbox<u32>(__atoi_fast<u32>("12345"));
/*
bench("Parse Number: " + snip_fast<u32>("12345").toString(), () => {
blackbox<u32>(snip_fast<u32>("12345"));
});*/
43 changes: 43 additions & 0 deletions assembly/serialize/integer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,47 @@ import { Sink } from "../src/sink";
@inline export function serializeInteger<T extends number>(data: T, out: Sink | null = null): Sink {
if (!out) out = Sink.withCapacity(0);
return out.writeNumber(data);
}

@inline export function utoa16(data: u32): string {
const str = changetype<string>(__new(10, idof<string>()));
const buf = changetype<usize>(str);
const fix4_28 = (1 << 28) / 10000;
let tmp = data * (fix4_28 + 1) - (data / 4);

store<u16>(buf, 48 + (tmp >> 28));
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 2);
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 4);
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 6);
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 8);
return str
}

@inline function utoa32(buf: usize, data: u32): void {
const low = data % 10000;
const high = data / 10000;
const fix4_28 = (1 << 28) / 10000;
let tmp = data * (fix4_28 + 1) - (data / 4);

store<u16>(buf, 48 + (tmp >> 28));
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 2);
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 4);
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 6);
tmp = (tmp & 0x0fffffff) * 10;

store<u16>(buf, 48 + (tmp >> 28), 8);
}
36 changes: 35 additions & 1 deletion assembly/src/sink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ export class Sink {
@inline get capacity(): i32 {
return this.buffer.byteLength >>> 1;
}

@inline reset(): void {
this.offset = 0;
}
@inline write(src: string, start: i32 = 0, end: i32 = i32.MAX_VALUE): Sink | null {
let len = src.length as u32;

Expand Down Expand Up @@ -199,6 +201,38 @@ export class Sink {
this.offset = offset;
return this;
}
@inline writeNumberUnsafe<T extends number>(value: T): Sink {
let offset = this.offset;
if (isInteger<T>()) {
offset += itoa_buffered(
changetype<usize>(this.buffer) + offset,
value
) << 1;
} else {
offset += dtoa_buffered(
changetype<usize>(this.buffer) + offset,
value
) << 1;
}
this.offset = offset;
return this;
}
@inline writeIntegerUnsafe<T extends number>(value: T): Sink {
let offset = this.offset;
if (isInteger<T>()) {
offset += itoa_buffered(
changetype<usize>(this.buffer) + offset,
value
) << 1;
} else {
offset += dtoa_buffered(
changetype<usize>(this.buffer) + offset,
value
) << 1;
}
this.offset = offset;
return this;
}

@inline reserve(capacity: i32, clear: bool = false): void {
if (clear) this.offset = 0;
Expand Down
3 changes: 3 additions & 0 deletions assembly/test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { JSON } from ".";
import { utoa16 } from "./serialize/integer";
import { Sink } from "./src/sink";
import { describe, expect } from "as-test/assembly";

Expand All @@ -8,6 +9,8 @@ describe("Serialize String", () => {

describe("Serialize Integer", () => {
expect(JSON.serialize(65536).toString()).toBe("65536");
expect(utoa16(65536)).toBe("65536");
console.log(utoa16(6553))
});
describe("Serialize Float", () => {
expect(JSON.serialize(3.1415).toString()).toBe("3.1415");
Expand Down

0 comments on commit 0e402ff

Please sign in to comment.