You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of the bottlenecks for Piscina at the moment is that we need to transfer data to the worker. Which is not efficient with the current implementation in NodeJS. This means that the tasks sent to piscina need to be large enough that the overhead of sending the task is not too bug.
One way around this would be to avoid the postMessage API's and use a ring buffer to write the tasks + data and let the user provide a Buffer serialize/deserialize function.
Here is a ring buffer implementation we have been using internally for inspiration:
importassertfrom'node:assert'// Make sure write and read are in different// cache lines.constWRITE_INDEX=0constREAD_INDEX=16exportfunctionalloc(size){return{sharedState: newSharedArrayBuffer(128),sharedBuffer: newSharedArrayBuffer(size),}}exportfunctionreader({ sharedState, sharedBuffer, hwmBytes =128*1024, hwmItems =1024}){conststate=newInt32Array(sharedState)constsize=sharedBuffer.byteLengthconstbuffer=Buffer.from(sharedBuffer)constview=newDataView(sharedBuffer)constdata={ buffer, view,offset: 0,length: 0}letreadPos=Atomics.load(state,READ_INDEX)|0letnotifying=falsefunction_notify(){notifying=falseAtomics.store(state,READ_INDEX,readPos)}functionread(next,arg1,arg2,arg3){letitems=0letbytes=0constwritePos=Atomics.load(state,WRITE_INDEX)|0while(items<hwmItems&&bytes<hwmBytes&&readPos!==writePos){constdataPos=readPos+4constdataLen=view.getInt32(dataPos-4,true)|0if(dataLen===-1){readPos=0}else{assert(dataLen>=0)assert(dataPos+dataLen<=size)readPos+=4+dataLenitems+=1bytes+=dataLendata.offset=dataPosdata.length=dataLennext(data,arg1,arg2,arg3)}}// Defer notify so that the returned buffers are valid for at least// one microtick.if(items>0&&!notifying){notifying=truesetImmediate(_notify)}returnitems}return{ read }}exportfunctionwriter({ sharedState, sharedBuffer },{yield: _yield, logger }={}){conststate=newInt32Array(sharedState)constsize=sharedBuffer.byteLengthconstbuffer=Buffer.from(sharedBuffer)constview=newDataView(sharedBuffer)constdata={ buffer, view,offset: 0,length: 0}letreadPos=Atomics.load(state,READ_INDEX)|0letwritePos=Atomics.load(state,WRITE_INDEX)|0letnotifying=falseletyielding=falsefunction_notify(){notifying=falseAtomics.store(state,WRITE_INDEX,writePos)}function_acquire(len,update){// len + {current packet header} + {next packet header}constrequired=len+4+4assert(required>=0)assert(required<=size)if(writePos>=readPos){// 0----RxxxxxxW---Sif(size-writePos>=required){returntrue}if(readPos===0){returnfalse}view.setInt32(writePos,-1,true)writePos=0assert(writePos+4<=size)// must have room for next header alsoassert(writePos!==readPos)Atomics.store(state,WRITE_INDEX,writePos)}// 0xxxxW------RxxxSreturnreadPos-writePos>=required}function_write(len,fn,arg1,arg2,arg3){constdataPos=writePos+4data.offset=dataPosdata.length=lenconstdataLen=fn(data,arg1,arg2,arg3)-dataPosassert(dataLen<=len+4)assert(dataLen>=0)assert(dataPos+dataLen<=size)view.setInt32(dataPos-4,dataLen,true)writePos+=4+dataLenassert(writePos+4<=size)// must have room for next header alsoassert(writePos!==readPos)if(!notifying){notifying=truequeueMicrotask(_notify)}returntrue}// TODO (fix): _sleep/_wait is a bit hacky and should at least have some// observability in tracing or logging.functionwrite(len,fn,arg1,arg2,arg3){// len + {current packet header} + {next packet header} + {alignment}constrequired=len+4+4+8+128// TODO (fix): Remove extra + 128assert(required>=0)assert(required<=size)assert(!yielding,'yielding')for(letn=0;!_acquire(required);n++){assert(n<1000,'deadlock')if(n>0){if(n===1){logger?.warn('yielding',{ readPos, writePos })}if(_yield){yielding=truetry{_yield?.()}finally{yielding=false}}Atomics.wait(state,READ_INDEX,readPos,n)}else{Atomics.store(state,WRITE_INDEX,writePos)}readPos=Atomics.load(state,READ_INDEX)|0}_write(len,fn,arg1,arg2,arg3)assert(writePos!==readPos)}return{ write }}
The text was updated successfully, but these errors were encountered:
This issue has been marked as stale because it has been opened 30 days without activity. Remove stale label or comment or this will be closed in 5 days.
I'm thinking here that, the a good portion of scenarios will be simple JSON (de)serialization or primitive values and having to guess them might bring some sort of overhead.
Part of the bottlenecks for Piscina at the moment is that we need to transfer data to the worker. Which is not efficient with the current implementation in NodeJS. This means that the tasks sent to piscina need to be large enough that the overhead of sending the task is not too bug.
One way around this would be to avoid the
postMessage
API's and use a ring buffer to write the tasks + data and let the user provide a Buffer serialize/deserialize function.Here is a ring buffer implementation we have been using internally for inspiration:
The text was updated successfully, but these errors were encountered: