Производительность NodeJS
Предыстория
Я работаю с протоколом Minecraft. И когда возник вопрос о производительности, я достаточно обиделся, потому, что скорость загрузки чанков была очень низкая, я перебирал территорию размером 16777216 блоков, тоесть 256x256x256, и просто устанавливал случайных блок, и это происходило довольно медленно, на создание полностью заполненного случайными блоками чанка, уходило около 3 - 4 секунд. В то время, как скорость загрузки чанков в одиночной игре достигало на глаз около 8 - 10 чанков в секунду, а там используются довольно сложные алгоритмы генерации мира.
Текущая реализация
На данный момент у меня есть пара функций, кодирующая и декодирующая пакет протокола. Выглядит это так
class XPacket extends Packet {
encode(){ /**/ }
decode(){ /**/ }
}
Эти методы выполняют в себе цепочку вызовов методов Packet, таким образом кодируя и декодируя данные из/в поля(ей) XPacket.
Packet внутри себя использует специальный буфер, по сути это обычный Buffer, просто расширенный специальными методами:
writeByte(...value: number[]): this { /**/ }
writeUByte(...value: number[]): this { /**/ }
writeInt(...value: number[]): this { /**/ }
writeUInt(...value: number[]): this { /**/ }
Так же он содержит более сложные методы и аналогичные им для чтения, все они переопределяют буфер с новым значением.
Вот к примеру один из методов:
writeVarint(value: varint): this {
this.buffer = Buffer.concat([data, new Varint().write(value)])
return this
}
Varint.ts
export class Varint extends DataType<number> {
private readonly SEGMENT_BITS = 0x7f
private readonly CONTINUE_BIT = 0x80
protected readonly maxLength: number = 32
read(data: Buffer): [result: number, offset: number] {
let result = 0
let offset = 0
let position = 0
let currentByte: number
while (true) {
currentByte = data.readInt8(offset)
offset++
result |= (currentByte & this.SEGMENT_BITS) << position
if ((currentByte & this.CONTINUE_BIT) === 0) break
position += 7
if (position >= this.maxLength)
throw new Error('Varint is too big')
}
return [result, offset]
}
write(value: number): Buffer {
let buf = Buffer.alloc(0)
while (true) {
if ((value & ~this.SEGMENT_BITS) == 0) {
buf = Buffer.concat([buf, Buffer.from([value])])
break
}
buf = Buffer.concat([
buf,
Buffer.from([(value & this.SEGMENT_BITS) | this.CONTINUE_BIT])
])
value >>>= 7
}
return buf
}
}
И также один из пакетов к примеру:
export class PlayerPositionAndLookClientPacket extends Packet {
static type: number = 0x38
static state: number = PacketState.Play
// Не обращайте внимания на типы double и т.д. это просто алиасы к
// типам number и bigint
x: double = 0
y: double = 0
z: double = 0
yaw: float = 0
pitch: float = 0
flags: byte = 0
teleportId: varint = 0
dismount: boolean = false
constructor() {
super(PlayerPositionAndLookClientPacket.state, PlayerPositionAndLookClientPacket.type)
}
encode(): void {
this
.writeDouble(this.x, this.y, this.z)
.writeFloat(this.yaw, this.pitch)
.writeByte(this.flags)
.writeVarint(this.teleportId)
.writeBoolean(this.dismount)
}
decode(): void {
this.x = this.readDouble()
this.y = this.readDouble()
this.z = this.readDouble()
this.yaw = this.readFloat()
this.pitch = this.readFloat()
this.flags = this.readByte()
this.teleportId = this.readVarint()
this.dismount = this.readBoolean()
}
}
Далее я просто склеиваю методом длину пакета с его данными и отправляю клиенту через встроенный пакет net
get data(): Buffer {
return Buffer.concat([
new Varint().write(this.buffer.length + 1),
new Varint().write(this.type),
this.buffer,
])
}
C++ ?
Может быть перенести этот специальный буфер в аддон c++? Но тут есть проблема, я не знаю как, т.к. почти не знаю c++. Но это меньшая из проблем на данный момент.
GPU.js
Нашёл я библиотечку GPU.js, вроде бы как она умеет делать вычисления на GPU. Есть ли в ней смысл. Также она вряд ли запустится на Linux сервере.
NodeJS не для этих целей
Возможно NodeJS для того, что я пытаюсь сделать и вовсе не подходит?
Вообщем я хочу получить ответ, о том, может ли NodeJS выполнять описанное выше быстрее, если да то как?