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
I noticed that the asXXX functions transpile the key to a single string. That means you have to do runtime logic to split the keys. Wouldn't it be better to pre-split them at compile-time to save some cycles?
Here are the benchmarks in case you'd like to run them yourself.
submain()runAllTests()endsubsubrunAllTests()print"Tests starting"m.opCount=100000m.testResults= []
stringVsArrayKeyLookups()printResults(m.testResults)startTime=CreateObject("roDateTime")'the print buffer isn't flushed when the app dies, so spin till it flushesprint" "whileCreateObject("roDateTime").AsSeconds()-startTime.AsSeconds()<1endwhileendsubsubstringVsArrayKeyLookups()json={user:{favorites: [
{isActive:true}
]
}}runTest("string split",sub(opCount,json,path)getPath=function(contentasobject,pathasstring,default=invalidasdynamic,disableIndexing=falseasboolean)asdynamicpart=invalidifpath<>invalidparts=path.split(".")numParts=parts.count()i=0part=contentwhilei<numPartsandpart<>invalidifnotdisableIndexingand(parts[i]="0"or(parts[i].toInt()<>0andparts[i].toInt().toStr()=parts[i]))iftype(part)<>"<uninitialized>"andpart<>invalidandGetInterface(part,"ifArray")<>invalidpart=part[parts[i].toInt()]
elseiftype(part)<>"<uninitialized>"andpart<>invalidandGetInterface(part,"ifAssociativeArray")<>invalidpart=part[parts[i]]
elseiftype(part)="roSGNode"part=part.getChild(parts[i].toInt())elsepart=invalidendifelseiftype(part)<>"<uninitialized>"andpart<>invalidandGetInterface(part,"ifAssociativeArray")<>invalidpart=part[parts[i]]
elsepart=invalidendifendifi++endwhileendififpart<>invalidreturnpartelsereturndefaultendifendfunctionfori=0toopCountgetPath(json,path)endforendsub,json,"user.favorites.0.isActive")runTest("already split",sub(opCount,json,keys)getPath=function(contentasobject,parts,default=invalidasdynamic,disableIndexing=falseasboolean)asdynamicpart=invalidifparts<>invalidnumParts=parts.count()i=0part=contentwhilei<numPartsandpart<>invalidifnotdisableIndexingand(parts[i]="0"or(parts[i].toInt()<>0andparts[i].toInt().toStr()=parts[i]))iftype(part)<>"<uninitialized>"andpart<>invalidandGetInterface(part,"ifArray")<>invalidpart=part[parts[i].toInt()]
elseiftype(part)<>"<uninitialized>"andpart<>invalidandGetInterface(part,"ifAssociativeArray")<>invalidpart=part[parts[i]]
elseiftype(part)="roSGNode"part=part.getChild(parts[i].toInt())elsepart=invalidendifelseiftype(part)<>"<uninitialized>"andpart<>invalidandGetInterface(part,"ifAssociativeArray")<>invalidpart=part[parts[i]]
elsepart=invalidendifendifi++endwhileendififpart<>invalidreturnpartelsereturndefaultendifendfunctionfori=0toopCountgetPath(json,keys)endforendsub,json, ["user","favorites","0","isActive"])endsubfunctiongetOpsPerSec(startDate,endDate,ops)startMs=getMilliseconds(startDate)endMs=getMilliseconds(endDate)seconds=(endMs-startMs)/1000opsPerSec=ops/secondsreturnopsPerSecendfunctionfunctiongetMilliseconds(date)result=0result+=date.GetMinutes()*60*1000result+=date.GetSeconds()*1000result+=date.GetMilliseconds()returnresultendfunctionfunctionnumberToString(num)result=""i=0whilenum>1loopNum=(nummod10).ToStr().Trim()result=loopNum+resultnum=num/10i++ifimod3=0andnum>1thenresult=","+resultendifendwhileresult=result+"."+((num*10)mod1).ToStr().Trim()+((num*100)mod1).ToStr().Trim()returnresultendfunction'' Run a single test.' @param name - the name of the test' @param testFn - reference to the test to run' @param args - an array of parameters to pass as arguments to the test function'subrunTest(nameasstring,testFuncasfunction,arg1=invalid,arg2=invalid,arg3=invalid,arg4=invalid)opCount=m.opCountifarg4<>invalidstartTime=CreateObject("roDateTime")testFunc(opCount,arg1,arg2,arg3,arg4)endTime=CreateObject("roDateTime")elseifarg3<>invalidstartTime=CreateObject("roDateTime")testFunc(opCount,arg1,arg2,arg3)endTime=CreateObject("roDateTime")elseifarg2<>invalidstartTime=CreateObject("roDateTime")testFunc(opCount,arg1,arg2)endTime=CreateObject("roDateTime")elseifarg1<>invalidstartTime=CreateObject("roDateTime")testFunc(opCount,arg1)endTime=CreateObject("roDateTime")elsestartTime=CreateObject("roDateTime")testFunc(opCount)endTime=CreateObject("roDateTime")endifresult={name:nameopsPerSec:getOpsPerSec(startTime,endTime,opCount)}printname; " (DONE)"m.testResults.push(result)endsubfunctionprintResults(results)print""print"RESULTS:"printpadRight("",50,"_")print""highestOpsPerSec=results[0].opsPerSeclowestOpsPerSec=results[0].opsPerSecopsPerSecMaxLen=0nameLengthMaxLen=0foreachresultinresults'calculate slowestifresult.opsPerSec<lowestOpsPerSeclowestOpsPerSec=result.opsPerSecendif'calculate highest ops/secifresult.opsPerSec>highestOpsPerSechighestOpsPerSec=result.opsPerSecendif'calculate logest ops/sec stringopsPerSecTextLength=numberToString(result.opsPerSec).Len()ifopsPerSecTextLength>opsPerSecMaxLenopsPerSecMaxLen=opsPerSecTextLengthendif'calculate longest nameifresult.name.Len()>nameLengthMaxLennameLengthMaxLen=result.name.Len()endifendforforeachresultinresultspostfix=""ifresult.opsPerSec=highestOpsPerSecthenpostfix=" (fastest)"endififresult.opsPerSec=lowestOpsPerSecthenpostfix=" (slowest)"endifprintResult(result,nameLengthMaxLen+5,opsPerSecMaxLen,postfix)endforendfunction'' Print a single test result'subprintResult(result,namePadding=1,opsPadding=0,postfix="")printpadRight(result.name+": ",namePadding,"-"); " "; padLeft(numberToString(result.opsPerSec),opsPadding," "); " ops/sec"; postfixendsubfunctionpadRight(valueasstring,padLength=2asinteger,paddingCharacter="0"asdynamic)asstringwhilevalue.len()<padLengthvalue+=paddingCharacterendwhilereturnvalueendfunctionfunctionpadLeft(valueasstring,padLength=2asinteger,paddingCharacter="0"asdynamic)asstringwhilevalue.len()<padLengthvalue=paddingCharacter+valueendwhilereturnvalueendfunctionsubnoop()endsub
The text was updated successfully, but these errors were encountered:
thanks - this is a good suggestion, which I'll definitely accept a pr on, if somebody needs it.
For me though, on my projects, there's no need.
The reason I've never sweated these getXXX methods, is coz I discourage parsing anything in my projects, as my mentor taught me when I first started doing roku.
So - while most people do 1000's or 10'000's of ops when they receive things from the wire, managing/parsing json fields, I do zero: I just bundle up the entire json in a node, and off it goes.
The renderers then end up doing the work - so the only part of any of my apps that can be using these asXXX methods, in any hot loop is a cell - and there's never more than say.. 200 getting rendered at a time. Given I use maestro list for everything these days, that's like.. < 50 max, at any given time.. (it supports rendering itself in batches, and is highly efficient to keep render thread nice and smooth).
In NBA, the most complex cell that I have, has 15 of these getXXX methods in it - we render 20 at at time, max.. so that means I'm going to save 1.4 ms of processing, in my worst case scenario
so, yeah - totally happy if someone who wants to do loads of pointless processing and uses maestro, wishes to improve their slow code.. they can give me a pr; but I need to go saving a lot more than 1.4ms before I start optimizing :)
I noticed that the
asXXX
functions transpile the key to a single string. That means you have to do runtime logic to split the keys. Wouldn't it be better to pre-split them at compile-time to save some cycles?Proposal
Source:
Transpiled (current):
Transpiled (proposed):
Justification
Here are some benchmark results, which show 12.9% speed improvement:
Here are the benchmarks in case you'd like to run them yourself.
The text was updated successfully, but these errors were encountered: