Hey,
This time I want to be sure before submitting anything.
The code is not entirely finished.
But what do you think about this:
You can read the documentation in the hidden part.
JASS:
libraryBenchmarkinitializerInit// |===========================================|// |============= S E T T I N G S =============|// |===========================================|// === DISABLE_ERROR ===// This messages will be displayed, if Emmeasure was disabled, because it hasn't been used// properly.// === ABORT_ERROR ===// This message will be displayed, if a Benchmark was aborted, because it hasn't been used// properly.// === COLOR_MASSIVELY ===// Changes the Color Algorithm, so if this is true, the Colors are more glaring and you can// see the speed differnces clearly. [Read PF 1]// === UNIQUE_ERRORS ===// If this is true, the system will not spam error messages, it will display each unique message// once. Example: It will not display 'I am nobus m' twice, even if the error appears 100 times or more.// [ Read PF 2 ]// === QUALITY ===// This shouldn't be changed in order to make tests more uniform. I think I'll add a protection later, so// that people can't cheat.// What it does? It Displays how good the test has been at the End.// If you tested 30 000 times for example, it would display 'safe'.// And if you test 500 times, it would display 'unacceptable'.// === [PF 1] ===// Performance Cost 1: The system itself gets a tiny little bit slower, so it might lag more, but this doesn't affect// the speed of other systems.// === [PF 2] ===// Performance Cost 2: The system itself gets a little bit slower, so it might lag more, but this doesn't affect// the speed of other systems.// ============= Thanks to =============// - Buster4 for Per2Clr and Int2Hex// - XieLong for a bit help with the colorcode algorithms.// - Vexorian for JassNewgNen// - Skater for Dec2HexglobalsprivateconstantintegerNOTICEABLE_FPS = 100// Eye can see 32 pcitures per second.privateconstantstringDISABLE_ERROR = "Emmeasure was Disabled."privateconstantstringABORT_ERROR = "Aborting Benchmark!"privateconstantbooleanVISUALIZE = false// Requires TestHelper used.privateconstantbooleanCOLOR_MASSIVELY = trueprivateconstantbooleanUNIQUE_ERRORS = falseprivateconstantbooleanTEST_FUNC_SPEED = true// This measures the speed of single functions. Costs a lot of speed! // WARNING: This costs a lot performance, because funcs are registered // by the system and use 2D Indexes.privateconstantrealTYPO_MIN_MATCH = 40.// Do not change!privateconstantintegerQUALITY = 15000// Do not change!privateconstantintegerVISION_VALUE = 100000// Do not change!endglobalsglobalsprivaterealarrayTime// How much time did a sys takeprivateintegerarrayRuns// How many times was it called?privaterealt0// temp valueprivaterealt1// temp valueprivateintegerClock// StopWatchprivateintegerSystem// temp ValueprivatebooleanRunning = false// temp Valueprivateintegersysc = 0// How many systems did you declare?privatestringarraysysn// NameprivateintegerarraysysmprivatestringarrayStableWord// Like 'Excellent'privaterealarrayFuncSpeed// For functions: How fast they've beenprivaterealarrayDividend// Rate/FrequencyprivateintegerarrayFuncExe// How often has a function be called?privatestringarrayGraph// Graph created at comparisonprivatebooleanDoTest = true// false = Abort Benchmark.privatestringTempFunc// Which function is just tested?privatetriggertrig = CreateTrigger()
privatetriggert = CreateTrigger()
privatebooleanRegisterSystems = trueprivatebooleanTest_Closed = trueprivateintegerWon = 0// Which System (Index of string array) was the fastest?privateintegerinstance = 0// Which instance is tested at the testhepler?privatemultiboardtableprivateintegerttt = 0endglobals// NOT MY CODEfunctionInt2HextakesintegerintreturnsstringlocalstringcharMap = "0123456789ABCDEF"localstringhex = ""localintegerindex = 0if ((int < 0) or (int > 0x7FFFFFFF)) thenreturn"|cffff0000Invalid argument in function Int2Hex()!|r"endifloopsetindex = ModuloInteger(int, 0x10) + 1setint = int / 0x10sethex = SubStringBJ(charMap, index, index) + hexexitwhen (int == 0)
endloopreturnhexendfunctionfunctionPer2Clrtakesstringtext, integerr, integerg, integerb, integeralphareturnsstringlocalstringarraystrif ((r >= 0) and (r <= 100) and (g >= 0) and (g <= 100) and (b >= 0) and (b <= 100) and (alpha >= 0) and (alpha <= 100)) thensetr = R2I(0xFF / 100.000 * r)
setg = R2I(0xFF / 100.000 * g)
setb = R2I(0xFF / 100.000 * b)
setalpha = R2I(0xFF / 100.000 * alpha)
if (alpha <= 0xF) thensetstr[0] = "0" + Int2Hex(alpha)
elsesetstr[0] = Int2Hex(alpha)
endifif (r <= 0xF) thensetstr[1] = "0" + Int2Hex(r)
elsesetstr[1] = Int2Hex(r)
endifif (g <= 0xF) thensetstr[2] = "0" + Int2Hex(g)
elsesetstr[2] = Int2Hex(g)
endifif (b <= 0xF) thensetstr[3] = "0" + Int2Hex(b)
elsesetstr[3] = Int2Hex(b)
endifreturn"|c" + str[0] + str[1] + str[2] + str[3] + textelsereturn"|cffff0000Invalid arguments in function Per2Clr()!|r"endifendfunctionfunctionDec2HextakesintegerireturnsstringlocalstringcharMap="0123456789ABCDEF"localstringhexifi>255thenreturn"|cffff0000This function is NOT designed to convert values above 255!|r"elseifi<0thenreturn"|cffff0000 The value is to small!|r"endififi<16thenreturn"0"+SubString(charMap, i, i+1)
elsesethex=SubString(charMap, i/16, i/16+1)
seti=ModuloInteger(i, 16)
sethex=hex+SubString(charMap, i, i+1)
returnhexendifendfunction// ====== MY CODE =======privatefunctionCreatePercentageBartakesrealPercentreturnsstringlocalrealRedlocalrealGreenlocalstringretlocalintegeri = 0ifCOLOR_MASSIVELYthenifPercent <= 67thensetRed = 100setGreen = 100 * (Percent / 67)
else//set Red = 300 - (100/33.3333*Percent) // Ist bereits ausgekürzt, eig 100 - (100 * (Procent - 66) / 33)//set Red = 100-Percent // 100 bis 0setRed = 3*(100-Percent)
setGreen = 100endifelsesetRed = 100-PercentsetGreen = Percentendifsetret = "["+ Per2Clr(" ",R2I(Red),R2I(Green),0,0)
loopseti = i + 1exitwheni == 100ifi-1 == R2I(Percent) thenifModuloInteger(i,2) == 1thensetret = ret + "|r"elsesetret = ret + "||r"endifsetret = ret + "|cff808080"endifsetret = ret + "|"endloopreturnret+" |r]"endfunctionglobalsprivateintegerErrorCount = 0privatestringarrayErrorsprivateintegerTypoCount = 0privatestringarrayTyposprivateintegerarrayTypoResultendglobalsfunctionBenchErrortakesstringsreturnsnothing//: We don't want to spam error messages, so each unique error is only displayed once.localintegeri = 0ifUNIQUE_ERRORSthenloopseti = i + 1exitwheni > ErrorCountifErrors[i] == "|cffff0000"+s+"|r"thenreturnendifendloopendif//: Error is unique: Register.setErrorCount = ErrorCount + 1setErrors[ErrorCount] = "|cffff0000"+s+"|r"callBJDebugMsg(Errors[ErrorCount])
endfunctionglobalsprivateintegerEPT = 100privateintegerThreads = 100privateintegerCURRENT_EPT = 0privateintegerCURRRENT_Threads = 0privatebooleanFinished = falseprivatebooleanNew = falseendglobalsstructTestHelperstaticmethodSetTesttakesintegerept, integerthreadsreturnsnothingifTest_Closedthensetinstance = 0setEPT = eptsetThreads = threadssetFinished = falsesetTest_Closed = falseelsecallBenchError("Started closed test during another one (TestHelper struct).")
callBenchError(ABORT_ERROR)
callTest.Disable()
endifendmethodstaticmethodNextInstancetakesnothingreturnsnothingsetCURRENT_EPT = CURRENT_EPT + 1ifCURRENT_EPT > EPTthensetCURRENT_EPT = 0setCURRRENT_Threads = CURRRENT_Threads + 1setNew = trueifCURRRENT_Threads > ThreadsthensetFinished = truesetTest_Closed = trueendifendififVISUALIZEthensetinstance = instance + 1callClearTextMessages()
callBJDebugMsg("Completed: "+R2S(instance/(EPT*Threads))+"%")
endifendmethodstaticmethodIsThreadNewtakesnothingreturnsbooleanlocalbooleanb = NewifbthensetNew = falseendifreturnbendmethodstaticmethodIsFinishedtakesnothingreturnsbooleanreturnFinishedendmethodendstructprivatefunctionI2Dtakesintegerunlimited, integersized, integersizereturnsintegerreturnsized+(unlimited*size)
endfunctionglobalsprivatestringcurrentFuncprivatestringarrayFuncsprivateintegerarrayFuncNumendglobalsstructTeststaticmethodDeclaretakesstringnamereturnsnothing//if RegisterSystems thensetsysc = sysc + 1setsysn[sysc] = namesetsysm[sysc] = 0//else// call BenchError("TEST: You may only declare systems in intitializers.")//endifendmethod// ====== ========= ====== Gets unique Indexes for Systems and functions.// ====== CONVERTER ======// ====== ========= ======staticmethodConverttakesstringnamereturnsintegerlocalintegeri = 0loopseti = i + 1exitwheni > syscifname == sysn[i] thenreturniendifendloopcallBenchError("Didn't find System: "+name )
callBenchError(ABORT_ERROR)
callTest.Disable()
return0// Don't return -1, it would crash wc3.endmethodstaticmethodConvertFunctakesintegercon, stringfuncnamereturnsinteger//local integer con = Test.Convert(name)// Uses 2D Indexes : NumberOfFunction, NumberOfSystemlocalintegeri = 0loopseti = i + 1exitwheni > FuncNum[con]
iffuncname == Funcs[I2D(i,con,sysc)] then//call BJDebugMsg("Found at "+I2S(I2D(i,con,sysc))+ " | "+I2S(i))returnI2D(i,con,sysc)
endifendloop//call BJDebugMsg("Registered at "+I2S(I2D(FuncNum[con],con,sysc)))// We don't use the var for loops anymore.setFuncNum[con] = FuncNum[con] + 1seti = I2D(FuncNum[con],con,sysc)
setFuncs[i] = funcnamesetDividend[i] = 1returnI2D(FuncNum[con],con,sysc)
endmethod// ====== ========= =======// ===== ENDCONVERTER =====// ====== ========= =======staticmethodDisplayErrorstakesnothingreturnsnothinglocalintegeri = 0loopseti = i + 1exitwheni > ErrorCountcallBJDebugMsg("Error Nr."+I2S(i)+": "+Errors[i])
endloopendmethodstaticmethodSetFuncDividendtakesstringName, stringFuncName, realDivisorreturnsnothingsetDividend[Test.ConvertFunc(Test.Convert(Name),FuncName)] = DivisorendmethodstaticmethodDisabletakesnothingreturnsnothingsetDoTest = falseendmethodstaticmethodStarttakesstringName, stringFuncNamereturnsintegerifDoTestthenifRunning == falsethensetTempFunc = FuncNamesetRunning = truesetSystem = Test.Convert(Name)
setRuns[System] = Runs[System] + 1sett0 = StopWatchMark(Clock)
return0elsecallBJDebugMsg("TEST: System "+sysn[System]+", function "+TempFunc+" is causing an open thread when calling "+FuncName+".")
callBenchError(ABORT_ERROR)
callTest.Disable()
endifendifreturn0endmethodstaticmethodEndtakesnothingreturnsintegersett1 = StopWatchMark(Clock)
ifDoTestthenifRunningthensetTime[System] = Time[System] + (t1-t0)
ifTEST_FUNC_SPEEDthensetttt = Test.ConvertFunc(System,TempFunc)
//call BJDebugMsg("Int for "+TempFunc+" - "+I2S(System)+": "+I2S(funcInt)) setFuncExe[ttt] = FuncExe[ttt] + 1setFuncSpeed[ttt] = FuncSpeed[ttt] + (t1-t0)
endifsetSystem = 0setRunning = falseendifendifreturn0endmethodstaticmethodDisplaytakesstringNamereturnsnothinglocalintegersys = Test.Convert(Name)
ifDoTestthencallBJDebugMsg(Name+ " took "+R2S(Time[sys])+ " seconds." )
elsecallBenchError(DISABLE_ERROR)
endifendmethodstaticmethodDisplayFunctakesstringName, stringFuncNamereturnsnothinglocalintegersys = Test.ConvertFunc(Test.Convert(Name),FuncName)
ifDoTestthenifTEST_FUNC_SPEED == falsethencallBenchError("TEST: Started DisplayFunc without having TEST_FUNC_SPEED enabled.")
returnendifcallBJDebugMsg("Speed of "+Per2Clr(Name,100,80,20,0)+"|r function "+Per2Clr(FuncName,0,100,0,0)+"|r: "+R2S(FuncSpeed[sys]*VISION_VALUE/FuncExe[sys]/Dividend[sys])+ " EPS.")
elsecallBenchError(DISABLE_ERROR)
endifendmethodstaticmethodCompareFuncstakesintegerFuncIndexreturnsnothinglocalintegeri = 0localintegerfuncInd = 0ifDoTest == falsethencallBenchError(DISABLE_ERROR)
returnendififTEST_FUNC_SPEEDthenloopseti = i + 1exitwheni > syscsetfuncInd = I2D(FuncIndex,i,sysc)
callBJDebugMsg("Speed of "+Per2Clr(sysn[i],100,80,20,0)+"|r, function "+Per2Clr(Funcs[funcInd],0,100,0,0)+"|r: "+R2S(FuncSpeed[funcInd]*VISION_VALUE/FuncExe[funcInd]/Dividend[funcInd])+ " EPS.")
endloopendifendmethodstaticmethodComparetakesnothingreturnsnothinglocalintegeri = 0localrealhighest = Time[1]
localrealperclocalrealExec = 0.localintegerTempifDoTest == falsethencallBenchError(DISABLE_ERROR)
returnendifsetWon = 1loopseti = i + 1exitwheni > syscifTime[i] < highestthensethighest = Time[i]
//call BJDebugMsg("Won; "+I2S(i))setWon = iendifendloopseti = 0callBJDebugMsg("================================")
loopseti = i + 1exitwheni > syscsetExec = Exec + Runs[i]
setperc = highest/Time[i]*100.callBJDebugMsg(sysn[i]+": "+R2S(perc)+"%")
setGraph[i] = CreatePercentageBar(perc)
callBJDebugMsg(Graph[i])
endloopsetExec = Exec / syscsetTemp = R2I(Exec/QUALITY)
ifExec >= 500*500thensetTemp = 8elseifTemp > 7thensetTemp = 7endifendifcallBJDebugMsg("Testing Quality: "+StableWord[Temp])
debugcallBJDebugMsg(Per2Clr("Test started in Debug Mode!",80,30,0,0)+"|r")
callBJDebugMsg("Emmeasure by Mr.Malte")
endmethod// This part is kinda ugly, I used many BJs - but I don't care.// It hasn't to be fast, it's only executed once and the BJs save some work.staticmethodcreateTabletakesnothingreturnsnothinglocalintegeri = 0localintegeri2 = 0localintegerfuncInd = 0localintegercc = sysc+1+2localrealarVa1localrealtemplocalrealarrayRatelocalrealarVa2ifWon == 0thencallTest.Compare()
callClearTextMessages()
endifsettable = CreateMultiboardBJ( 4, 20, "Test Information" )
debugcallMultiboardSetTitleText(table,"Test Information (Debug Mode)")
callMultiboardSetItemStyleBJ( table, 0, 0, true, false )
//call MultiboardSetTitleText(table,"Emmeasure Test")//call MultiboardSetRowCount(table,1+sysc)//call MultiboardSetColumnCount(table,4)callMultiboardSetItemWidthBJ( table, 0, 0, 7.00 )
callMultiboardSetItemWidthBJ( table, 2, 0, 12.00 )
callMultiboardSetItemWidthBJ( table, 1, 0, 13.50 )
callMultiboardSetItemColorBJ(table,0,1,30,70,90,0)
callMultiboardSetItemValueBJ(table,1,1,"System")
callMultiboardSetItemValueBJ(table,2,1,"Speed")
callMultiboardSetItemValueBJ(table,3,1,"EPF*")
callMultiboardSetItemValueBJ(table,4,1,"CPF**")
setcc = cc + 1callMultiboardSetItemColorBJ(table,0,cc,30,70,90,0)
callMultiboardSetItemValueBJ(table,1,cc,"Function")
callMultiboardSetItemValueBJ(table,2,cc,"Speed")
callMultiboardSetItemValueBJ(table,3,cc,"Rate")
// Declare Rate: How often systems are executed.seti = 0loopseti = i + 1seti2 = 0exitwheni > syscsetRate[i] = 0loopseti2 = i2 + 1exitwheni2 > FuncNum[i]
setfuncInd = I2D(i2,i,sysc)
setRate[i] = Rate[i] + Dividend[funcInd]
endloop//call BJDebugMsg("Rate ["+I2S(i)+"] - "+I2S(Rate[i]))endloopseti2 = 0seti = 0loopseti = i + 1seti2 = 0exitwheni > sysccallMultiboardSetItemValueBJ(table,1,i+1,sysn[i])
callMultiboardSetItemValueBJ(table,1,i+1+sysc,sysn[i])
callMultiboardSetItemValueBJ(table,2,i+1,Graph[i])
setcc = cc + 1callMultiboardSetItemColorBJ(table,1,cc,100,80,20,0)
callMultiboardSetItemValueBJ(table,1,cc," -"+sysn[i])
setarVa1 = 0.setarVa2 = 0.// Calculate arithmetric value of speef of the funcs.loopseti2 = i2 + 1exitwheni2 > FuncNum[i]
setfuncInd = I2D(i2,i,sysc)
settemp = FuncSpeed[funcInd]/FuncExe[funcInd]/Dividend[funcInd]
setarVa1 = arVa1 + tempsetarVa2 = arVa2 + (temp*Dividend[funcInd])
setcc = cc + 1// Add the function valuescallMultiboardSetItemValueBJ(table,1,cc,Funcs[funcInd])
callMultiboardSetItemValueBJ(table,2,cc,R2S(temp*VISION_VALUE))
callMultiboardSetItemValueBJ(table,3,cc,R2S(Dividend[funcInd]/Rate[i]*100.)+"%")
endloopsetarVa1 = Runs[i]/arVa1setarVa2 = Runs[i]/arVa2// Add the System speed valuescallMultiboardSetItemValueBJ(table,3,i+1,I2S(R2I(arVa1/NOTICEABLE_FPS/VISION_VALUE)))
callMultiboardSetItemValueBJ(table,4,i+1,I2S(R2I(arVa2/NOTICEABLE_FPS/VISION_VALUE)))
endloopcallMultiboardSetItemValueBJ(table,2,sysc+2,"*k Executions per Frame")
callMultiboardSetItemValueBJ(table,2,sysc+3,"** Cycles per Frame")
callMultiboardSetItemValueBJ(table,1,sysc+2,"")
callMultiboardSetItemValueBJ(table,1,sysc+3,"")
callMultiboardSetItemValueBJ(table,1,sysc+4,"System")
setcc = cc + 2callBJDebugMsg(Per2Clr(sysn[Won],80,30,0,0))
callMultiboardSetItemValueBJ(table,2,cc,"Testwinner: "+Per2Clr(sysn[Won],80,30,0,0)+"|r")
callMultiboardDisplay(table,true)
endmethodendstructprivatefunctionAbortDebugtakesnothingreturnsnothing// Debug mode can be detected, but I don't know how to detect Wc3err.// Commented this to allow tests in Debug mode.//debug call BenchError("Test was started in Debug Mode; Disabling Benchmark!")//debug call Test.Disable()setRegisterSystems = false// Test QualityifQUALITY/((3*5*5*2*2*2*2*5)+(9*50*20)) == 1andQUALITY < 243094andQUALITY > 5432thenelsecallBenchError("Constant 'QUALITY' was changed; Disabling Benchmark")
callTest.Disable()
endifendfunctionprivatefunctionInittakesnothingreturnsnothingsetClock = StopWatchCreate()
callTriggerRegisterTimerEventSingle(t,0.001)
callTriggerAddAction(t,functionAbortDebug)
setStableWord[0] = Per2Clr("unacceptable",100,0,0,0)
setStableWord[1] = Per2Clr("poor",86,14,0,0)
setStableWord[2] = Per2Clr("lacking",71,28,0,0)
setStableWord[3] = Per2Clr("not bad",100,100,0,0)
setStableWord[4] = Per2Clr("fine",45,70,0,0)
setStableWord[5] = Per2Clr("safe",30,80,0,0)
setStableWord[6] = Per2Clr("Great",20,80,0,0)
setStableWord[7] = Per2Clr("Excellent",0,100,0,0)
setStableWord[8] = Per2Clr("Perfect",0,100,0,0)
endfunctionendlibrary
so far?
It is a system used to test the speed of functions using StopWatch. A collection of fancy wrappers.
I planned this Introduction:
Hidden information:
TestWrapper is an exact copy of my old system Emmeasure which is used to test the speed of scripts or parts of scripts. It is collection of fancy wrappers that use StopWatchMark.
I have to warn you: This is a lot of text. But if you read it you know how to make perfect and accurate Benchmarks.
How is Benchmark structured?
Very simple. You have to do the four golden steps in order to get a scripts speed.
The golden steps:
Declare the scripts name
Start the test instance
End the test instance
Display the results.
You can follow these steps by using these four functions:
Test.Declare has to run in an initializer and tells the Benchmark that you are going to test the speed of that script Test.Start starts a stopwatch and registers how much time elapses until the Test.End function with the functionName and the systemName as key. It returns an integer, so you can even place it before other variable locals in a function. Test.End ends the last test you started with Test.Start. The time that elapsed between Test.Start and Test.End is stored for the comparison Test.Compare compares how fast a testing instance of each system runs averagely. The fastest system will have 100% speed, the others less.
Test.createTable creates a multiboard where the systems, their functions and the speed of the functions and a comparison are listed. Test.CompareFuncs compares the speed of the functions of the system. The Index means the number of the function. Numbers are assigned in the order of usage of the functions. If you use Test.CompareFuncs(1) the first function that was used for each system is compared to the others. Display display how much runtime a system consumed at all. DisplayFunc displays how quick a function runs.
There are some things that make tests inaccurate:
1.) Causing open threads: If users run a benchmarked function inside of a benchmarked function, they cause what I call open thread. The called function counts multiple times.
This system solves that problem. When users cause open thread, they get a detailed debug message where the open thread was caused.
JASS:
callBJDebugMsg("TEST: System "+sysn[System]+", function "+TempFunc+" is causing an open thread when calling "+FuncName+".")
2.) Having too few system usages:
The example I showed to "test" TimerUtils was definitely not enough and very inaccurate.
If you want accurate results, you should test multiple times! Run the function a few hundred times. The system will display how accurate the test was at the end of it and choose words from 8 steps to describe it (unacceptable - perfect).
3.) Hitting the OP-Limit: But just changing the script to this:
JASS:
functionTesttakesnothingreturnsnothinglocalintegeri = 0localtimertloopseti = i + 1exitwheni > 600callTest.Start("TimerUtils","GeneralTest")
sett = NewTimer()
callSetTimerData(t,1)
callGetTimerData(t)
callReleaseTimer(t)
callTest.End()
endloopcallTest.Display("TimerUtils")
endfunction
won't work, because all the functions use "OPs". Each native uses one op. If the number of used ops without having some time between hits 8191, the thread stops. The function simple stops doing anything. And to be labelled with "Perfect" the test should run
250 000 times. Well, you can use the function TriggerSyncReady() to reset the op counter. But you will have to find out when the resetting is neccesary. If Timerutils cycle needs about 60 ops, you'd have to call TriggerSyncReady() every time the integer i reaches a multiple of 8191/60 you have to call TriggerSyncReady. And TriggerSyncReady is like a tiny little wait - your test will take years. The better solution are 0 timers.
Timers that fire the function every 0. seconds. You have a counter in the function and if the counter reaches a certain integer the timer is destroyed and the results are displayed.
Just like here:
4.) Using the same dummies: It is really bad to always use the same dummies for a speed test. Why you need dummies? Imagine you want to test AutoIndex. Which unit do you get the id from using GetUnitId? You have to create a dummy. An example could look like this:
Of course this could not be a nice AutoIndex test anyways, because you have to structure it different and give the main points and many AutoIndex things are done internally. But I will tell you more about that problem later.
The problem with the test script I just showed, is that AutoIndex does not make new indexes for units each time you use GetUnitId, but just returns the index of the unit. Just imagine we have already injected Benchmark into AutoIndex and the speed of the Index-Assigning functions is stored, too.
To get more accurate values we'd have to change the dummy unit sometimes. You won't get the Index of a unit 250000 times while it's alive. So we do this:
SetTest just teals the TestHelper how many threads and how many executions per thread you want. NextInstance is just a counter. You have to do that at the beginning of your testing instance. IsThreadNew If the instances reach the number that matches the first parameter of SetTest, this will return true. In my example it will return true once in hundred times. IsFinished returns whether the instances reached the two parameters of the SetTest function multiplied with each other. In my example it will returns true if NextInstance was called 100*100 times.
Injecting code
I said something about I'm going to write later how to test a system like AutoIndex correctly. Well, I also said that many things are done internally in AutoIndex.
Here is an example of such a function:
If you want to include these functions into the test, too, there are three simple rules to follow:
Insert "local integer Benchmark = Test.Start("AutoIndex","###")" into each function you want to test
End the test before the function stops
Be careful that you don't cause open threads.
This is exactly the situation where the anti-open-thread functionality is very important.
Imagine you want to test the function now:
JASS:
privatestaticmethodunitEntersMaptakesunitureturnsnothinglocalintegerBenchmark = Test.Start("AutoIndex","AssignIndex")
localintegerindexlocalintegern = 0ifgetIndex(u) != 0then//If a unit already has an ID, don't assign a new one.return//This only happens if a unit leaves the entire map area.endifsetindex = create()
callsetIndex(u, index) //Assign an index to the entering unit.callUnitAddAbility(u, LeaveDetectAbilityID) //Add the leave detect ability to the entering unit.callUnitMakeAbilityPermanent(u, true, LeaveDetectAbilityID) //Prevent it from disappearing on morph.setdead[index] = IsUnitType(u, UNIT_TYPE_DEAD) //Reset all of the flags for the enteringsetsummoned[index] = IsUnitType(u, UNIT_TYPE_SUMMONED) //unit. These flags are necessary to detectsetanimated[index] = false//when the unit leaves the map.setnodecay[index] = falsesetremoving[index] = falsedebugsetaltered[index] = false//In debug mode, this flag tracks wheter a unit's index was altered.setidunit[index] = u//Attach the unit that is supposed to have this index to the index.loop//Run the OnUnitIndexed events.exitwhenn > indexfuncs_ncallindexfuncs[n].evaluate(u)
setn = n + 1endloopcallTest.End()
endmethod
But you also benchmarked the SetIndex function which is used in the unitEntersMap function:
Then you cause an open thread and make the time the setIndex function uses count twice. There are some solutions of how to solve that problem. My favourite solution is to not insert Benchmark to the setIndex function at all because these two rules say so: a) If the function is just called by other internal functions, don't benchmark it. It will definitely cause open threads.
b) If a function is used internally and outside of the system (like GetUnitId in AutoIndex) it should only be benchmarked outside.
Another problem with testing AutoIndex is that it has special focuses.
GetUnitId will be used much more often than indexes are assigned. So you should make GetUnitId count more than the other functions. Do you remember the values 'rate' in the table? Rate means how much the functions count.
Is the function that can give other functions different importancies. The time the function needs is multiplied with the real Weight.
Example:
JASS:
callSetFuncWeight("AutoIndex","GetUnitId",0.2)
Will make GetUnitId count 5 times more.
But to make the test accurate you should do such things with the systems you want to compare AutoIndex to, too.
In the special situation of AutoIndex thit would fit best:
I've read through the documentation and I still have no idea how the heck do you do your benchmarks.
How I do my benchmarks or how you (Antiarf) do your benchmarks?
Quote:
Furthermore, including non-functional fluff like MakeGradientText doesn't make this a more useful resource.
True. Will remove it.
Quote:
Why would anyone use this over, say, the Stopwatch natives...?
These are just wrappers for the Stopwatch natives that make testing easier.
Well, I did not make a list of advantages.
If you want to find out all the advantages you have to read thrugh all the hidden stuff, I'm sorry.
A raw summarize:
detects open threads
makes nice graphical compares
helps you making accurate tests
makes the work with the stopwatch natives much easier.
You can take a look at miy TimerUtils example.
Let me quote grim:
Quote:
Originally Posted by grim001
Err I am pretty sure this system does use the stopwatch natives, it just adds a bunch of fancy wrappers to make testing systems easier.
edit:
The biggest problem of this system I see right now are these uberfeatures:
- Displaying errors uniquely (That's where Optibug came from)
- Fixing typos you did when writing system names
But I'm not sure whether they can stay or not.
They cause code bloat, but they are not negative and since this uses StopWatch and will never run in a real map, I think actually it's not important.
i read your documentation, but have no clue what following functions do:
JASS:
// Test.RegisterInstance(sysname) : Registers, that a new instance of the test has started.// Test.RemoveInstance(sysname) : Removes a registered Instance.// Test.RunThisCode(sysname) : Returns, if the system has exactly one registered Instance.// Test.RunCode() : Displays if no System Instances are running.
documentation is still quite lacking...
and that "detect typos" thing is totally unneccessary
That's true.
Will remove it, too.
The documentation of the system iteself is lacking, that's true, too.
But I will make a proper documentation for it later. The actual documentation is hidden in the first post.
Oh, I removed the part you quoted anyways.
It was useless.
@ Antiarf: No I understand.
You read the documentation of the system itself.
I will rework it.
If you want to understand it, you should read the hidden part.
How I do my benchmarks or how you (Antiarf) do your benchmarks?
Well, seeing as how he was talking to you and used the word 'you', it would suggest he is wondering how you do your benchmarks.
Quote:
Originally Posted by Mr.Malte
These are just wrappers for the Stopwatch natives that make testing easier.
It's strange, because in the few cases I've ever actually benchmarked anything, they were already stupid-easy to use. I can't imagine needing any wrapper syntax for it at all. I mean, basically your library does nothing useful, it just creates a multiboard to display some stats from benchmarking.
your library does nothing useful, it just creates a multiboard to display some stats from benchmarking.
It compares all the systems. It puts out nice graphics where you can just take a screen of to proof your benchmarks. Graphics and such things like multiboards are always better to have a nice comparison.
Of course this doesn't seem useful for such an easy benchmark like TimerUtils.
But it becomes useful in more complicated systems like AutoIndex.
And since this is a system that's not used in real maps, it's ot important how long the library is or whatever. I think if it gives you small advanataged it's okay, too.
If a user claims your system being slow or that you could do something faster differently;
Graphics are always more persuading.
And since the screen of Emmeasure contains necessary information you can trust the test more as if it would only display these values:
So this makes your code a little shorter and displays very nice, trustable graphics.
What's wrong about using this over unwrapped StopWatch marks?
Also, as I said, the real usefulness shows when you have to place the benchmark code directly into other libraries. Causing open threads is very probable then.
Example:
JASS:
libraryPseudoNiceglobalsprivatestringarrayPseudoendglobalsfunctionGetPseudotakesintegerireturnsstringreturnPseudo[i]
endfunctionprivatefunctionModifyPseudotakesintegeri, integerbreturnsnothingifGetRandomInt(0,1) == 0thensetPseudo[i] = "0"+ GetPseudo(b)
elsesetPseudo[i] = "1"+ GetPseudo(b)
endifendfunction// Fired when a unit is attackedfunctionUnitAttacktstakesnothingreturnsnothingcallModifyPseudo(GetUnitKills(GetTriggerUnit()),GetUnitKills(GetAttacker()))
endfunctionendlibrary
This:
JASS:
libraryPseudoNiceglobalsprivatestringarrayPseudoendglobalsfunctionGetPseudotakesintegerireturnsstringcallTest.Start("Pseudo","ModifyPseudo")
seti = Pseudo[i]
callTest.End()
returniendfunctionprivatefunctionModifyPseudotakesintegeri, integerbreturnsnothingcallTest.Start("Pseudo","ModifyPseudo")
ifGetRandomInt(0,1) == 0thensetPseudo[i] = "0"+ GetPseudo(b)
elsesetPseudo[i] = "1"+ GetPseudo(b)
endifcallTest.End()
endfunction// Fired when a unit is attackedfunctionUnitAttacktstakesnothingreturnsnothingcallTest.Start("Pseudo","UnitAttacks")
callModifyPseudo(GetUnitKills(GetTriggerUnit()),GetUnitKills(GetAttacker()))
callTest.End()
endfunctionendlibrary
Will be buggy and inaccurate.
Benchmark abouts the benchmark and gives you error messages. Normal StopWatch wont. And believe me, that is useful. I already caused many open threads when testing systems internally I wouldn't have detected otherwise because the systems weren't made by me and I didn't know them.
A perfect example is KT2:
which does many internal things and was hard to test. Without the benchmark library I wouldn't have been able to make accurate tests.
That's the thread where I started to think about the design and where I started making the systems. You should notice the many tests I did. Those tests would have seemed less important with pure values.
I am thinking about some possibilites now:
- I keep the code as it is (maybe make it shorter/remove some unneccesary uberfeatures)
and everythings fine.
- I make my own thread where users can request benchmarks or where I simply test many important things and attach all the screennshots to the first post (hashtables vs. handleId-offset vs UnitUserData for example)
- I offer two versions of Benchmark. The current (reworked a bit) and this one: