Performance enhancements and benchmarks
This commit is contained in:
parent
0f7ccb2a0a
commit
4151d0335c
@ -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()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user