Performance enhancements and benchmarks

This commit is contained in:
themodernhakr 2025-06-02 09:55:00 -05:00
parent 0f7ccb2a0a
commit 4151d0335c

View File

@ -1,6 +1,9 @@
import { type Result, tryCatch } from '@maxmorozoff/try-catch-tuple'
import { nanoseconds } from 'bun'
const encoder = new TextEncoder()
const emptyBytes = encoder.encode('')
type Writer = (x: Uint8Array | Array<Uint8Array>) => Promise<number>
const writer: Writer = x => Bun.write(Bun.stdout, x)
export const ANSI = {
// Text Styles
@ -108,137 +111,110 @@ export const ANSI_DYNAMIC = {
type AnsiBufferKey = keyof typeof ANSI_BUFFERS
export async function writeAnsi(
codes: (AnsiBufferKey | Uint8Array)[],
text?: string,
autoReset: boolean = true,
): Promise<Result<number, Error>> {
const buffers: Uint8Array[] = []
type WriteAnsi = (
c: Array<(AnsiBufferKey | Uint8Array)>,
t?: string,
a?: boolean,
) => Promise<number>
const writeAnsi: WriteAnsi = async (codes, text?, autoReset = true) => {
const arr = [
...codes,
!text ? emptyBytes : encoder.encode(text),
autoReset ? ANSI_BUFFERS['RESET'] : emptyBytes,
].map(x => x instanceof Uint8Array ? x : ANSI_BUFFERS[x])
// Add requested ANSI codes
for (const code of codes) {
if (code instanceof Uint8Array) {
buffers.push(code)
} else {
buffers.push(ANSI_BUFFERS[code])
}
return writer(Buffer.concat(arr))
}
// Add optional text
if (text) {
buffers.push(encoder.encode(text))
}
const writeAnsi1: WriteAnsi = async (codes, text?, autoReset = true) => {
const buffers = [
...codes,
!text ? emptyBytes : encoder.encode(text),
autoReset ? ANSI_BUFFERS['RESET'] : emptyBytes,
].map(x => x instanceof Uint8Array ? x : ANSI_BUFFERS[x])
// Auto-reset if requested
if (autoReset && !codes.includes('RESET')) {
buffers.push(ANSI_BUFFERS.RESET)
}
// Combine all buffers into single write
const combined = new Uint8Array(
buffers.reduce((acc, buf) => acc + buf.length, 0),
)
// Calculate total length and create combined buffer
const totalLength = buffers.reduce((sum, buf) => sum + buf.length, 0)
const combined = new Uint8Array(totalLength)
// Copy all buffers into the combined array
let offset = 0
for (const buf of buffers) {
combined.set(buf, offset)
offset += buf.length
for (const buffer of buffers) {
combined.set(buffer, offset)
offset += buffer.length
}
// return tryCatch(() => Bun.write(Bun.stdout, combined))
return tryCatch(() => process.stdout.write(combined))
}
export async function writeAnsi2(
codes: (AnsiBufferKey | Uint8Array)[],
text?: string,
autoReset: boolean = true,
): Promise<Result<number, Error>> {
const buffers: Uint8Array[] = []
// Add requested ANSI codes
for (const code of codes) {
if (code instanceof Uint8Array) {
buffers.push(code)
} else {
buffers.push(ANSI_BUFFERS[code])
}
}
// Add optional text
if (text) {
buffers.push(encoder.encode(text))
}
// Auto-reset if requested
if (autoReset && !codes.includes('RESET')) {
buffers.push(ANSI_BUFFERS.RESET)
}
// Combine all buffers into single write
const combined = new Uint8Array(
buffers.reduce((acc, buf) => acc + buf.length, 0),
)
let offset = 0
for (const buf of buffers) {
combined.set(buf, offset)
offset += buf.length
}
return Bun.write(Bun.stdout, combined)
// return tryCatch(() => process.stdout.write(combined))
return writer(combined)
}
// For CommonJS compatibility
export default {ANSI, ANSI_BUFFERS, writeAnsi}
const testNum = 5
// const testCase = [['BG_WHITE', 'BLACK'], 'This is some text']
const testCase =
`${ANSI_BUFFERS.BG_WHITE}${ANSI_BUFFERS.BLACK}This is some text${ANSI_BUFFERS.RESET}`
const testCase2 = `${ANSI.BG_WHITE}${ANSI.BLACK}This is some text`
const bench = async (x, y, n) => {
const bench = async (x, n) => {
const a = Bun.nanoseconds()
let i = 0
while (i < n) {
await x(y)
await x()
i++
}
const b = Bun.nanoseconds()
return writeAnsi2(['YELLOW'], `${Math.round((b - a) / 1000000)}ms`)
return (b - a) / 100000
}
const out = async (a, b, c) => {
const one = await a
const two = await b
const three = await c
const log = x => async y => console.log(x, await y)
const diff = x => y => {
if (y < x) return Math.round(((y - x) / y) * 100)
return Math.round(((y - x) / x) * 100)
}
writeAnsi(['RESET', 'CLEAR_SCREEN', ANSI_DYNAMIC.CURSOR_TO(0, 0)])
console.log('')
console.log('---------------')
console.log('Output in milliseconds')
console.log('---------------')
log('ANSI: ')(!one ? '' : one)
log('ANSI_BUFFERS: ')(!two ? '' : two)
log('THREE: ')(!three ? '' : three)
log(`${diff(one)(two)}%`)('faster')
}
out(
bench(x => Bun.write(Bun.stdout, x), testCase, testNum),
bench(x => Bun.write(Bun.stdout, x), testCase2, testNum),
bench(
x => writeAnsi2(...x),
[['BG_WHITE', 'BLACK'], 'This is some text'],
testNum,
),
const one = async () => {
await writeAnsi(
['BG_CYAN', 'BLACK', 'ITALIC'],
'This is the first option I want',
)
await writeAnsi(
['BG_BLUE', 'BLACK', 'BOLD'],
'This is the second option I want',
)
await writeAnsi(
['BG_YELLOW', 'BLACK', 'BLINK'],
'Now, this is the third and final option',
)
}
const two = async () => {
await writeAnsi1(
['BG_CYAN', 'BLACK', 'ITALIC'],
'This is the first option I want',
)
await writeAnsi1(
['BG_BLUE', 'BLACK', 'BOLD'],
'This is the second option I want',
)
await writeAnsi1(
['BG_YELLOW', 'BLACK', 'BLINK'],
'Now, this is the third and final option',
)
}
const test = async () => {
const testN = 100000
await bench(one, 100000)
await bench(two, 100000)
const testTwo = await bench(two, testN)
const testOne = await bench(one, testN)
await writeAnsi(
['RESET', ANSI_DYNAMIC.CURSOR_TO(0, 0)],
`${ANSI.CLEAR_SCREEN}\n\n`,
)
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------\n')
await writeAnsi(['BG_YELLOW', 'BOLD', 'BLACK'], '***************\n')
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------\n')
await writeAnsi(['BG_YELLOW', 'BLACK'], 'ONE:')
await writeAnsi(['BG_YELLOW', 'GREEN'], testOne.toString() + '\n')
await writeAnsi(['BG_YELLOW', 'BLACK'], 'TWO:')
await writeAnsi(['BG_YELLOW', 'GREEN'], testTwo.toString() + '\n')
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------\n')
await writeAnsi(['BG_YELLOW', 'BOLD', 'BLACK'], '***************\n')
await writeAnsi(['BG_YELLOW', 'BLACK'], '---------------')
await writeAnsi(['RESET'], '\n\n')
}
test()