Accessing interleaved struct-like data from a TypedArray, without multiple reads?

  Kiến thức lập trình

This is a performance-related question. As such, regular JS rules and conventions are irrelevant here. Thank you for your understanding.

In C, we read or write large structs’ data fast by pulling a struct instance from an array of struct into a local variable, modifying it, and writing that (as a value) directly back into the array again by simple assignment. The struct can be up to some platform-specified limit, usually up to several thousand bytes. This makes for both easy and super-fast handling of data. Disassembly may show some fast memcpy type stuff happening under the hood to achieve this.

In Javascript, the only performant alternative is to use (Typed)Arrays. We create and repeat our “fake struct” in an array thus:

const SIZEOF_X = 1; //1 byte
const SIZEOF_Y = 1; //1 byte
const SIZEOF_Z = 1; //1 byte
const SIZEOF_PADDING0 = 1; //1 byte padding, aligns following members to 4B heap boundaries.
const SIZEOF_PAYLOAD1 = 2; //2 bytes
const SIZEOF_PAYLOAD2 = 2; //2 bytes

const SIZEOF_BYTES_ENTITY = 
                      SIZEOF_X +
                      SIZEOF_Y + 
                      SIZEOF_Z + 
                      SIZEOF_PADDING0 + 
                      SIZEOF_PAYLOAD1 +
                      SIZEOF_PAYLOAD2; //8 bytes total.

//calculating offsets into the "struct" can be done using a loop, but for clarity:
const OFFSET_BYTES_X = 0;
const OFFSET_BYTES_Y = SIZEOF_BYTES_X;
const OFFSET_BYTES_Z = SIZEOF_BYTES_X + SIZEOF_BYTES_Y;
//...etc.

const buffer = new ArrayBuffer(ENTITIES_COUNT * SIZEOF_BYTES_ENTITY);
const structsAs64 = new Uint64Array(buffer); //Uint64 because one struct = 8B.
const structsAs16 = new Uint16Array(buffer);
const structsAs8  = new  Uint8Array(buffer);

const structValue64 = structsAs64[i]; //OK, just disassemble via bit shifts.
//OR
const z = structsAs8[i * SIZEOF_BYTES_ENTITY + OFFSET_BYTES_Z];

Great, it works. But what if our fake structs are greater than 64-bits?

//now the new struct type we want to pull is 48B in length, so 12x larger.
const SIZEOF_BYTES_ENTITY = 96;
const SIZEOF_WORDS_ENTITY = SIZEOF_BYTES_ENTITY / 8; //multiple 64-bit / 8-byte words.

const bigStructsAs64 = new Uint64Array(buffer);

let index = 43; //to the entity we want

let bigStruct = bigStructsAs64.subarray(index * SIZEOF_ENTITY_WORDS);

.subarray() itself is a no-no as it creates a new TypedArray view which must then be GC’ed. But at least we can be sure we are not doing this in JS code:

//copy out, one 64-bit word at a time:
for (int w = 0; w < SIZEOF_ENTITY_WORDS; w++)
{
    bigStruct[w] = bigStructsAs64[index * SIZEOF_ENTITY_WORDS + w];
}

Is there any method faster than this for pulling back large amounts of data in struct (or any other) format?

LEAVE A COMMENT