Further testing pretty much just confirms what I've already known, but perhaps didn't communicate properly. It mostly just adds to my arguments about why proper frame locking is necessary, and perhaps aids in why and how to do so.
Here's what I did. I set up my profiling, checking the amount of time it takes for each individual part of your Main function to execute. I set it to loop 100 times, and I put all of the echoes inside the frame lock.
Here's the results with 1 core enabled: (comments added to the titles for each part, for your understanding, and note that the listed order is not the order of execution)
Code: Select all
VGTest.Program.Main:base [color=red]Total time for 100 iterations of VGTest.Instance.Go()[/color]
----------------------------------------
Executions: 1
Minimum time: 249.72785694328ms
Average time: 249.72785694328ms
Maximum time: 249.72785694328ms
Total time: 249.72785694328ms (108.363429119599%)
VGTest.VGTest.Go:base [color=red]Individual executions of VGTest.Go().[/color]
----------------------------------------
Executions: 100
Minimum time: 0.284157585917854ms
Average time: 2.2550991897178ms
Maximum time: 179.980441464096ms
Total time: 225.536183291422ms (97.4530942513723%)
VGTest.VGTest.Go:Echo("Required extension (ISXVG) not ready")
----------------------------------------
Executions: 100
Minimum time: 0.0128527521654093ms
Average time: 0.0201173512154233ms
Maximum time: 0.122380553227158ms
Total time: 2.0148086057558ms (0.870589056220247%)
VGTest.VGTest.Go:Echo("Starting up.")
----------------------------------------
Executions: 100
Minimum time: 0.0120145291981ms
Average time: 0.0368818105616094ms
Maximum time: 1.68454875663593ms
Total time: 3.71304833752445ms (1.60439023271541%)
VGTest.VGTest.Go:isxvg.IsReady [color=red]calls to isxvg.IsReady, WITHOUT the ISXVG() call, that's shown in another section[/color]
----------------------------------------
Executions: 100
Minimum time: 0.00167644593461861ms
Average time: 0.137747974294496ms
Maximum time: 13.5959765297569ms
Total time: 13.7988264878458ms (5.96240620309154%)
VGTest.VGTest.Go:Lock [color=red]Calls to FrameLock.Lock()[/color]
----------------------------------------
Executions: 100
Minimum time: 0.00279407655769768ms
Average time: 0.504889633975971ms
Maximum time: 50.0257055043308ms
Total time: 50.508521933501ms (21.8244881005317%)
VGTest.VGTest.Go:VGExt = new Extension()
----------------------------------------
Executions: 100
Minimum time: 0.00167644593461861ms
Average time: 0.00949986029617212ms
Maximum time: 0.755797708857223ms
Total time: 0.961441743503772ms (0.415434328450127%)
VGTest.VGTest.Go:VGExt.ISXVG() [color=red]done before the ISXVG.IsReady call[/color]
----------------------------------------
Executions: 100
Minimum time: 0.00474993014808606ms
Average time: 0.088013411567477ms
Maximum time: 8.02542609667505ms
Total time: 8.8262084381112ms (3.81376198820432%)
And here it is with 2 cores enabled:
Code: Select all
VGTest.Program.Main:base
----------------------------------------
Executions: 1
Minimum time: 1784.61385861973ms
Average time: 1784.61385861973ms
Maximum time: 1784.61385861973ms
Total time: 1784.61385861973ms (102.041404531889%)
VGTest.VGTest.Go:base
----------------------------------------
Executions: 100
Minimum time: 0.281084101704387ms
Average time: 17.6918133556859ms
Maximum time: 408.022911427773ms
Total time: 1769.20368818106ms (101.160275301584%)
VGTest.VGTest.Go:Echo("Required extension (ISXVG) not ready")
----------------------------------------
Executions: 100
Minimum time: 0.0131321598211791ms
Average time: 0.0924839340597932ms
Maximum time: 2.11958647666946ms
Total time: 9.25062866722548ms (0.528936350823097%)
VGTest.VGTest.Go:Echo("Starting up.")
----------------------------------------
Executions: 100
Minimum time: 0.0120145291981ms
Average time: 0.126571668063705ms
Maximum time: 2.96870634255379ms
Total time: 12.6716960044705ms (0.724547583137884%)
VGTest.VGTest.Go:isxvg.IsReady
----------------------------------------
Executions: 100
Minimum time: 0.00167644593461861ms
Average time: 0.057837384744342ms
Maximum time: 5.58284436993574ms
Total time: 5.78932662754959ms (0.331024561708788%)
VGTest.VGTest.Go:Lock
----------------------------------------
Executions: 100
Minimum time: 0.00307348421346745ms
Average time: 15.9457949147807ms
Maximum time: 405.354568315172ms
Total time: 1594.58982956133ms (91.1761303851756%)
VGTest.VGTest.Go:VGExt = new Extension()
----------------------------------------
Executions: 100
Minimum time: 0.00167644593461861ms
Average time: 0.00922045264040235ms
Maximum time: 0.693769209276334ms
Total time: 0.922324671696005ms (0.0527370694112312%)
VGTest.VGTest.Go:VGExt.ISXVG()
----------------------------------------
Executions: 100
Minimum time: 0.00474993014808606ms
Average time: 0.167365185806091ms
Maximum time: 9.48225761385862ms
Total time: 16.7401508801341ms (0.956641275335%)
The difference in time is very significant. 1769ms with 2 cores, and 249ms with 1 core. You'll ALSO notice that the only non-negligible difference in execution times is the call to FrameLock.Lock(), for a total of 1594ms. That's very significant.
Now, here's another test, with both cores enabled, with the only difference being that I added using (new FrameLock(true)) around the for (int i = 0 ; i < 100 ; i++) VGTest.Instance.Go(); loop, so that the entire loop iterates in 1 frame instead of up to 100.
Code: Select all
VGTest.Program.Main:base
----------------------------------------
Executions: 1
Minimum time: 329.86392847164ms
Average time: 329.86392847164ms
Maximum time: 329.86392847164ms
Total time: 329.86392847164ms (175.02708917151%)
VGTest.VGTest.Go:base
----------------------------------------
Executions: 100
Minimum time: 0.280804694048617ms
Average time: 1.74713607152836ms
Maximum time: 120.704945515507ms
Total time: 174.731489242805ms (92.7132108396039%)
VGTest.VGTest.Go:Echo("Required extension (ISXVG) not ready")
----------------------------------------
Executions: 100
Minimum time: 0.0128527521654093ms
Average time: 0.0606314613020397ms
Maximum time: 3.9158982956133ms
Total time: 6.06817546800782ms (3.20320071579427%)
VGTest.VGTest.Go:Echo("Starting up.")
----------------------------------------
Executions: 100
Minimum time: 0.0120145291981ms
Average time: 0.109527801061749ms
Maximum time: 6.22687901648505ms
Total time: 10.9639564124057ms (5.78753090007216%)
VGTest.VGTest.Go:isxvg.IsReady
----------------------------------------
Executions: 100
Minimum time: 0.00167644593461861ms
Average time: 0.0922045264040235ms
Maximum time: 9.03827884884046ms
Total time: 9.24504051411009ms (4.88016876711487%)
VGTest.VGTest.Go:Lock
----------------------------------------
Executions: 100
Minimum time: 0.00279407655769768ms
Average time: 0.0729253981559095ms
Maximum time: 6.66191673651858ms
Total time: 7.29533389214865ms (3.85097940369225%)
VGTest.VGTest.Go:VGExt = new Extension()
----------------------------------------
Executions: 100
Minimum time: 0.00167644593461861ms
Average time: 0.0111763062307907ms
Maximum time: 0.925956971221012ms
Total time: 1.13411567476949ms (0.598664320168015%)
VGTest.VGTest.Go:VGExt.ISXVG()
----------------------------------------
Executions: 100
Minimum time: 0.00474993014808606ms
Average time: 0.0799105895501537ms
Maximum time: 7.27437831796591ms
Total time: 8.0148086057558ms (4.23076768268527%)
Now the total execution time with 2 cores is 329ms, and 174ms of it is taken by the same Go() function originally used, implying that it took me roughly 100-130ms to lock the frame initially, which isn't in the profiling info but can be deduced from the differences between the total execution time of main() and VGTest.Go() with (3rd paste) and without the lock (first two pastes), and knowing that the lock is the only difference for that.
So what does this tell us?
The first two tests both released and re-locked the frame each iteration of the loop. The first test was either a) more successful at re-locking before the frame actually advanced, or b) took significantly less time for the frame to advance. And it wasn't B. So, with a single core, the chances of re-locking before the frame advances is much greater than with two cores. The reason for this is fairly obvious -- the main thread can be competing with the frame locking threads at the same instant, instead of only on context switches (in other words, both CPU cores are executing threads at the same time, and both CPU cores are context switching. a context switch is when one core rotates to processing another thread.). So, when it's all running on one core, the bet is that the thread will not switch contexts before you re-lock the frame. The operating system decides when that is, usually based on some amount of time based on the priority of the thread, unless the thread yields control (e.g. by using Sleep()). When it's running on multiple cores, the bet must ALSO take into account that the thread may be running on another CPU (core) entirely.
The game's main thread has a significantly better chance of taking control between your frame locks when it is running on another core.
So... how do we use this info to our advantage?
Well, first of all we can cop out and set the processor affinity to a single core. But beyond that, obviously if the game has a good chance of taking control between our frame locks, then we need to make sure that that doesn't happen when we intended for something to actually perform well. Loops that contain implicit or explicit frame locks can be, in turn, explicitly frame locked to force the loop to execute in a single frame -- the expected savings could be dozens of milliseconds per frame lock. Any call to anything in the Inner Space API should be assumed to implicitly frame lock, so this of course includes any loop that simply does InnerSpace.Echo, and so on.
This is, of course, nothing new, it's what I've been saying all along. However, now there's a significantly better argument in favor of greedy frame locking, with test data to back it up and showing the difference between 1 and 2 cores.
But as far as any bugs in the API, absolutely not. The data conclusively tells me that the API in use in your small test case is performing as intended, though perhaps not as initially predicted (in hindsight, it's obvious and makes perfect sense).
I may distribute my profiling code since it would be beneficial to all of these people trying to report that the API is slow.