From 8dfeaf0f3e971a26b697308cb3e464061f0a81af Mon Sep 17 00:00:00 2001 From: Florian De Rop <55103921+fderop@users.noreply.github.com> Date: Thu, 1 Oct 2026 23:29:10 -0700 Subject: [PATCH 1/3] perf: skip allocation for absent collision callbacks --- Source/Entities/MovableObject.cpp | 12 +- Tools/Benchmarks/CaptureCollisionGame.py | 49 + .../CollisionCallbacks-game-results.csv | 1441 +++++++++++++++++ .../CollisionCallbacks-game-summary.json | 84 + .../CollisionCallbacks-results.json | 674 ++++++++ Tools/Benchmarks/CollisionCallbacks.cpp | 248 +++ Tools/Benchmarks/CollisionCallbacks.md | 157 ++ Tools/Benchmarks/CollisionCallbacks.py | 73 + .../PerformanceStress.rte/Activities.ini | 10 + .../PerformanceStress.rte/CallbackCheck.lua | 71 + .../PerformanceStress.rte/CollisionProbe.lua | 11 + .../EmptyCollisionProbe.lua | 3 + .../PerformanceStress.rte/Index.ini | 4 + .../PerformanceStress.rte/Stress.lua | 35 + 14 files changed, 2870 insertions(+), 2 deletions(-) create mode 100644 Tools/Benchmarks/CaptureCollisionGame.py create mode 100644 Tools/Benchmarks/CollisionCallbacks-game-results.csv create mode 100644 Tools/Benchmarks/CollisionCallbacks-game-summary.json create mode 100644 Tools/Benchmarks/CollisionCallbacks-results.json create mode 100644 Tools/Benchmarks/CollisionCallbacks.cpp create mode 100644 Tools/Benchmarks/CollisionCallbacks.md create mode 100644 Tools/Benchmarks/CollisionCallbacks.py create mode 100644 Tools/Benchmarks/PerformanceStress.rte/Activities.ini create mode 100644 Tools/Benchmarks/PerformanceStress.rte/CallbackCheck.lua create mode 100644 Tools/Benchmarks/PerformanceStress.rte/CollisionProbe.lua create mode 100644 Tools/Benchmarks/PerformanceStress.rte/EmptyCollisionProbe.lua create mode 100644 Tools/Benchmarks/PerformanceStress.rte/Index.ini create mode 100644 Tools/Benchmarks/PerformanceStress.rte/Stress.lua diff --git a/Source/Entities/MovableObject.cpp b/Source/Entities/MovableObject.cpp index a8190cc418..7bd2a05b5a 100644 --- a/Source/Entities/MovableObject.cpp +++ b/Source/Entities/MovableObject.cpp @@ -763,7 +763,11 @@ bool MovableObject::IsAtRest() { bool MovableObject::OnMOHit(HitData& hd) { if (hd.RootBody[HITOR] != hd.RootBody[HITEE] && (hd.Body[HITOR] == this || hd.Body[HITEE] == this)) { - RunScriptedFunctionInAppropriateScripts("OnCollideWithMO", false, false, {hd.Body[hd.Body[HITOR] == this ? HITEE : HITOR], hd.RootBody[hd.Body[HITOR] == this ? HITEE : HITOR]}); + static const std::string functionName = "OnCollideWithMO"; + auto function = m_FunctionsAndScripts.find(functionName); + if (function != m_FunctionsAndScripts.end() && !function->second.empty()) { + RunScriptedFunctionInAppropriateScripts(functionName, false, false, {hd.Body[hd.Body[HITOR] == this ? HITEE : HITOR], hd.RootBody[hd.Body[HITOR] == this ? HITEE : HITOR]}); + } } return hd.Terminate[hd.RootBody[HITOR] == this ? HITOR : HITEE] = false; } @@ -775,7 +779,11 @@ unsigned char MovableObject::HitWhatTerrMaterial() const { void MovableObject::SetHitWhatTerrMaterial(unsigned char matID) { m_TerrainMatHit = matID; m_LastCollisionSimFrameNumber = g_MovableMan.GetSimUpdateFrameNumber(); - RunScriptedFunctionInAppropriateScripts("OnCollideWithTerrain", false, false, {}, {std::to_string(m_TerrainMatHit)}); + static const std::string functionName = "OnCollideWithTerrain"; + auto function = m_FunctionsAndScripts.find(functionName); + if (function != m_FunctionsAndScripts.end() && !function->second.empty()) { + RunScriptedFunctionInAppropriateScripts(functionName, false, false, {}, {std::to_string(m_TerrainMatHit)}); + } } Vector MovableObject::GetTotalForce() { diff --git a/Tools/Benchmarks/CaptureCollisionGame.py b/Tools/Benchmarks/CaptureCollisionGame.py new file mode 100644 index 0000000000..28a178e2bb --- /dev/null +++ b/Tools/Benchmarks/CaptureCollisionGame.py @@ -0,0 +1,49 @@ +from pathlib import Path +import json, shlex, subprocess, sys +repository = Path(__file__).resolve().parents[2] +build = Path(sys.argv[1]).resolve() +artifacts = Path(sys.argv[2]).resolve() +artifacts.mkdir(parents=True, exist_ok=True) +source = (repository/'Source/Main.cpp').read_text() +source = source.replace('void RunGameLoop() {', '''void RunGameLoop() { + struct CaptureSample { long long simulation; long long movable; int particles; }; + std::vector captureSamples; + captureSamples.reserve(600); +''') +source = source.replace('g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::SimTotal);', '''g_RandomGenerator.Seed(731 + captureSamples.size()); + const long long captureStart = g_TimerMan.GetAbsoluteTime(); + g_PerformanceMan.StartPerformanceMeasurement(PerformanceMan::SimTotal);''') +source = source.replace('g_MovableMan.Update();', '''const long long captureMovableStart = g_TimerMan.GetAbsoluteTime(); + g_MovableMan.Update(); + const long long captureMovableEnd = g_TimerMan.GetAbsoluteTime();''') +source = source.replace('g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::SimTotal);', '''g_PerformanceMan.StopPerformanceMeasurement(PerformanceMan::SimTotal); + captureSamples.push_back({g_TimerMan.GetAbsoluteTime() - captureStart, captureMovableEnd - captureMovableStart, g_MovableMan.GetParticleCount()});''') +source = source.replace('g_PerformanceMan.UpdateMSPF(updateTotalTime, drawTotalTime);', '''g_PerformanceMan.UpdateMSPF(updateTotalTime, drawTotalTime); + if (captureSamples.size() >= 240) { + for (size_t sample = 0; sample < captureSamples.size(); ++sample) { + const auto& value = captureSamples[sample]; + std::printf("PERF_CAPTURE,%zu,%lld,%lld,%d\\n", sample, value.simulation, value.movable, value.particles); + } + std::printf("PERF_CAPTURE_DONE\\n"); + std::fflush(stdout); + System::SetQuit(true); + }''') +(artifacts/'Main-dense.cpp').write_text(source) +commands=json.loads((build/'compile_commands.json').read_text()) +command=shlex.split(next(c['command'] for c in commands if c['file'].endswith('/Main.cpp'))) +command=command[:command.index('-MD')] +command=[arg for arg in command if arg not in {'-ICortexCommand.p', '-fpch-preprocess'}] +command=['-I'+str(repository/arg[5:]) if arg.startswith('-I../Source') else arg for arg in command] +command[command.index('-include')+1]=str(repository/'Source/System/StandardIncludes.h') +command+=['-c',str(artifacts/'Main-dense.cpp'),'-o',str(artifacts/'Main-dense.o')] +subprocess.run(command,cwd=build,check=True) +link=shlex.split(subprocess.check_output(['ninja','-C',str(build),'-t','commands','CortexCommand'],text=True).splitlines()[-1]) +link[link.index('CortexCommand.p/Source_Main.cpp.o')]=str(artifacts/'Main-dense.o') +for variant in ['baseline','changed']: + command=link.copy() + command[command.index('-o')+1]=str(artifacts/('game-'+variant)) + command[command.index('CortexCommand.p/Source_Entities_MovableObject.cpp.o')]=str(artifacts/('MovableObject-'+variant+'.o')) + for argument in sys.argv[3:]: + name,path=argument.split('=',1) + command[command.index('CortexCommand.p/'+name)]=str(Path(path).resolve()) + subprocess.run(command,cwd=build,check=True) diff --git a/Tools/Benchmarks/CollisionCallbacks-game-results.csv b/Tools/Benchmarks/CollisionCallbacks-game-results.csv new file mode 100644 index 0000000000..5e8f5de201 --- /dev/null +++ b/Tools/Benchmarks/CollisionCallbacks-game-results.csv @@ -0,0 +1,1441 @@ +run,variant,tick,simulation_us,movable_us,particles +1,baseline,0,6554,642,508 +1,baseline,1,16217,16132,487 +1,baseline,2,12107,12054,455 +1,baseline,3,10238,10187,412 +1,baseline,4,7323,7265,359 +1,baseline,5,5357,5294,314 +1,baseline,6,4217,4158,274 +1,baseline,7,2997,2934,248 +1,baseline,8,2624,2567,242 +1,baseline,9,1917,1860,256 +1,baseline,10,1737,1674,271 +1,baseline,11,1655,1595,361 +1,baseline,12,1504,1439,372 +1,baseline,13,1400,1323,383 +1,baseline,14,1325,1251,391 +1,baseline,15,1266,1155,390 +1,baseline,16,1234,1180,390 +1,baseline,17,1212,1159,393 +1,baseline,18,1195,1124,413 +1,baseline,19,1151,1095,419 +1,baseline,20,1140,1091,429 +1,baseline,21,1115,1050,425 +1,baseline,22,1163,1079,432 +1,baseline,23,1081,998,451 +1,baseline,24,1118,1038,448 +1,baseline,25,1063,997,449 +1,baseline,26,1097,1036,443 +1,baseline,27,1067,995,433 +1,baseline,28,1075,1016,434 +1,baseline,29,1024,964,424 +1,baseline,30,1118,1051,416 +1,baseline,31,1014,954,409 +1,baseline,32,972,919,401 +1,baseline,33,975,910,392 +1,baseline,34,1090,1000,385 +1,baseline,35,1108,965,374 +1,baseline,36,1107,1048,374 +1,baseline,37,1018,941,367 +1,baseline,38,1002,936,362 +1,baseline,39,959,904,352 +1,baseline,40,1067,994,342 +1,baseline,41,1780,1207,309 +1,baseline,42,1043,971,283 +1,baseline,43,993,925,274 +1,baseline,44,898,838,267 +1,baseline,45,1079,1019,267 +1,baseline,46,1003,938,259 +1,baseline,47,1040,977,251 +1,baseline,48,1022,907,240 +1,baseline,49,935,877,235 +1,baseline,50,941,880,237 +1,baseline,51,1011,949,245 +1,baseline,52,928,872,239 +1,baseline,53,1110,1045,229 +1,baseline,54,973,903,221 +1,baseline,55,1141,921,213 +1,baseline,56,1015,949,203 +1,baseline,57,1046,975,200 +1,baseline,58,1000,918,193 +1,baseline,59,948,832,190 +1,baseline,60,7364,1050,696 +1,baseline,61,17772,17683,666 +1,baseline,62,12818,12605,630 +1,baseline,63,10914,10853,580 +1,baseline,64,8722,8656,528 +1,baseline,65,6259,6175,506 +1,baseline,66,5075,4993,489 +1,baseline,67,4177,4089,459 +1,baseline,68,3433,3340,420 +1,baseline,69,2677,2475,397 +1,baseline,70,2411,2318,400 +1,baseline,71,2279,2219,454 +1,baseline,72,2177,2122,468 +1,baseline,73,2013,1947,473 +1,baseline,74,1884,1778,475 +1,baseline,75,1704,1644,486 +1,baseline,76,1630,1505,480 +1,baseline,77,1555,1500,473 +1,baseline,78,1473,1421,470 +1,baseline,79,1580,1524,483 +1,baseline,80,1709,1644,481 +1,baseline,81,1460,1355,490 +1,baseline,82,1534,1473,532 +1,baseline,83,1679,1518,566 +1,baseline,84,1569,1512,587 +1,baseline,85,1454,1385,587 +1,baseline,86,1444,1346,583 +1,baseline,87,1291,1237,572 +1,baseline,88,1355,1308,568 +1,baseline,89,1308,1236,565 +1,baseline,90,1487,1267,554 +1,baseline,91,1440,1361,547 +1,baseline,92,1408,1342,547 +1,baseline,93,1411,1361,533 +1,baseline,94,1378,1309,530 +1,baseline,95,1360,1296,564 +1,baseline,96,1344,1281,582 +1,baseline,97,1399,1225,561 +1,baseline,98,1389,1310,545 +1,baseline,99,1223,1148,537 +1,baseline,100,1258,1202,539 +1,baseline,101,1334,1265,534 +1,baseline,102,1447,1385,533 +1,baseline,103,1371,1306,524 +1,baseline,104,1781,1446,505 +1,baseline,105,1290,1223,488 +1,baseline,106,1357,1297,483 +1,baseline,107,1411,1329,482 +1,baseline,108,1390,1334,472 +1,baseline,109,1318,1249,447 +1,baseline,110,1319,1240,434 +1,baseline,111,1710,1074,420 +1,baseline,112,1194,1093,406 +1,baseline,113,1213,1132,386 +1,baseline,114,1161,1092,369 +1,baseline,115,1168,1098,358 +1,baseline,116,1108,1047,347 +1,baseline,117,1115,946,342 +1,baseline,118,1101,1008,333 +1,baseline,119,1181,1059,332 +1,baseline,120,7473,1232,840 +1,baseline,121,17940,17865,811 +1,baseline,122,12827,12758,772 +1,baseline,123,10439,10242,720 +1,baseline,124,7921,7852,655 +1,baseline,125,6283,6231,614 +1,baseline,126,5316,5264,581 +1,baseline,127,4234,4174,579 +1,baseline,128,3330,3277,529 +1,baseline,129,2860,2688,514 +1,baseline,130,2684,2580,514 +1,baseline,131,2395,2344,509 +1,baseline,132,2209,2165,507 +1,baseline,133,2162,2102,504 +1,baseline,134,2024,1964,497 +1,baseline,135,2159,1956,511 +1,baseline,136,2052,2005,537 +1,baseline,137,2004,1956,548 +1,baseline,138,1985,1931,552 +1,baseline,139,2146,2036,565 +1,baseline,140,1979,1910,560 +1,baseline,141,1921,1870,552 +1,baseline,142,1952,1712,557 +1,baseline,143,1852,1778,572 +1,baseline,144,1782,1710,582 +1,baseline,145,1837,1785,586 +1,baseline,146,1895,1828,588 +1,baseline,147,1811,1748,579 +1,baseline,148,1900,1709,590 +1,baseline,149,1801,1717,580 +1,baseline,150,2068,1992,571 +1,baseline,151,1957,1894,565 +1,baseline,152,1792,1728,570 +1,baseline,153,1721,1675,572 +1,baseline,154,1604,1543,561 +1,baseline,155,1603,1437,566 +1,baseline,156,1513,1467,565 +1,baseline,157,1501,1438,550 +1,baseline,158,1603,1554,554 +1,baseline,159,1425,1377,551 +1,baseline,160,1489,1422,540 +1,baseline,161,1559,1385,538 +1,baseline,162,1389,1328,539 +1,baseline,163,1480,1432,532 +1,baseline,164,1497,1433,524 +1,baseline,165,1403,1341,513 +1,baseline,166,1495,1432,492 +1,baseline,167,1529,1482,478 +1,baseline,168,1447,1331,478 +1,baseline,169,1364,1313,462 +1,baseline,170,1349,1295,449 +1,baseline,171,1322,1262,447 +1,baseline,172,1323,1275,446 +1,baseline,173,1334,1252,434 +1,baseline,174,1538,1366,424 +1,baseline,175,1332,1268,435 +1,baseline,176,1330,1272,430 +1,baseline,177,1299,1239,444 +1,baseline,178,1218,1172,440 +1,baseline,179,1469,1351,438 +1,baseline,180,5965,1310,883 +1,baseline,181,17686,17467,860 +1,baseline,182,12229,12162,804 +1,baseline,183,9745,9697,742 +1,baseline,184,8386,7918,696 +1,baseline,185,6736,6663,656 +1,baseline,186,5028,4976,625 +1,baseline,187,3679,3552,584 +1,baseline,188,2965,2899,553 +1,baseline,189,2944,2890,526 +1,baseline,190,2547,2479,510 +1,baseline,191,2362,2276,501 +1,baseline,192,2241,2187,488 +1,baseline,193,2108,2033,498 +1,baseline,194,2184,1925,504 +1,baseline,195,1975,1917,496 +1,baseline,196,1798,1751,480 +1,baseline,197,1797,1742,469 +1,baseline,198,1913,1857,477 +1,baseline,199,1776,1708,469 +1,baseline,200,1943,1693,461 +1,baseline,201,1582,1512,454 +1,baseline,202,1640,1579,447 +1,baseline,203,1536,1453,440 +1,baseline,204,1578,1503,431 +1,baseline,205,1623,1559,423 +1,baseline,206,1624,1562,404 +1,baseline,207,1586,1406,401 +1,baseline,208,1438,1388,419 +1,baseline,209,1428,1338,420 +1,baseline,210,1556,1483,416 +1,baseline,211,1663,1585,406 +1,baseline,212,1570,1507,410 +1,baseline,213,1773,1495,411 +1,baseline,214,1643,1578,406 +1,baseline,215,1651,1591,401 +1,baseline,216,1620,1549,389 +1,baseline,217,1608,1542,379 +1,baseline,218,1560,1493,377 +1,baseline,219,1506,1387,374 +1,baseline,220,1683,1489,374 +1,baseline,221,1442,1362,366 +1,baseline,222,1538,1458,363 +1,baseline,223,1488,1420,357 +1,baseline,224,1558,1483,355 +1,baseline,225,1687,1615,357 +1,baseline,226,1565,1492,363 +1,baseline,227,1544,1365,367 +1,baseline,228,1452,1373,355 +1,baseline,229,1467,1402,350 +1,baseline,230,1473,1393,345 +1,baseline,231,1475,1394,345 +1,baseline,232,1479,1391,340 +1,baseline,233,1638,1360,339 +1,baseline,234,1344,1267,331 +1,baseline,235,1363,1292,331 +1,baseline,236,1553,1456,325 +1,baseline,237,1521,1449,314 +1,baseline,238,1488,1393,294 +1,baseline,239,1536,1414,290 +1,changed,0,6734,601,508 +1,changed,1,16004,15938,487 +1,changed,2,11350,11300,455 +1,changed,3,10349,10298,412 +1,changed,4,7512,7439,359 +1,changed,5,5279,5215,314 +1,changed,6,4132,4081,274 +1,changed,7,3137,3076,248 +1,changed,8,2699,2633,242 +1,changed,9,2005,1937,256 +1,changed,10,1765,1709,271 +1,changed,11,1646,1582,361 +1,changed,12,1566,1514,372 +1,changed,13,1391,1325,383 +1,changed,14,1486,1420,391 +1,changed,15,1436,1316,390 +1,changed,16,1284,1240,390 +1,changed,17,1212,1159,393 +1,changed,18,1190,1137,413 +1,changed,19,1174,1113,419 +1,changed,20,1169,1110,429 +1,changed,21,1062,1002,425 +1,changed,22,1197,1109,432 +1,changed,23,1131,1070,451 +1,changed,24,1056,972,448 +1,changed,25,1080,1015,449 +1,changed,26,1071,997,443 +1,changed,27,990,936,433 +1,changed,28,1077,1004,434 +1,changed,29,1176,1106,424 +1,changed,30,1191,1110,416 +1,changed,31,1149,1067,409 +1,changed,32,1129,1051,401 +1,changed,33,1148,1064,392 +1,changed,34,1014,944,385 +1,changed,35,1068,920,374 +1,changed,36,1150,1094,374 +1,changed,37,1037,985,367 +1,changed,38,953,874,362 +1,changed,39,1069,1012,352 +1,changed,40,1005,946,342 +1,changed,41,1037,912,309 +1,changed,42,1917,1823,283 +1,changed,43,1062,987,274 +1,changed,44,970,911,267 +1,changed,45,934,854,267 +1,changed,46,1075,1005,259 +1,changed,47,965,907,251 +1,changed,48,1095,932,240 +1,changed,49,1012,932,235 +1,changed,50,1032,970,237 +1,changed,51,984,923,245 +1,changed,52,817,760,239 +1,changed,53,993,929,229 +1,changed,54,1098,926,221 +1,changed,55,992,926,213 +1,changed,56,1033,954,203 +1,changed,57,1016,953,200 +1,changed,58,1034,967,193 +1,changed,59,954,848,190 +1,changed,60,7389,1114,696 +1,changed,61,17710,17517,666 +1,changed,62,13107,13037,630 +1,changed,63,11180,11104,580 +1,changed,64,9078,9005,528 +1,changed,65,6304,6227,506 +1,changed,66,5203,5139,489 +1,changed,67,4424,4227,459 +1,changed,68,3582,3489,420 +1,changed,69,2728,2663,397 +1,changed,70,2406,2301,400 +1,changed,71,2124,2072,454 +1,changed,72,2032,1973,468 +1,changed,73,1937,1814,473 +1,changed,74,1839,1785,475 +1,changed,75,1613,1542,486 +1,changed,76,1550,1500,480 +1,changed,77,1516,1425,473 +1,changed,78,1560,1490,470 +1,changed,79,1564,1494,483 +1,changed,80,1805,1599,481 +1,changed,81,1464,1401,490 +1,changed,82,1466,1411,532 +1,changed,83,1498,1449,566 +1,changed,84,1406,1336,587 +1,changed,85,1402,1324,587 +1,changed,86,1487,1299,583 +1,changed,87,1317,1257,572 +1,changed,88,1483,1426,568 +1,changed,89,1388,1305,565 +1,changed,90,1327,1238,554 +1,changed,91,1369,1286,547 +1,changed,92,1285,1193,547 +1,changed,93,1378,1191,533 +1,changed,94,1264,1157,530 +1,changed,95,1333,1234,564 +1,changed,96,1279,1220,582 +1,changed,97,1270,1207,561 +1,changed,98,1269,1209,545 +1,changed,99,1310,1224,537 +1,changed,100,1367,1165,539 +1,changed,101,1218,1167,534 +1,changed,102,1236,1163,533 +1,changed,103,1274,1217,524 +1,changed,104,1167,1105,505 +1,changed,105,1184,1136,488 +1,changed,106,1384,1210,483 +1,changed,107,1139,1066,482 +1,changed,108,1198,1141,472 +1,changed,109,1157,1101,447 +1,changed,110,1150,1084,434 +1,changed,111,1603,1104,420 +1,changed,112,1125,1065,406 +1,changed,113,1225,1010,386 +1,changed,114,1128,1017,369 +1,changed,115,1047,969,358 +1,changed,116,1086,1014,347 +1,changed,117,1011,933,342 +1,changed,118,1065,982,333 +1,changed,119,1212,933,332 +1,changed,120,7169,1080,840 +1,changed,121,16960,16903,811 +1,changed,122,12335,12296,772 +1,changed,123,10216,10179,720 +1,changed,124,7717,7629,655 +1,changed,125,6051,5858,614 +1,changed,126,4867,4826,581 +1,changed,127,3841,3795,579 +1,changed,128,3117,3073,529 +1,changed,129,2655,2607,514 +1,changed,130,2567,2517,514 +1,changed,131,2444,2263,509 +1,changed,132,2301,2233,507 +1,changed,133,2002,1961,504 +1,changed,134,1989,1932,497 +1,changed,135,1968,1915,511 +1,changed,136,1946,1854,537 +1,changed,137,1942,1847,548 +1,changed,138,1910,1799,552 +1,changed,139,1843,1796,565 +1,changed,140,2128,2070,560 +1,changed,141,1912,1850,552 +1,changed,142,1818,1764,557 +1,changed,143,1868,1789,572 +1,changed,144,1682,1631,582 +1,changed,145,1878,1688,586 +1,changed,146,1688,1592,588 +1,changed,147,1717,1608,579 +1,changed,148,1607,1518,590 +1,changed,149,1684,1583,580 +1,changed,150,1591,1488,571 +1,changed,151,1575,1521,565 +1,changed,152,1686,1466,570 +1,changed,153,1590,1507,572 +1,changed,154,1524,1463,561 +1,changed,155,1478,1429,566 +1,changed,156,1556,1455,565 +1,changed,157,1556,1460,550 +1,changed,158,1610,1408,554 +1,changed,159,1491,1391,551 +1,changed,160,1474,1422,540 +1,changed,161,1450,1364,538 +1,changed,162,1431,1386,539 +1,changed,163,1468,1383,532 +1,changed,164,1426,1324,524 +1,changed,165,1548,1331,513 +1,changed,166,1497,1393,492 +1,changed,167,1312,1266,478 +1,changed,168,1349,1275,478 +1,changed,169,1391,1346,462 +1,changed,170,1328,1240,449 +1,changed,171,1492,1284,447 +1,changed,172,1385,1303,446 +1,changed,173,1353,1259,434 +1,changed,174,1216,1171,424 +1,changed,175,1364,1260,435 +1,changed,176,1352,1304,430 +1,changed,177,1286,1184,444 +1,changed,178,1497,1242,440 +1,changed,179,1242,1102,438 +1,changed,180,6885,1359,883 +1,changed,181,16601,16549,860 +1,changed,182,11893,11857,804 +1,changed,183,9777,9738,742 +1,changed,184,8080,7530,696 +1,changed,185,5966,5920,656 +1,changed,186,4577,4535,625 +1,changed,187,3597,3543,584 +1,changed,188,2946,2883,553 +1,changed,189,2621,2562,526 +1,changed,190,2490,2373,510 +1,changed,191,2253,2214,501 +1,changed,192,2215,2143,488 +1,changed,193,1996,1923,498 +1,changed,194,1900,1846,504 +1,changed,195,1956,1861,496 +1,changed,196,1910,1842,480 +1,changed,197,1926,1701,469 +1,changed,198,1774,1694,477 +1,changed,199,1799,1752,469 +1,changed,200,1688,1620,461 +1,changed,201,1582,1528,454 +1,changed,202,1511,1465,447 +1,changed,203,1573,1529,440 +1,changed,204,1613,1410,431 +1,changed,205,1510,1420,423 +1,changed,206,1628,1526,404 +1,changed,207,1524,1478,401 +1,changed,208,1565,1486,419 +1,changed,209,1418,1330,420 +1,changed,210,1594,1437,416 +1,changed,211,1502,1441,406 +1,changed,212,1698,1626,410 +1,changed,213,1664,1596,411 +1,changed,214,1509,1423,406 +1,changed,215,1674,1608,401 +1,changed,216,1556,1499,389 +1,changed,217,1638,1411,379 +1,changed,218,1565,1493,377 +1,changed,219,1422,1313,374 +1,changed,220,1488,1424,374 +1,changed,221,1268,1217,366 +1,changed,222,1359,1302,363 +1,changed,223,1427,1362,357 +1,changed,224,1506,1332,355 +1,changed,225,1428,1300,357 +1,changed,226,1376,1275,363 +1,changed,227,1343,1289,367 +1,changed,228,1334,1237,355 +1,changed,229,1401,1291,350 +1,changed,230,1633,1317,345 +1,changed,231,1319,1211,345 +1,changed,232,1470,1372,340 +1,changed,233,1303,1243,339 +1,changed,234,2000,1931,331 +1,changed,235,1394,1325,331 +1,changed,236,1809,1406,325 +1,changed,237,1524,1453,314 +1,changed,238,1352,1299,294 +1,changed,239,1313,1197,290 +2,baseline,0,6938,661,508 +2,baseline,1,16339,16268,487 +2,baseline,2,12440,12379,455 +2,baseline,3,10421,10373,412 +2,baseline,4,7659,7591,359 +2,baseline,5,5601,5535,314 +2,baseline,6,4323,4266,274 +2,baseline,7,2958,2888,248 +2,baseline,8,2833,2762,242 +2,baseline,9,2032,1968,256 +2,baseline,10,1798,1723,271 +2,baseline,11,1636,1572,361 +2,baseline,12,1466,1414,372 +2,baseline,13,1605,1558,383 +2,baseline,14,1428,1371,391 +2,baseline,15,1228,1121,390 +2,baseline,16,1262,1197,390 +2,baseline,17,1193,1074,393 +2,baseline,18,1140,1090,413 +2,baseline,19,1098,1029,419 +2,baseline,20,1126,1068,429 +2,baseline,21,1152,1046,425 +2,baseline,22,1097,1033,432 +2,baseline,23,1069,1015,451 +2,baseline,24,1265,1203,448 +2,baseline,25,1081,1013,449 +2,baseline,26,1350,1270,443 +2,baseline,27,1036,969,433 +2,baseline,28,1078,1014,434 +2,baseline,29,1057,978,424 +2,baseline,30,1007,940,416 +2,baseline,31,1036,951,409 +2,baseline,32,1064,1000,401 +2,baseline,33,946,871,392 +2,baseline,34,1124,981,385 +2,baseline,35,961,903,374 +2,baseline,36,986,925,374 +2,baseline,37,1040,960,367 +2,baseline,38,972,919,362 +2,baseline,39,1136,1028,352 +2,baseline,40,1188,949,342 +2,baseline,41,945,884,309 +2,baseline,42,1142,1049,283 +2,baseline,43,998,929,274 +2,baseline,44,962,886,267 +2,baseline,45,926,864,267 +2,baseline,46,825,769,259 +2,baseline,47,1048,931,251 +2,baseline,48,1295,1224,240 +2,baseline,49,908,836,235 +2,baseline,50,841,770,237 +2,baseline,51,921,857,245 +2,baseline,52,1058,998,239 +2,baseline,53,948,889,229 +2,baseline,54,1003,845,221 +2,baseline,55,972,917,213 +2,baseline,56,880,811,203 +2,baseline,57,899,829,200 +2,baseline,58,955,887,193 +2,baseline,59,843,747,190 +2,baseline,60,6933,903,696 +2,baseline,61,17974,17673,666 +2,baseline,62,12539,12485,630 +2,baseline,63,10749,10699,580 +2,baseline,64,8746,8670,528 +2,baseline,65,6198,6089,506 +2,baseline,66,4872,4770,489 +2,baseline,67,4474,4260,459 +2,baseline,68,3399,3308,420 +2,baseline,69,2865,2786,397 +2,baseline,70,2380,2325,400 +2,baseline,71,2106,2042,454 +2,baseline,72,1983,1926,468 +2,baseline,73,1975,1849,473 +2,baseline,74,1980,1924,475 +2,baseline,75,1687,1617,486 +2,baseline,76,1678,1616,480 +2,baseline,77,1570,1514,473 +2,baseline,78,1442,1389,470 +2,baseline,79,1486,1438,483 +2,baseline,80,1798,1615,481 +2,baseline,81,1569,1510,490 +2,baseline,82,1572,1512,532 +2,baseline,83,1653,1594,566 +2,baseline,84,1473,1417,587 +2,baseline,85,1431,1357,587 +2,baseline,86,1580,1369,583 +2,baseline,87,1396,1338,572 +2,baseline,88,1456,1398,568 +2,baseline,89,1454,1357,565 +2,baseline,90,1610,1520,554 +2,baseline,91,1395,1328,547 +2,baseline,92,1283,1193,547 +2,baseline,93,1459,1282,533 +2,baseline,94,1295,1198,530 +2,baseline,95,1326,1272,564 +2,baseline,96,1380,1326,582 +2,baseline,97,1354,1238,561 +2,baseline,98,1190,1140,545 +2,baseline,99,1410,1187,537 +2,baseline,100,1424,1349,539 +2,baseline,101,1347,1265,534 +2,baseline,102,1301,1252,533 +2,baseline,103,1265,1173,524 +2,baseline,104,1247,1165,505 +2,baseline,105,1329,1181,488 +2,baseline,106,1248,1177,483 +2,baseline,107,1343,1276,482 +2,baseline,108,1157,1080,472 +2,baseline,109,1206,1133,447 +2,baseline,110,1184,1096,434 +2,baseline,111,1649,1114,420 +2,baseline,112,1253,1051,406 +2,baseline,113,1217,1091,386 +2,baseline,114,1156,1051,369 +2,baseline,115,1197,1141,358 +2,baseline,116,1128,1036,347 +2,baseline,117,1099,1050,342 +2,baseline,118,1206,977,333 +2,baseline,119,1093,944,332 +2,baseline,120,7066,1119,840 +2,baseline,121,17905,17795,811 +2,baseline,122,12963,12911,772 +2,baseline,123,10418,10356,720 +2,baseline,124,8160,7983,655 +2,baseline,125,6023,5971,614 +2,baseline,126,5130,5083,581 +2,baseline,127,3917,3864,579 +2,baseline,128,3377,3318,529 +2,baseline,129,2721,2659,514 +2,baseline,130,2642,2575,514 +2,baseline,131,2444,2269,509 +2,baseline,132,2254,2201,507 +2,baseline,133,2089,2040,504 +2,baseline,134,1990,1919,497 +2,baseline,135,2138,2076,511 +2,baseline,136,1956,1893,537 +2,baseline,137,2096,1978,548 +2,baseline,138,1943,1857,552 +2,baseline,139,2044,1991,565 +2,baseline,140,1984,1930,560 +2,baseline,141,1887,1839,552 +2,baseline,142,1882,1806,557 +2,baseline,143,1805,1747,572 +2,baseline,144,1878,1660,582 +2,baseline,145,1765,1701,586 +2,baseline,146,1801,1730,588 +2,baseline,147,1623,1561,579 +2,baseline,148,1687,1616,590 +2,baseline,149,1651,1600,580 +2,baseline,150,1697,1644,571 +2,baseline,151,1803,1603,565 +2,baseline,152,1739,1640,570 +2,baseline,153,1602,1547,572 +2,baseline,154,1564,1488,561 +2,baseline,155,1559,1494,566 +2,baseline,156,1553,1501,565 +2,baseline,157,1629,1464,550 +2,baseline,158,1500,1453,554 +2,baseline,159,1543,1481,551 +2,baseline,160,1556,1505,540 +2,baseline,161,1493,1435,538 +2,baseline,162,1465,1412,539 +2,baseline,163,1607,1548,532 +2,baseline,164,1690,1446,524 +2,baseline,165,1540,1458,513 +2,baseline,166,1551,1452,492 +2,baseline,167,1404,1354,478 +2,baseline,168,1474,1370,478 +2,baseline,169,1483,1374,462 +2,baseline,170,1415,1359,449 +2,baseline,171,1454,1251,447 +2,baseline,172,1355,1289,446 +2,baseline,173,1446,1349,434 +2,baseline,174,1336,1239,424 +2,baseline,175,1406,1302,435 +2,baseline,176,1428,1373,430 +2,baseline,177,1415,1186,444 +2,baseline,178,1419,1342,440 +2,baseline,179,1296,1169,438 +2,baseline,180,7167,1415,883 +2,baseline,181,17032,16979,860 +2,baseline,182,12401,12359,804 +2,baseline,183,10157,9967,742 +2,baseline,184,8230,7793,696 +2,baseline,185,6091,6022,656 +2,baseline,186,4966,4904,625 +2,baseline,187,3554,3501,584 +2,baseline,188,2869,2784,553 +2,baseline,189,2759,2714,526 +2,baseline,190,2770,2565,510 +2,baseline,191,2390,2344,501 +2,baseline,192,2136,2078,488 +2,baseline,193,2027,1985,498 +2,baseline,194,1960,1870,504 +2,baseline,195,1937,1874,496 +2,baseline,196,1884,1802,480 +2,baseline,197,1955,1709,469 +2,baseline,198,1747,1704,477 +2,baseline,199,1690,1636,469 +2,baseline,200,1756,1702,461 +2,baseline,201,1622,1568,454 +2,baseline,202,1539,1484,447 +2,baseline,203,1719,1520,440 +2,baseline,204,1598,1493,431 +2,baseline,205,1586,1531,423 +2,baseline,206,1610,1522,404 +2,baseline,207,1488,1377,401 +2,baseline,208,1545,1487,419 +2,baseline,209,1432,1337,420 +2,baseline,210,1603,1483,416 +2,baseline,211,1463,1392,406 +2,baseline,212,1466,1414,410 +2,baseline,213,1351,1294,411 +2,baseline,214,1455,1395,406 +2,baseline,215,1490,1379,401 +2,baseline,216,1426,1339,389 +2,baseline,217,1444,1248,379 +2,baseline,218,1422,1375,377 +2,baseline,219,1240,1182,374 +2,baseline,220,1264,1216,374 +2,baseline,221,1352,1240,366 +2,baseline,222,1296,1225,363 +2,baseline,223,1452,1359,357 +2,baseline,224,1406,1266,355 +2,baseline,225,1273,1213,357 +2,baseline,226,1332,1273,363 +2,baseline,227,1266,1195,367 +2,baseline,228,1288,1235,355 +2,baseline,229,1336,1274,350 +2,baseline,230,1269,1219,345 +2,baseline,231,1340,1124,345 +2,baseline,232,1256,1151,340 +2,baseline,233,1215,1155,339 +2,baseline,234,1150,1091,331 +2,baseline,235,1212,1129,331 +2,baseline,236,1231,1178,325 +2,baseline,237,1244,1141,314 +2,baseline,238,1351,1090,294 +2,baseline,239,1237,1107,290 +2,changed,0,7208,608,508 +2,changed,1,15786,15709,487 +2,changed,2,12156,12070,455 +2,changed,3,10302,10251,412 +2,changed,4,7664,7616,359 +2,changed,5,5316,5260,314 +2,changed,6,4201,4128,274 +2,changed,7,2993,2944,248 +2,changed,8,2630,2567,242 +2,changed,9,1969,1900,256 +2,changed,10,1760,1688,271 +2,changed,11,1671,1595,361 +2,changed,12,1503,1428,372 +2,changed,13,1389,1327,383 +2,changed,14,1370,1311,391 +2,changed,15,1270,1155,390 +2,changed,16,1176,1130,390 +2,changed,17,1130,1073,393 +2,changed,18,1224,1153,413 +2,changed,19,1100,1054,419 +2,changed,20,1092,1037,429 +2,changed,21,1119,1057,425 +2,changed,22,1117,1053,432 +2,changed,23,1057,980,451 +2,changed,24,1016,970,448 +2,changed,25,1030,975,449 +2,changed,26,1128,1021,443 +2,changed,27,1013,950,433 +2,changed,28,1035,970,434 +2,changed,29,1021,917,424 +2,changed,30,1023,924,416 +2,changed,31,981,934,409 +2,changed,32,967,903,401 +2,changed,33,981,884,392 +2,changed,34,990,883,385 +2,changed,35,1017,886,374 +2,changed,36,1082,995,374 +2,changed,37,898,833,367 +2,changed,38,917,862,362 +2,changed,39,953,885,352 +2,changed,40,911,859,342 +2,changed,41,925,858,309 +2,changed,42,1062,955,283 +2,changed,43,914,854,274 +2,changed,44,977,875,267 +2,changed,45,866,779,267 +2,changed,46,981,887,259 +2,changed,47,937,859,251 +2,changed,48,845,772,240 +2,changed,49,815,726,235 +2,changed,50,953,887,237 +2,changed,51,906,828,245 +2,changed,52,987,922,239 +2,changed,53,989,921,229 +2,changed,54,969,904,221 +2,changed,55,813,763,213 +2,changed,56,1079,868,203 +2,changed,57,832,771,200 +2,changed,58,849,762,193 +2,changed,59,873,707,190 +2,changed,60,7018,897,696 +2,changed,61,17213,17156,666 +2,changed,62,12508,12442,630 +2,changed,63,10622,10459,580 +2,changed,64,8486,8405,528 +2,changed,65,5941,5860,506 +2,changed,66,4708,4627,489 +2,changed,67,4163,4105,459 +2,changed,68,3157,3099,420 +2,changed,69,2549,2495,397 +2,changed,70,2542,2296,400 +2,changed,71,2171,2121,454 +2,changed,72,2142,2074,468 +2,changed,73,1898,1815,473 +2,changed,74,1813,1719,475 +2,changed,75,1637,1579,486 +2,changed,76,1953,1890,480 +2,changed,77,1705,1558,473 +2,changed,78,1525,1443,470 +2,changed,79,1570,1489,483 +2,changed,80,1701,1605,481 +2,changed,81,1721,1655,490 +2,changed,82,1513,1462,532 +2,changed,83,1591,1517,566 +2,changed,84,1424,1292,587 +2,changed,85,1392,1321,587 +2,changed,86,1536,1478,583 +2,changed,87,1420,1366,572 +2,changed,88,1461,1398,568 +2,changed,89,1283,1224,565 +2,changed,90,1418,1303,554 +2,changed,91,1329,1260,547 +2,changed,92,1301,1254,547 +2,changed,93,1324,1265,533 +2,changed,94,1349,1291,530 +2,changed,95,1421,1333,564 +2,changed,96,1348,1284,582 +2,changed,97,1517,1284,561 +2,changed,98,1261,1179,545 +2,changed,99,1330,1265,537 +2,changed,100,1301,1242,539 +2,changed,101,1307,1232,534 +2,changed,102,1255,1167,533 +2,changed,103,1242,1109,524 +2,changed,104,1302,1088,505 +2,changed,105,1258,1152,488 +2,changed,106,1318,1261,483 +2,changed,107,1161,1076,482 +2,changed,108,1258,1178,472 +2,changed,109,1135,1086,447 +2,changed,110,1102,1044,434 +2,changed,111,1684,1073,420 +2,changed,112,1132,1044,406 +2,changed,113,1233,1149,386 +2,changed,114,1110,1016,369 +2,changed,115,1133,1059,358 +2,changed,116,1044,980,347 +2,changed,117,1126,1063,342 +2,changed,118,1107,956,333 +2,changed,119,1134,985,332 +2,changed,120,6925,1123,840 +2,changed,121,16941,16887,811 +2,changed,122,12388,12341,772 +2,changed,123,10403,10289,720 +2,changed,124,7893,7663,655 +2,changed,125,6002,5935,614 +2,changed,126,5067,5009,581 +2,changed,127,3974,3906,579 +2,changed,128,3211,3127,529 +2,changed,129,2764,2700,514 +2,changed,130,2794,2604,514 +2,changed,131,2362,2276,509 +2,changed,132,2311,2269,507 +2,changed,133,2205,2147,504 +2,changed,134,2070,2002,497 +2,changed,135,2036,1936,511 +2,changed,136,2100,1979,537 +2,changed,137,2153,1911,548 +2,changed,138,1897,1795,552 +2,changed,139,1915,1811,565 +2,changed,140,1981,1881,560 +2,changed,141,1881,1780,552 +2,changed,142,1821,1723,557 +2,changed,143,1827,1770,572 +2,changed,144,1970,1681,582 +2,changed,145,1711,1615,586 +2,changed,146,1805,1702,588 +2,changed,147,1753,1690,579 +2,changed,148,1641,1540,590 +2,changed,149,1710,1618,580 +2,changed,150,1716,1607,571 +2,changed,151,1803,1553,565 +2,changed,152,1540,1491,570 +2,changed,153,1676,1621,572 +2,changed,154,1597,1551,561 +2,changed,155,1526,1450,566 +2,changed,156,1516,1471,565 +2,changed,157,1920,1685,550 +2,changed,158,1523,1466,554 +2,changed,159,1465,1366,551 +2,changed,160,1472,1379,540 +2,changed,161,1447,1370,538 +2,changed,162,1465,1362,539 +2,changed,163,1467,1372,532 +2,changed,164,1493,1306,524 +2,changed,165,1453,1396,513 +2,changed,166,1422,1325,492 +2,changed,167,1390,1343,478 +2,changed,168,1404,1353,478 +2,changed,169,1343,1280,462 +2,changed,170,1449,1246,449 +2,changed,171,1317,1260,447 +2,changed,172,1389,1323,446 +2,changed,173,1357,1299,434 +2,changed,174,1289,1233,424 +2,changed,175,1340,1229,435 +2,changed,176,1376,1320,430 +2,changed,177,1410,1168,444 +2,changed,178,1332,1253,440 +2,changed,179,1311,1163,438 +2,changed,180,6894,1351,883 +2,changed,181,17064,17006,860 +2,changed,182,12332,12285,804 +2,changed,183,9940,9791,742 +2,changed,184,8300,7842,696 +2,changed,185,5897,5838,656 +2,changed,186,4602,4541,625 +2,changed,187,3672,3607,584 +2,changed,188,3097,3027,553 +2,changed,189,2717,2647,526 +2,changed,190,2663,2524,510 +2,changed,191,2445,2396,501 +2,changed,192,2216,2167,488 +2,changed,193,1993,1936,498 +2,changed,194,1931,1882,504 +2,changed,195,2055,1986,496 +2,changed,196,1927,1876,480 +2,changed,197,1941,1762,469 +2,changed,198,1757,1699,477 +2,changed,199,1662,1620,469 +2,changed,200,1678,1605,461 +2,changed,201,1706,1642,454 +2,changed,202,1557,1513,447 +2,changed,203,1688,1503,440 +2,changed,204,1594,1532,431 +2,changed,205,1455,1400,423 +2,changed,206,1494,1435,404 +2,changed,207,1487,1424,401 +2,changed,208,1602,1549,419 +2,changed,209,1608,1482,420 +2,changed,210,1454,1401,416 +2,changed,211,1439,1371,406 +2,changed,212,1460,1397,410 +2,changed,213,1399,1337,411 +2,changed,214,1473,1408,406 +2,changed,215,1422,1360,401 +2,changed,216,1516,1319,389 +2,changed,217,1297,1239,379 +2,changed,218,1314,1250,377 +2,changed,219,1293,1230,374 +2,changed,220,1338,1285,374 +2,changed,221,1313,1257,366 +2,changed,222,1317,1266,363 +2,changed,223,1395,1211,357 +2,changed,224,1370,1307,355 +2,changed,225,1303,1241,357 +2,changed,226,1272,1201,363 +2,changed,227,1271,1209,367 +2,changed,228,1385,1308,355 +2,changed,229,1340,1132,350 +2,changed,230,1251,1189,345 +2,changed,231,1265,1193,345 +2,changed,232,1182,1133,340 +2,changed,233,1213,1141,339 +2,changed,234,1193,1136,331 +2,changed,235,1162,1099,331 +2,changed,236,1327,1138,325 +2,changed,237,1222,1159,314 +2,changed,238,1228,1166,294 +2,changed,239,1166,1053,290 +3,baseline,0,7130,618,508 +3,baseline,1,16108,16030,487 +3,baseline,2,13020,12960,455 +3,baseline,3,12082,11974,412 +3,baseline,4,8446,8368,359 +3,baseline,5,5808,5727,314 +3,baseline,6,4760,4691,274 +3,baseline,7,3284,3209,248 +3,baseline,8,2939,2876,242 +3,baseline,9,2056,2000,256 +3,baseline,10,1811,1755,271 +3,baseline,11,1835,1746,361 +3,baseline,12,1491,1436,372 +3,baseline,13,1481,1419,383 +3,baseline,14,1383,1310,391 +3,baseline,15,1411,1333,390 +3,baseline,16,1306,1172,390 +3,baseline,17,1291,1226,393 +3,baseline,18,1238,1147,413 +3,baseline,19,1326,1257,419 +3,baseline,20,1227,1172,429 +3,baseline,21,1272,1185,425 +3,baseline,22,1254,1200,432 +3,baseline,23,1311,1212,451 +3,baseline,24,1214,1143,448 +3,baseline,25,1207,1139,449 +3,baseline,26,1159,1098,443 +3,baseline,27,1041,979,433 +3,baseline,28,1148,1056,434 +3,baseline,29,1119,1038,424 +3,baseline,30,1062,993,416 +3,baseline,31,1095,985,409 +3,baseline,32,999,948,401 +3,baseline,33,1072,985,392 +3,baseline,34,1053,981,385 +3,baseline,35,967,912,374 +3,baseline,36,1106,906,374 +3,baseline,37,1229,1161,367 +3,baseline,38,1014,957,362 +3,baseline,39,1047,980,352 +3,baseline,40,1102,1002,342 +3,baseline,41,963,906,309 +3,baseline,42,1084,892,283 +3,baseline,43,993,937,274 +3,baseline,44,931,879,267 +3,baseline,45,986,908,267 +3,baseline,46,917,840,259 +3,baseline,47,935,882,251 +3,baseline,48,949,891,240 +3,baseline,49,1223,1026,235 +3,baseline,50,1113,1051,237 +3,baseline,51,948,886,245 +3,baseline,52,914,850,239 +3,baseline,53,917,833,229 +3,baseline,54,914,813,221 +3,baseline,55,1069,819,213 +3,baseline,56,824,767,203 +3,baseline,57,978,918,200 +3,baseline,58,996,946,193 +3,baseline,59,911,783,190 +3,baseline,60,6925,925,696 +3,baseline,61,17450,17376,666 +3,baseline,62,12681,12520,630 +3,baseline,63,10666,10616,580 +3,baseline,64,8726,8662,528 +3,baseline,65,6095,6046,506 +3,baseline,66,4823,4771,489 +3,baseline,67,4027,3980,459 +3,baseline,68,3267,3100,420 +3,baseline,69,2597,2540,397 +3,baseline,70,2289,2222,400 +3,baseline,71,2070,2018,454 +3,baseline,72,2181,2094,468 +3,baseline,73,1907,1847,473 +3,baseline,74,1866,1736,475 +3,baseline,75,1794,1674,486 +3,baseline,76,1644,1562,480 +3,baseline,77,1580,1522,473 +3,baseline,78,1568,1507,470 +3,baseline,79,1521,1441,483 +3,baseline,80,1512,1449,481 +3,baseline,81,1550,1387,490 +3,baseline,82,1497,1437,532 +3,baseline,83,1557,1486,566 +3,baseline,84,1464,1376,587 +3,baseline,85,1401,1321,587 +3,baseline,86,1462,1369,583 +3,baseline,87,1344,1266,572 +3,baseline,88,1420,1220,568 +3,baseline,89,1458,1359,565 +3,baseline,90,1368,1263,554 +3,baseline,91,1488,1401,547 +3,baseline,92,1332,1246,547 +3,baseline,93,1403,1337,533 +3,baseline,94,1307,1209,530 +3,baseline,95,1455,1267,564 +3,baseline,96,1387,1293,582 +3,baseline,97,1273,1196,561 +3,baseline,98,1284,1221,545 +3,baseline,99,1364,1290,537 +3,baseline,100,1336,1220,539 +3,baseline,101,1425,1189,534 +3,baseline,102,1342,1233,533 +3,baseline,103,1233,1166,524 +3,baseline,104,1236,1156,505 +3,baseline,105,1253,1169,488 +3,baseline,106,1406,1332,483 +3,baseline,107,1729,1102,482 +3,baseline,108,1289,1202,472 +3,baseline,109,1188,1110,447 +3,baseline,110,1171,1085,434 +3,baseline,111,1258,1201,420 +3,baseline,112,1274,1222,406 +3,baseline,113,1279,1068,386 +3,baseline,114,1235,1162,369 +3,baseline,115,1242,1175,358 +3,baseline,116,1107,1045,347 +3,baseline,117,1104,1023,342 +3,baseline,118,1071,990,333 +3,baseline,119,1174,1048,332 +3,baseline,120,6981,1160,840 +3,baseline,121,17770,17717,811 +3,baseline,122,13019,12976,772 +3,baseline,123,10832,10778,720 +3,baseline,124,8169,8118,655 +3,baseline,125,6396,6335,614 +3,baseline,126,5380,5171,581 +3,baseline,127,4134,4078,579 +3,baseline,128,3184,3122,529 +3,baseline,129,3008,2941,514 +3,baseline,130,2630,2578,514 +3,baseline,131,2394,2338,509 +3,baseline,132,2299,2245,507 +3,baseline,133,2237,2065,504 +3,baseline,134,2044,1991,497 +3,baseline,135,2042,1982,511 +3,baseline,136,2016,1954,537 +3,baseline,137,1953,1892,548 +3,baseline,138,1970,1903,552 +3,baseline,139,2142,2023,565 +3,baseline,140,1901,1831,560 +3,baseline,141,1851,1793,552 +3,baseline,142,1833,1772,557 +3,baseline,143,1948,1885,572 +3,baseline,144,1735,1681,582 +3,baseline,145,1781,1717,586 +3,baseline,146,1955,1727,588 +3,baseline,147,1632,1571,579 +3,baseline,148,1681,1601,590 +3,baseline,149,1700,1645,580 +3,baseline,150,1660,1589,571 +3,baseline,151,1633,1569,565 +3,baseline,152,1775,1577,570 +3,baseline,153,1705,1636,572 +3,baseline,154,1612,1559,561 +3,baseline,155,1594,1536,566 +3,baseline,156,1622,1555,565 +3,baseline,157,1500,1443,550 +3,baseline,158,1640,1479,554 +3,baseline,159,1517,1453,551 +3,baseline,160,1446,1390,540 +3,baseline,161,1492,1423,538 +3,baseline,162,1433,1381,539 +3,baseline,163,1509,1446,532 +3,baseline,164,1414,1349,524 +3,baseline,165,1571,1342,513 +3,baseline,166,1403,1345,492 +3,baseline,167,1334,1272,478 +3,baseline,168,1353,1290,478 +3,baseline,169,1409,1350,462 +3,baseline,170,1390,1322,449 +3,baseline,171,1634,1425,447 +3,baseline,172,1450,1380,446 +3,baseline,173,1459,1391,434 +3,baseline,174,1385,1320,424 +3,baseline,175,1470,1415,435 +3,baseline,176,1338,1284,430 +3,baseline,177,1429,1372,444 +3,baseline,178,1710,1434,440 +3,baseline,179,1273,1151,438 +3,baseline,180,7140,1341,883 +3,baseline,181,17232,17168,860 +3,baseline,182,13002,12957,804 +3,baseline,183,10170,10119,742 +3,baseline,184,8285,8123,696 +3,baseline,185,6305,6246,656 +3,baseline,186,4751,4666,625 +3,baseline,187,3799,3727,584 +3,baseline,188,2984,2921,553 +3,baseline,189,2728,2661,526 +3,baseline,190,2768,2677,510 +3,baseline,191,2509,2367,501 +3,baseline,192,2231,2154,488 +3,baseline,193,2215,2156,498 +3,baseline,194,2097,2037,504 +3,baseline,195,2065,2009,496 +3,baseline,196,1994,1943,480 +3,baseline,197,1782,1736,469 +3,baseline,198,1991,1769,477 +3,baseline,199,1742,1694,469 +3,baseline,200,1726,1637,461 +3,baseline,201,1579,1523,454 +3,baseline,202,1708,1621,447 +3,baseline,203,1625,1524,440 +3,baseline,204,1741,1493,431 +3,baseline,205,1564,1464,423 +3,baseline,206,1563,1486,404 +3,baseline,207,1607,1494,401 +3,baseline,208,1611,1506,419 +3,baseline,209,1545,1437,420 +3,baseline,210,1626,1500,416 +3,baseline,211,1573,1457,406 +3,baseline,212,1413,1327,410 +3,baseline,213,1515,1430,411 +3,baseline,214,1435,1334,406 +3,baseline,215,1512,1408,401 +3,baseline,216,1466,1391,389 +3,baseline,217,1479,1254,379 +3,baseline,218,1401,1346,377 +3,baseline,219,1375,1278,374 +3,baseline,220,1455,1345,374 +3,baseline,221,1276,1206,366 +3,baseline,222,1369,1282,363 +3,baseline,223,1415,1275,357 +3,baseline,224,1390,1330,355 +3,baseline,225,1324,1248,357 +3,baseline,226,1343,1256,363 +3,baseline,227,1318,1238,367 +3,baseline,228,1307,1209,355 +3,baseline,229,1303,1195,350 +3,baseline,230,1381,1147,345 +3,baseline,231,1272,1220,345 +3,baseline,232,1298,1205,340 +3,baseline,233,1266,1163,339 +3,baseline,234,1215,1104,331 +3,baseline,235,1307,1254,331 +3,baseline,236,1175,1075,325 +3,baseline,237,1309,1073,314 +3,baseline,238,1369,1305,294 +3,baseline,239,1254,1094,290 +3,changed,0,6660,544,508 +3,changed,1,16264,16190,487 +3,changed,2,11940,11883,455 +3,changed,3,10371,10306,412 +3,changed,4,7776,7724,359 +3,changed,5,5609,5542,314 +3,changed,6,4360,4304,274 +3,changed,7,3219,3157,248 +3,changed,8,2752,2682,242 +3,changed,9,2036,1981,256 +3,changed,10,1778,1723,271 +3,changed,11,1677,1633,361 +3,changed,12,1561,1505,372 +3,changed,13,1439,1380,383 +3,changed,14,1350,1301,391 +3,changed,15,1420,1342,390 +3,changed,16,1440,1319,390 +3,changed,17,1276,1203,393 +3,changed,18,1268,1214,413 +3,changed,19,1322,1266,419 +3,changed,20,1228,1136,429 +3,changed,21,1136,1070,425 +3,changed,22,1153,1099,432 +3,changed,23,1159,1050,451 +3,changed,24,1058,975,448 +3,changed,25,1201,1101,449 +3,changed,26,1084,1010,443 +3,changed,27,1088,1012,433 +3,changed,28,1074,989,434 +3,changed,29,1019,926,424 +3,changed,30,1125,951,416 +3,changed,31,926,866,409 +3,changed,32,1050,991,401 +3,changed,33,1000,935,392 +3,changed,34,990,928,385 +3,changed,35,917,859,374 +3,changed,36,951,880,374 +3,changed,37,996,931,367 +3,changed,38,896,835,362 +3,changed,39,914,846,352 +3,changed,40,938,884,342 +3,changed,41,904,847,309 +3,changed,42,963,903,283 +3,changed,43,959,849,274 +3,changed,44,933,876,267 +3,changed,45,997,930,267 +3,changed,46,896,841,259 +3,changed,47,896,839,251 +3,changed,48,872,817,240 +3,changed,49,986,887,235 +3,changed,50,921,863,237 +3,changed,51,1029,966,245 +3,changed,52,929,860,239 +3,changed,53,1017,955,229 +3,changed,54,936,881,221 +3,changed,55,977,790,213 +3,changed,56,999,941,203 +3,changed,57,940,877,200 +3,changed,58,883,818,193 +3,changed,59,955,828,190 +3,changed,60,6986,900,696 +3,changed,61,17694,17640,666 +3,changed,62,12846,12633,630 +3,changed,63,10751,10682,580 +3,changed,64,8702,8617,528 +3,changed,65,6169,6098,506 +3,changed,66,4956,4864,489 +3,changed,67,4085,4011,459 +3,changed,68,3275,3221,420 +3,changed,69,2699,2494,397 +3,changed,70,2292,2224,400 +3,changed,71,2138,2049,454 +3,changed,72,1939,1890,468 +3,changed,73,1977,1912,473 +3,changed,74,1971,1913,475 +3,changed,75,1879,1758,486 +3,changed,76,1665,1594,480 +3,changed,77,1543,1492,473 +3,changed,78,1496,1429,470 +3,changed,79,1563,1473,483 +3,changed,80,1604,1542,481 +3,changed,81,1462,1391,490 +3,changed,82,1639,1490,532 +3,changed,83,1587,1509,566 +3,changed,84,1470,1412,587 +3,changed,85,1415,1362,587 +3,changed,86,1367,1319,583 +3,changed,87,1403,1354,572 +3,changed,88,1382,1321,568 +3,changed,89,1552,1310,565 +3,changed,90,1354,1297,554 +3,changed,91,1344,1258,547 +3,changed,92,1274,1207,547 +3,changed,93,1329,1226,533 +3,changed,94,1269,1219,530 +3,changed,95,1430,1258,564 +3,changed,96,1306,1253,582 +3,changed,97,1288,1207,561 +3,changed,98,1365,1284,545 +3,changed,99,1367,1299,537 +3,changed,100,1275,1207,539 +3,changed,101,1275,1223,534 +3,changed,102,1496,1243,533 +3,changed,103,1331,1269,524 +3,changed,104,1369,1259,505 +3,changed,105,1684,1207,488 +3,changed,106,1265,1207,483 +3,changed,107,1177,1123,482 +3,changed,108,1444,1222,472 +3,changed,109,1223,1141,447 +3,changed,110,1199,1125,434 +3,changed,111,1123,1062,420 +3,changed,112,1144,1095,406 +3,changed,113,1171,1081,386 +3,changed,114,1295,1109,369 +3,changed,115,1166,1067,358 +3,changed,116,1162,1072,347 +3,changed,117,1189,1122,342 +3,changed,118,1119,1057,333 +3,changed,119,1121,983,332 +3,changed,120,6913,1095,840 +3,changed,121,17538,17351,811 +3,changed,122,12882,12814,772 +3,changed,123,10096,10045,720 +3,changed,124,7793,7740,655 +3,changed,125,6113,6062,614 +3,changed,126,5066,5016,581 +3,changed,127,4148,3983,579 +3,changed,128,3305,3218,529 +3,changed,129,2938,2837,514 +3,changed,130,2900,2837,514 +3,changed,131,2514,2427,509 +3,changed,132,2607,2535,507 +3,changed,133,2352,2124,504 +3,changed,134,2041,1981,497 +3,changed,135,2080,1990,511 +3,changed,136,1974,1874,537 +3,changed,137,1964,1907,548 +3,changed,138,2072,2024,552 +3,changed,139,1977,1895,565 +3,changed,140,1972,1812,560 +3,changed,141,1966,1885,552 +3,changed,142,1806,1702,557 +3,changed,143,1829,1726,572 +3,changed,144,1828,1709,582 +3,changed,145,1874,1814,586 +3,changed,146,1922,1717,588 +3,changed,147,1609,1559,579 +3,changed,148,1753,1642,590 +3,changed,149,1675,1577,580 +3,changed,150,1705,1655,571 +3,changed,151,1567,1522,565 +3,changed,152,1643,1545,570 +3,changed,153,1674,1432,572 +3,changed,154,1570,1472,561 +3,changed,155,1557,1507,566 +3,changed,156,1585,1534,565 +3,changed,157,1468,1417,550 +3,changed,158,1583,1484,554 +3,changed,159,1544,1432,551 +3,changed,160,1579,1387,540 +3,changed,161,1385,1344,538 +3,changed,162,1400,1304,539 +3,changed,163,1398,1338,532 +3,changed,164,1395,1331,524 +3,changed,165,1418,1319,513 +3,changed,166,1443,1398,492 +3,changed,167,1479,1234,478 +3,changed,168,1338,1254,478 +3,changed,169,1557,1507,462 +3,changed,170,1448,1374,449 +3,changed,171,1329,1244,447 +3,changed,172,1333,1280,446 +3,changed,173,1357,1191,434 +3,changed,174,1284,1208,424 +3,changed,175,1385,1295,435 +3,changed,176,1283,1233,430 +3,changed,177,1304,1220,444 +3,changed,178,1674,1165,440 +3,changed,179,1304,1178,438 +3,changed,180,6818,1344,883 +3,changed,181,16948,16864,860 +3,changed,182,12456,12420,804 +3,changed,183,10103,10043,742 +3,changed,184,7729,7680,696 +3,changed,185,6099,6029,656 +3,changed,186,4702,4584,625 +3,changed,187,3623,3567,584 +3,changed,188,2860,2798,553 +3,changed,189,2605,2547,526 +3,changed,190,2702,2638,510 +3,changed,191,2462,2399,501 +3,changed,192,2240,2174,488 +3,changed,193,2284,2063,498 +3,changed,194,1978,1933,504 +3,changed,195,1891,1839,496 +3,changed,196,1848,1807,480 +3,changed,197,1774,1710,469 +3,changed,198,1897,1840,477 +3,changed,199,1610,1552,469 +3,changed,200,1802,1590,461 +3,changed,201,1551,1477,454 +3,changed,202,1544,1492,447 +3,changed,203,1576,1520,440 +3,changed,204,1565,1514,431 +3,changed,205,1458,1390,423 +3,changed,206,1636,1470,404 +3,changed,207,1481,1417,401 +3,changed,208,1598,1535,419 +3,changed,209,2059,1964,420 +3,changed,210,1674,1604,416 +3,changed,211,1721,1637,406 +3,changed,212,2021,1655,410 +3,changed,213,1653,1568,411 +3,changed,214,1523,1457,406 +3,changed,215,1410,1347,401 +3,changed,216,1476,1406,389 +3,changed,217,1433,1379,379 +3,changed,218,1408,1343,377 +3,changed,219,1379,1204,374 +3,changed,220,1459,1375,374 +3,changed,221,1325,1259,366 +3,changed,222,1392,1333,363 +3,changed,223,1301,1243,357 +3,changed,224,1381,1320,355 +3,changed,225,1346,1264,357 +3,changed,226,1510,1296,363 +3,changed,227,1299,1226,367 +3,changed,228,1354,1290,355 +3,changed,229,1470,1387,350 +3,changed,230,1352,1237,345 +3,changed,231,1261,1207,345 +3,changed,232,1326,1253,340 +3,changed,233,1394,1143,339 +3,changed,234,1325,1246,331 +3,changed,235,1230,1158,331 +3,changed,236,1293,1218,325 +3,changed,237,1176,1113,314 +3,changed,238,1282,1210,294 +3,changed,239,1261,1149,290 diff --git a/Tools/Benchmarks/CollisionCallbacks-game-summary.json b/Tools/Benchmarks/CollisionCallbacks-game-summary.json new file mode 100644 index 0000000000..59fff50830 --- /dev/null +++ b/Tools/Benchmarks/CollisionCallbacks-game-summary.json @@ -0,0 +1,84 @@ +{ + "samples_per_run": 240, + "excluded_initial_ticks": 60, + "seed": "731 + capture sample index", + "waves": "400 collidable stock gibs + actual frag grenade explosions every 60 ticks", + "pairs": [ + { + "run": 1, + "baseline": { + "simulation_mean_us": 2641.1, + "simulation_p95_us": 8722, + "movable_mean_us": 2453.6722222222224, + "movable_p95_us": 8656 + }, + "changed": { + "simulation_mean_us": 2564.472222222222, + "simulation_p95_us": 9078, + "movable_mean_us": 2369.9777777777776, + "movable_p95_us": 9005 + }, + "particles_equal": true + }, + { + "run": 2, + "baseline": { + "simulation_mean_us": 2586.538888888889, + "simulation_p95_us": 8746, + "movable_mean_us": 2394.5555555555557, + "movable_p95_us": 8670 + }, + "changed": { + "simulation_mean_us": 2547.6444444444446, + "simulation_p95_us": 8486, + "movable_mean_us": 2358.222222222222, + "movable_p95_us": 8405 + }, + "particles_equal": true + }, + { + "run": 3, + "baseline": { + "simulation_mean_us": 2601.8444444444444, + "simulation_p95_us": 8726, + "movable_mean_us": 2411.7944444444443, + "movable_p95_us": 8662 + }, + "changed": { + "simulation_mean_us": 2582.7277777777776, + "simulation_p95_us": 8702, + "movable_mean_us": 2392.8611111111113, + "movable_p95_us": 8617 + }, + "particles_equal": true + } + ], + "aggregate": { + "baseline": { + "simulation_mean_us": 2609.8277777777776, + "simulation_p95_us": 8746, + "movable_mean_us": 2420.0074074074073, + "movable_p95_us": 8670 + }, + "changed": { + "simulation_mean_us": 2564.948148148148, + "simulation_p95_us": 9078, + "movable_mean_us": 2373.687037037037, + "movable_p95_us": 9005 + } + }, + "lua_callbacks": { + "baseline": [ + "LUA_CHECK_START", + "LUA_CHECK_ENABLED mo=77134 terrain=56712 creates=32", + "LUA_CHECK_DISABLED_PASS", + "LUA_CHECK_PASS mo=152236 terrain=109160 creates=32" + ], + "changed": [ + "LUA_CHECK_START", + "LUA_CHECK_ENABLED mo=77134 terrain=56712 creates=32", + "LUA_CHECK_DISABLED_PASS", + "LUA_CHECK_PASS mo=152236 terrain=109160 creates=32" + ] + } +} diff --git a/Tools/Benchmarks/CollisionCallbacks-results.json b/Tools/Benchmarks/CollisionCallbacks-results.json new file mode 100644 index 0000000000..eb3aea1e78 --- /dev/null +++ b/Tools/Benchmarks/CollisionCallbacks-results.json @@ -0,0 +1,674 @@ +{ + "baseline_ref": "41e6b7010817936e09d2929330b7519852a1fcca", + "calls_per_sample": 4096, + "runs": 3, + "modes": { + "dense-1": { + "baseline": { + "behavior_hashes": [ + 1015060394229153914, + 1015060394229153914, + 1015060394229153914 + ], + "responses": [ + 89956, + 89956, + 89956 + ], + "allocations": [ + 3190671, + 3190671, + 3190671 + ], + "bytes": [ + 120301472, + 120301472, + 120301472 + ], + "ms": [ + 447.497, + 446.235, + 448.35, + 449.061, + 448.854, + 449.461, + 449.362, + 448.774, + 448.436, + 449.07, + 451.546, + 453.324, + 450.558, + 463.621, + 449.201, + 450.902, + 450.757, + 467.158, + 448.547, + 451.245, + 452.049 + ] + }, + "changed": { + "behavior_hashes": [ + 1015060394229153914, + 1015060394229153914, + 1015060394229153914 + ], + "responses": [ + 89956, + 89956, + 89956 + ], + "allocations": [ + 309291, + 309291, + 309291 + ], + "bytes": [ + 74199392, + 74199392, + 74199392 + ], + "ms": [ + 392.087, + 392.121, + 394.902, + 399.071, + 400.116, + 391.631, + 391.788, + 390.463, + 401.463, + 388.651, + 388.538, + 391.346, + 390.874, + 392.405, + 391.131, + 395.341, + 402.408, + 397.021, + 408.959, + 402.276, + 395.864 + ] + }, + "baseline_median_ms": 449.362, + "changed_median_ms": 392.405, + "time_reduction_percent": 12.675081560078528 + }, + "dense-8": { + "baseline": { + "behavior_hashes": [ + 10678557251391008333, + 10678557251391008333, + 10678557251391008333 + ], + "responses": [ + 87690, + 87690, + 87690 + ], + "allocations": [ + 3765537, + 3765537, + 3765537 + ], + "bytes": [ + 112031808, + 112031808, + 112031808 + ], + "ms": [ + 567.417, + 581.624, + 569.528, + 568.91, + 571.32, + 568.585, + 567.605, + 568.296, + 569.229, + 572.319, + 570.493, + 569.199, + 568.692, + 580.558, + 576.332, + 568.775, + 572.084, + 589.427, + 570.135, + 570.642, + 570.621 + ] + }, + "changed": { + "behavior_hashes": [ + 10678557251391008333, + 10678557251391008333, + 10678557251391008333 + ], + "responses": [ + 87690, + 87690, + 87690 + ], + "allocations": [ + 971963, + 971963, + 971963 + ], + "bytes": [ + 67334624, + 67334624, + 67334624 + ], + "ms": [ + 517.821, + 513.658, + 514.637, + 512.494, + 512.828, + 513.3, + 513.978, + 517.383, + 518.366, + 518.94, + 518.01, + 528.781, + 518.303, + 516.892, + 525.315, + 519.82, + 524.762, + 512.832, + 546.846, + 515.815, + 536.198 + ] + }, + "baseline_median_ms": 570.135, + "changed_median_ms": 517.821, + "time_reduction_percent": 9.17572153963534 + }, + "dense-32": { + "baseline": { + "behavior_hashes": [ + 6501605824948049464, + 6501605824948049464, + 6501605824948049464 + ], + "responses": [ + 84035, + 84035, + 84035 + ], + "allocations": [ + 4719757, + 4719757, + 4719757 + ], + "bytes": [ + 86475680, + 86475680, + 86475680 + ], + "ms": [ + 855.329, + 840.286, + 836.947, + 835.584, + 839.985, + 852.704, + 839.677, + 833.841, + 839.712, + 850.79, + 836.581, + 839.276, + 839.029, + 832.138, + 834.341, + 836.198, + 853.546, + 843.917, + 839.132, + 838.003, + 838.655 + ] + }, + "changed": { + "behavior_hashes": [ + 6501605824948049464, + 6501605824948049464, + 6501605824948049464 + ], + "responses": [ + 84035, + 84035, + 84035 + ], + "allocations": [ + 2028381, + 2028381, + 2028381 + ], + "bytes": [ + 43413664, + 43413664, + 43413664 + ], + "ms": [ + 779.985, + 780.796, + 777.219, + 780.675, + 782.868, + 793.508, + 782.867, + 785.561, + 783.126, + 799.869, + 784.236, + 783.144, + 784.145, + 781.947, + 784.856, + 786.438, + 783.234, + 794.957, + 783.827, + 786.091, + 781.165 + ] + }, + "baseline_median_ms": 839.132, + "changed_median_ms": 783.234, + "time_reduction_percent": 6.661407263696284 + }, + "terrain": { + "baseline": { + "behavior_hashes": [ + 15405424348098678617, + 15405424348098678617, + 15405424348098678617 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 2406712, + 2406712, + 2406712 + ], + "bytes": [ + 44524172, + 44524172, + 44524172 + ], + "ms": [ + 135.477, + 135.481, + 135.641, + 135.572, + 135.444, + 135.502, + 135.496, + 135.889, + 148.369, + 136.545, + 135.817, + 135.471, + 135.315, + 135.801, + 136.876, + 136.858, + 135.579, + 135.726, + 150.523, + 138.244, + 136.817 + ] + }, + "changed": { + "behavior_hashes": [ + 15405424348098678617, + 15405424348098678617, + 15405424348098678617 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 92.089, + 92.475, + 92.124, + 92.759, + 92.616, + 92.127, + 92.727, + 92.416, + 92.702, + 92.541, + 92.704, + 92.73, + 92.674, + 92.316, + 91.928, + 91.988, + 92.532, + 93.189, + 92.106, + 92.412, + 92.664 + ] + }, + "baseline_median_ms": 135.726, + "changed_median_ms": 92.532, + "time_reduction_percent": 31.824410945581537 + }, + "air": { + "baseline": { + "behavior_hashes": [ + 16994007857569456424, + 16994007857569456424, + 16994007857569456424 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 58.863, + 58.827, + 58.855, + 58.654, + 58.819, + 58.772, + 58.714, + 58.762, + 59.058, + 58.466, + 58.85, + 58.543, + 58.803, + 58.517, + 59.216, + 59.063, + 59.045, + 58.91, + 58.66, + 58.809, + 58.629 + ] + }, + "changed": { + "behavior_hashes": [ + 16994007857569456424, + 16994007857569456424, + 16994007857569456424 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 60.482, + 59.634, + 60.004, + 59.884, + 59.868, + 59.955, + 60.199, + 59.958, + 59.898, + 59.86, + 59.826, + 59.791, + 59.585, + 59.509, + 59.477, + 59.666, + 59.922, + 59.704, + 60.021, + 59.615, + 60.108 + ] + }, + "baseline_median_ms": 58.809, + "changed_median_ms": 59.868, + "time_reduction_percent": -1.8007447839616386 + }, + "push-terrain": { + "baseline": { + "behavior_hashes": [ + 18279129362497917356, + 18279129362497917356, + 18279129362497917356 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 36864, + 36864, + 36864 + ], + "bytes": [ + 18579456, + 18579456, + 18579456 + ], + "ms": [ + 30.678, + 30.933, + 30.941, + 31.257, + 30.99, + 31.062, + 30.96, + 31.198, + 31.162, + 31.051, + 31.269, + 31.314, + 32.112, + 31.478, + 31.11, + 31.117, + 31.056, + 31.423, + 31.971, + 31.846, + 31.623 + ] + }, + "changed": { + "behavior_hashes": [ + 18279129362497917356, + 18279129362497917356, + 18279129362497917356 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 36864, + 36864, + 36864 + ], + "bytes": [ + 18579456, + 18579456, + 18579456 + ], + "ms": [ + 32.614, + 32.542, + 32.578, + 32.694, + 32.879, + 32.684, + 32.655, + 32.629, + 32.613, + 32.722, + 32.584, + 32.593, + 32.568, + 43.228, + 35.713, + 33.098, + 33.434, + 32.718, + 32.358, + 32.256, + 32.28 + ] + }, + "baseline_median_ms": 31.162, + "changed_median_ms": 32.629, + "time_reduction_percent": -4.707656761440204 + }, + "push-air": { + "baseline": { + "behavior_hashes": [ + 12725381982755921557, + 12725381982755921557, + 12725381982755921557 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 18.042, + 18.0, + 17.948, + 18.051, + 17.964, + 18.123, + 18.059, + 18.041, + 17.963, + 18.04, + 17.99, + 18.054, + 18.049, + 17.982, + 18.645, + 18.441, + 18.113, + 18.236, + 18.173, + 18.297, + 18.648 + ] + }, + "changed": { + "behavior_hashes": [ + 12725381982755921557, + 12725381982755921557, + 12725381982755921557 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 18.954, + 18.889, + 19.044, + 19.02, + 18.939, + 18.923, + 18.853, + 19.522, + 19.055, + 18.981, + 18.951, + 18.961, + 18.918, + 19.017, + 19.062, + 18.877, + 18.904, + 18.847, + 18.938, + 19.006, + 18.913 + ] + }, + "baseline_median_ms": 18.051, + "changed_median_ms": 18.951, + "time_reduction_percent": -4.985873358816706 + } + }, + "platform": "Apple M3 Pro, macOS, GCC 15.2, C++20, release -O3", + "modified_engine_source": "Source/Entities/MovableObject.cpp" +} diff --git a/Tools/Benchmarks/CollisionCallbacks.cpp b/Tools/Benchmarks/CollisionCallbacks.cpp new file mode 100644 index 0000000000..9cb2a734ad --- /dev/null +++ b/Tools/Benchmarks/CollisionCallbacks.cpp @@ -0,0 +1,248 @@ +#include "StandardIncludes.h" +#include "SLTerrain.h" +#include "SettingsMan.h" +#include "LuaMan.h" +#include "ThreadMan.h" +#include "AtomGroup.h" +#include "Material.h" +#include +#include + +using namespace RTE; + +// Count actual C++ heap requests, including aligned allocations. +thread_local bool countAllocations = false; +thread_local uint64_t allocations = 0; +thread_local uint64_t allocationBytes = 0; +void recordAllocation(size_t size) { + if (countAllocations) { ++allocations; allocationBytes += size; } +} +void* operator new(size_t size) { recordAllocation(size); return std::malloc(size); } +void* operator new[](size_t size) { return ::operator new(size); } +void* operator new(size_t size, std::align_val_t alignment) { + recordAllocation(size); + void* result; + posix_memalign(&result, static_cast(alignment), size); + return result; +} +void* operator new[](size_t size, std::align_val_t alignment) { return ::operator new(size, alignment); } +void operator delete(void* p) noexcept { std::free(p); } +void operator delete[](void* p) noexcept { std::free(p); } +void operator delete(void* p, size_t) noexcept { std::free(p); } +void operator delete[](void* p, size_t) noexcept { std::free(p); } +void operator delete(void* p, std::align_val_t) noexcept { std::free(p); } +void operator delete[](void* p, std::align_val_t) noexcept { std::free(p); } +void operator delete(void* p, size_t, std::align_val_t) noexcept { std::free(p); } +void operator delete[](void* p, size_t, std::align_val_t) noexcept { std::free(p); } + +// Explicit template instantiation permits access to private members for fixtures. +// Install an activity without starting the graphical game or loading content files. +struct ActivitySlot { + using type = std::unique_ptr ActivityMan::*; + friend type fixtureMember(ActivitySlot); +}; +struct TerrainWidth { + using type = int SLTerrain::*; + friend type fixtureMember(TerrainWidth); +}; +struct TerrainHeight { + using type = int SLTerrain::*; + friend type fixtureMember(TerrainHeight); +}; +template struct FixtureMember { + friend typename Tag::type fixtureMember(Tag) { return Member; } +}; +template struct FixtureMember; +template struct FixtureMember; +template struct FixtureMember; + +struct SceneAccess : SceneMan { + static Scene* SceneMan::* scene() { return &SceneAccess::m_pCurrentScene; } + static SpatialPartitionGrid SceneMan::* grid() { return &SceneAccess::m_MOIDsGrid; } + static std::array SceneMan::* materials() { return &SceneAccess::m_apMatPalette; } +}; +struct MovableAccess : MovableMan { + static std::vector MovableMan::* index() { return &MovableAccess::m_MOIDIndex; } +}; +struct TerrainFixture : SLTerrain { + TerrainFixture() { + this->*fixtureMember(TerrainWidth{}) = 520; + this->*fixtureMember(TerrainHeight{}) = 260; + m_WrapX = m_WrapY = false; + m_MainBitmap = create_bitmap_ex(8, 520, 260); + m_MainBitmapOwned = true; + clear_to_color(m_MainBitmap, g_MaterialAir); + } +}; +struct MaterialFixture : Material { + MaterialFixture() { + m_Index = 1; + m_Integrity = 1000000.0F; + m_Restitution = 0.65F; + m_Friction = 0.2F; + } +}; +struct AtomFixture : AtomGroup { + AtomFixture(MOSRotating* owner, Material* material, int atomCount) { + m_OwnerMOSR = owner; + m_Material = material; + for (int i = 0; i < atomCount; ++i) + AddAtom(new Atom(Vector(5.0F, i - atomCount / 2.0F), material, owner)); + } +}; + +uint64_t hashBits(uint64_t hash, uint64_t value) { return (hash ^ value) * 1099511628211ULL; } +uint64_t hashFloat(uint64_t hash, float value) { + uint32_t bits; + std::memcpy(&bits, &value, sizeof(bits)); + return hashBits(hash, bits); +} +uint64_t behaviorHash = 1469598103934665603ULL; +uint64_t responses = 0; +bool recordBehavior = false; + +struct DebrisFixture : MOSRotating { + Vector fixturePosition; + DebrisFixture(BITMAP* bitmap, Material* material, MOID id, Vector position, int atoms) : fixturePosition(position) { + m_aSprite = {bitmap}; + m_SpriteOffset = Vector(0, -bitmap->h / 2.0F); + m_SpriteRadius = 40; + m_Pos = position; + m_MOID = m_RootMOID = id; + m_Mass = 2.0F + (id % 7) * 0.1F; + m_HitsMOs = m_GetsHitByMOs = true; + m_pAtomGroup = new AtomFixture(this, material, atoms); + } + bool CollideAtPoint(HitData& hit) override { + bool result = MOSRotating::CollideAtPoint(hit); + if (recordBehavior) { + ++responses; + behaviorHash = hashBits(behaviorHash, GetID()); + behaviorHash = hashBits(behaviorHash, hit.Body[HITOR]->GetID()); + for (int body : {HITOR, HITEE}) { + behaviorHash = hashFloat(behaviorHash, hit.ResImpulse[body].m_X); + behaviorHash = hashFloat(behaviorHash, hit.ResImpulse[body].m_Y); + behaviorHash = hashFloat(behaviorHash, hit.HitRadius[body].m_X); + behaviorHash = hashFloat(behaviorHash, hit.HitRadius[body].m_Y); + behaviorHash = hashFloat(behaviorHash, hit.ImpulseFactor[body]); + } + } + return result; + } + void reset(int tick, bool hitsMOs) { + m_Pos = Vector(120.25F, 128.25F + (tick % 3) * 0.1F); + m_Vel = Vector(30.0F + (tick % 7), (tick % 5) * 0.05F); + m_Rotation.SetRadAngle((tick % 3 - 1) * 0.001F); + m_AngularVel = (tick % 5 - 2) * 0.02F; + m_TravelImpulse.Reset(); + m_HitsMOs = hitsMOs; + m_MOIDHit = g_NoMOID; + } + void travel(bool push) { + float remaining; + if (push) { + Vector impulse = m_pAtomGroup->PushTravel(m_Pos, m_Vel, 100.0F, m_DidWrap, 0.02F); + m_TravelImpulse = impulse; + remaining = 0; + } else { + remaining = m_pAtomGroup->Travel(0.02F); + } + if (recordBehavior) { + for (float value : {m_Pos.m_X, m_Pos.m_Y, m_Vel.m_X, m_Vel.m_Y, + m_Rotation.m_Rotation, m_AngularVel, m_TravelImpulse.m_X, m_TravelImpulse.m_Y, remaining}) + behaviorHash = hashFloat(behaviorHash, value); + behaviorHash = hashBits(behaviorHash, m_DidWrap); + } + } +}; + +int main(int argc, char** argv) { + const int calls = argc > 1 ? std::stoi(argv[1]) : 4096; + install_allegro(SYSTEM_NONE, &errno, atexit); + ThreadMan::Construct(); + TimerMan::Construct(); + SettingsMan::Construct(); + SceneMan::Construct(); + MovableMan::Construct(); + LuaMan::Construct(); + ActivityMan::Construct(); + (g_ActivityMan.*fixtureMember(ActivitySlot{})).reset(new Activity()); + auto* terrain = new TerrainFixture(); + Scene scene; + scene.Create(terrain); + g_SceneMan.*SceneAccess::scene() = &scene; + auto& grid = g_SceneMan.*SceneAccess::grid(); + grid.Create(520, 260, 20); + auto& index = g_MovableMan.*MovableAccess::index(); + MaterialFixture material; + auto& materials = g_SceneMan.*SceneAccess::materials(); + materials[0] = materials[1] = &material; + BITMAP* moverBitmap = create_bitmap_ex(8, 8, 64); + clear_to_color(moverBitmap, 21); + DebrisFixture mover(moverBitmap, &material, 200, Vector(120.25F, 128.25F), 64); + for (const std::string mode : {"dense-1", "dense-8", "dense-32", "terrain", "air", "push-terrain", "push-air"}) { + grid.Reset(); + index.assign(201, nullptr); + index[200] = &mover; + const bool dense = mode.starts_with("dense"); + const bool terrainOnly = mode.ends_with("terrain"); + const bool push = mode.starts_with("push"); + const int targets = dense ? std::stoi(mode.substr(6)) : 0; + std::vector> debris; + BITMAP* targetBitmap = create_bitmap_ex(8, 8, dense ? 64 / targets : 64); + clear_to_color(targetBitmap, 21); + for (int i = 0; i < targets; ++i) { + auto object = std::make_unique(targetBitmap, &material, i + 1, + Vector(132, 96 + (i + 0.5F) * (64 / targets)), 8); + index[i + 1] = object.get(); + grid.Add(IntRect(132, 96 + i * (64 / targets), 140, 96 + (i + 1) * (64 / targets) - 1), *object); + debris.push_back(std::move(object)); + } + clear_to_color(terrain->GetMaterialBitmap(), g_MaterialAir); + if (terrainOnly) rectfill(terrain->GetMaterialBitmap(), 132, 64, 140, 192, 1); + auto run = [&](int count) { + for (int i = 0; i < count; ++i) { + for (auto& target : debris) target->SetPos(target->fixturePosition); + mover.reset(i, !push); + mover.travel(push); + for (auto& target : debris) { + if (recordBehavior) { + for (const auto& impulse : target->GetImpulses()) { + behaviorHash = hashFloat(behaviorHash, impulse.first.m_X); + behaviorHash = hashFloat(behaviorHash, impulse.first.m_Y); + behaviorHash = hashFloat(behaviorHash, impulse.second.m_X); + behaviorHash = hashFloat(behaviorHash, impulse.second.m_Y); + } + } + target->ClearImpulseForces(); + target->SetHitWhatMOID(g_NoMOID); + } + } + }; + recordBehavior = true; + behaviorHash = 1469598103934665603ULL; + responses = 0; + run(256); + recordBehavior = false; + std::cout << "mode=" << mode << " behavior_hash=" << behaviorHash << " responses=" << responses << '\n'; + run(512); + // Allocation counting is separate from timing to avoid counting overhead in samples. + allocations = allocationBytes = 0; + countAllocations = true; + run(calls); + countAllocations = false; + std::cout << "mode=" << mode << " calls=" << calls << " allocations=" << allocations << " bytes=" << allocationBytes << '\n'; + for (int sample = 0; sample < 7; ++sample) { + auto start = std::chrono::steady_clock::now(); + run(calls); + double elapsed = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + std::cout << "mode=" << mode << " sample=" << sample << " calls=" << calls << " ms=" << std::fixed << std::setprecision(3) << elapsed << '\n'; + } + debris.clear(); + destroy_bitmap(targetBitmap); + } + g_SceneMan.*SceneAccess::scene() = nullptr; + materials.fill(nullptr); + index.clear(); + destroy_bitmap(moverBitmap); +} diff --git a/Tools/Benchmarks/CollisionCallbacks.md b/Tools/Benchmarks/CollisionCallbacks.md new file mode 100644 index 0000000000..5e5a40267c --- /dev/null +++ b/Tools/Benchmarks/CollisionCallbacks.md @@ -0,0 +1,157 @@ +# Collision callback allocations + +Collision events originally construct script argument vectors before the script dispatcher finds that no callback exists. +Terrain events also construct a callback-name string at each invocation. +The changed event methods find the callback first and construct arguments only for a nonempty function list. +Each method reuses a constant callback-name string. +Collision state assignments and the existing script dispatcher remain unchanged. +Disabled callbacks retain the existing dispatcher behavior. + +## Fixtures + +The benchmark links the compiled engine objects and replaces `Main.cpp` with a fixture program. +It calls the production `Travel`, `PushTravel`, atom stepping, spatial grid, pixel queries, and collision response routines. +The fixture override of `CollideAtPoint` calls the production implementation before it records the response. + +Each moving debris object contains 64 atoms. A 520-by-260 scene uses the production grid cell size of 20 pixels. +Dense fixtures collide with one, eight, or 32 separate sprite bodies. +Additional fixtures cover terrain collisions and empty space through both travel routines. +The fixture resets object positions and clears target impulse queues between calls. + +The correctness pass records the collision order, both bodies' impulses, contact radii, and impulse factors. +It also records final positions, velocities, rotations, total impulses, remaining travel time, and target impulse queues. +The runner requires identical hashes and response counts for both versions. +Allocation counts include all C++ heap requests during the real routines, including aligned requests. +Allocation measurement and correctness checks occur outside the timed samples. + +## Run + +Create a release build with the normal engine dependencies. +Run the following command from the repository root: + +```sh +python3 Tools/Benchmarks/CollisionCallbacks.py build /tmp/contact-storage \ + --baseline-ref 41e6b7010817936e09d2929330b7519852a1fcca \ + --runs 3 --calls 4096 +``` + +On macOS, expose the FMOD library before you run the command: + +```sh +export DYLD_LIBRARY_PATH="$PWD/external/lib/macos" +``` + +The runner compiles both `MovableObject.cpp` versions with the same release flags. +All other compiled engine objects remain identical between versions. +The runner alternates process order between pairs and collects seven samples per fixture in each process. +Each sample contains 4096 travel calls, after 768 initial calls and a separate allocation pass. +The output directory contains both executables, raw logs, and `summary.json` with every sample. + +For an existing build with other local optimizations, replace those objects in both links: + +```sh +python3 Tools/Benchmarks/CollisionCallbacks.py /path/to/build /tmp/contact-storage \ + --baseline-ref 41e6b7010817936e09d2929330b7519852a1fcca \ + --replace-object Source_System_Matrix.cpp.o /path/to/baseline-Matrix.cpp.o \ + --replace-object Source_Entities_MOSprite.cpp.o /path/to/baseline-MOSprite.cpp.o \ + --runs 3 --calls 4096 +``` + +## Results + +Results use an Apple M3 Pro, GCC 15.2, C++20, and a release build with `-O3`. +The engine objects came from a complete release build. +Both versions use pristine `Matrix.cpp` and `MOSprite.cpp` objects from the stated baseline. +Local macOS dependency and runtime support changes remain outside this commit. + +The recorded results appear in `CollisionCallbacks-results.json`. + +Times are medians across 21 samples. Allocation counts cover one separate pass of 4096 calls and match across all three runs. + +| Fixture | Baseline ms | Changed ms | Time reduction | Baseline allocations | Changed allocations | +| --- | ---: | ---: | ---: | ---: | ---: | +| Dense, one body | 449.362 | 392.405 | 12.68% | 3,190,671 | 309,291 | +| Dense, eight bodies | 570.135 | 517.821 | 9.18% | 3,765,537 | 971,963 | +| Dense, 32 bodies | 839.132 | 783.234 | 6.66% | 4,719,757 | 2,028,381 | +| Terrain | 135.726 | 92.532 | 31.82% | 2,406,712 | 0 | +| Empty space | 58.809 | 59.868 | -1.80% | 0 | 0 | +| Push, terrain | 31.162 | 32.629 | -4.71% | 36,864 | 36,864 | +| Push, empty space | 18.051 | 18.951 | -4.99% | 0 | 0 | + +Every paired process produces identical physics hashes and collision response counts. +The dense time reductions persist across all three pairs. +The empty-space and push fixtures take 1.8–5.0% more time with the changed executable. +Their allocation counts remain identical. +The changed executable also changes code layout, but these measurements do not isolate the cause of those timing increases. + +These fixtures measure the real physics routines with synthetic geometry. +They do not measure a full game frame or prove a frame-rate increase. +These fixtures contain no collision scripts. Scripted debris still pays the existing callback costs. + +## Full game checks + +`PerformanceStress.rte` contains the full game fixtures. +The callback fixture uses 24 debris objects with collision callbacks and eight objects with a `Create` callback only. +It disables callbacks at tick 80 and enables them at tick 160. +The probes remain alive and retain their initial `Create` count. + +Both engine versions produced these identical results: + +```text +LUA_CHECK_ENABLED mo=77134 terrain=56712 creates=32 +LUA_CHECK_DISABLED_PASS +LUA_CHECK_PASS mo=152236 terrain=109160 creates=32 +``` + +The debris workload creates 400 collidable stock gibs and an actual frag grenade explosion every 60 ticks. +The capture uses a fixed simulation seed at each tick and stops after 240 ticks. +Every particle count matches between versions in all three pairs. +The timing analysis excludes the first 60 ticks of each process. +The CSV file contains all 1440 raw tick records. +The game summary contains the callback results and timing statistics. + +| Full game metric | Baseline ms | Changed ms | +| --- | ---: | ---: | +| Simulation mean | 2.610 | 2.565 | +| Simulation p95 | 8.746 | 9.078 | +| MovableMan mean | 2.420 | 2.374 | +| MovableMan p95 | 8.670 | 9.005 | + +Mean simulation time decreased by 0.73–2.90% across the three pairs, with a combined reduction of 1.72%. +The combined p95 increased by 3.80%. +These game timings include draws inside `MovableMan::Update`, so render cadence also affects the results. +Three pairs do not establish a general frame-rate or latency improvement. + +## Reproduce the game capture + +First, compile the paired engine objects with `CollisionCallbacks.py`. +Then build the capture executables: + +```sh +python3 Tools/Benchmarks/CaptureCollisionGame.py build /tmp/contact-storage +``` + +The helper writes `game-baseline` and `game-changed` into the output directory. +It adds timing and seed code to a temporary copy of `Main.cpp`. +It does not change the repository's game loop. +It also accepts object replacements as additional `NAME=PATH` arguments. + +Copy `Tools/Benchmarks/PerformanceStress.rte` into a game runtime's `Mods` directory. +Configure `UserData/Settings.ini` with these properties: + +```ini +LaunchIntoActivity = 1 +DefaultActivityType = GAScripted +DefaultActivityName = Debris Performance Stress +DefaultSceneName = Grasslands +``` + +Run each capture executable from that runtime with `-cout`. +Each `PERF_CAPTURE` row contains the tick index, simulation microseconds, MovableMan microseconds, and particle count. +The executable prints `PERF_CAPTURE_DONE` after the capture. +Alternate executable order for three pairs. +Exclude ticks 0–59 from timing statistics. +Use the nearest-rank method for p95. + +For the callback check, set `ScriptPath` to `PerformanceStress.rte/CallbackCheck.lua` in the fixture's `Activities.ini`. +Run both executables and compare the `LUA_CHECK` lines. diff --git a/Tools/Benchmarks/CollisionCallbacks.py b/Tools/Benchmarks/CollisionCallbacks.py new file mode 100644 index 0000000000..840081e814 --- /dev/null +++ b/Tools/Benchmarks/CollisionCallbacks.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +import argparse +import json +import pathlib +import shlex +import statistics +import subprocess + +parser = argparse.ArgumentParser(description='Build baseline and changed collision callback benchmarks with compiled engine objects.') +parser.add_argument('build_directory', type=pathlib.Path) +parser.add_argument('output_directory', type=pathlib.Path) +parser.add_argument('--baseline-ref', required=True) +parser.add_argument('--replace-object', nargs=2, action='append', default=[], metavar=('NAME', 'PATH')) +parser.add_argument('--runs', type=int, default=0, help='Run this many alternating baseline/changed pairs after compilation.') +parser.add_argument('--calls', type=int, default=4096) +args = parser.parse_args() +build = args.build_directory.resolve() +output = args.output_directory.resolve() +output.mkdir(parents=True, exist_ok=True) +source = pathlib.Path(__file__).with_suffix('.cpp').resolve() +repository = source.parents[2] +commands = json.loads((build / 'compile_commands.json').read_text()) +command = shlex.split(next(x['command'] for x in commands if x['file'].endswith('MovableObject.cpp'))) +command = command[:command.index('-MD')] +# Compile against the engine headers without the generated precompiled header. +command = [arg for arg in command if arg not in {'-ICortexCommand.p', '-fpch-preprocess'}] +command = ['-I' + str(repository / arg[5:]) if arg.startswith('-I../Source') else arg for arg in command] +index = command.index('-include') +del command[index:index + 2] +benchmark_object = str(output / 'CollisionCallbacks.o') +subprocess.run(command + ['-c', str(source), '-o', benchmark_object], cwd=build, check=True) +baseline_source = output / 'MovableObject-baseline.cpp' +baseline_source.write_bytes(subprocess.check_output( + ['git', 'show', args.baseline_ref + ':Source/Entities/MovableObject.cpp'], cwd=repository)) +link = subprocess.check_output(['ninja', '-C', str(build), '-t', 'commands', 'CortexCommand'], text=True).splitlines()[-1] +for variant, engine_source in [('baseline', baseline_source), ('changed', repository / 'Source/Entities/MovableObject.cpp')]: + engine_object = str(output / ('MovableObject-' + variant + '.o')) + # The source relies on the project's normal forced StandardIncludes.h include. + subprocess.run(command + ['-include', str(repository / 'Source/System/StandardIncludes.h'), + '-c', str(engine_source), '-o', engine_object], cwd=build, check=True) + link_command = shlex.split(link) + link_command[link_command.index('-o') + 1] = str(output / variant) + link_command[link_command.index('CortexCommand.p/Source_Main.cpp.o')] = benchmark_object + link_command[link_command.index('CortexCommand.p/Source_Entities_MovableObject.cpp.o')] = engine_object + for name, replacement in args.replace_object: + link_command[link_command.index('CortexCommand.p/' + name)] = str(pathlib.Path(replacement).resolve()) + subprocess.run(link_command, cwd=build, check=True) + +results = {'baseline_ref': args.baseline_ref, 'calls_per_sample': args.calls, 'runs': args.runs, 'modes': {}} +for run in range(args.runs): + for variant in (['baseline', 'changed'] if run % 2 == 0 else ['changed', 'baseline']): + log = subprocess.check_output([str(output / variant), str(args.calls)], text=True) + (output / (variant + '-run' + str(run + 1) + '.txt')).write_text(log) + for line in log.splitlines(): + row = dict(item.split('=', 1) for item in line.split()) + mode = results['modes'].setdefault(row['mode'], {}) + entry = mode.setdefault(variant, {'behavior_hashes': [], 'responses': [], 'allocations': [], 'bytes': [], 'ms': []}) + for key in entry: + field = 'behavior_hash' if key == 'behavior_hashes' else key + if field in row: + entry[key].append(float(row[field]) if key == 'ms' else int(row[field])) +for mode, values in results['modes'].items(): + baseline, changed = values['baseline'], values['changed'] + assert baseline['behavior_hashes'] == changed['behavior_hashes'], mode + ': outcome or collision order differs' + assert baseline['responses'] == changed['responses'], mode + ': response count differs' + values['baseline_median_ms'] = statistics.median(baseline['ms']) + values['changed_median_ms'] = statistics.median(changed['ms']) + values['time_reduction_percent'] = 100 * (1 - values['changed_median_ms'] / values['baseline_median_ms']) + print(mode, 'baseline_ms=', round(values['baseline_median_ms'], 3), + 'changed_ms=', round(values['changed_median_ms'], 3), + 'time_reduction_percent=', round(values['time_reduction_percent'], 2)) +if args.runs: + (output / 'summary.json').write_text(json.dumps(results, indent=2) + '\n') diff --git a/Tools/Benchmarks/PerformanceStress.rte/Activities.ini b/Tools/Benchmarks/PerformanceStress.rte/Activities.ini new file mode 100644 index 0000000000..cff322fa4d --- /dev/null +++ b/Tools/Benchmarks/PerformanceStress.rte/Activities.ini @@ -0,0 +1,10 @@ +AddActivity = GAScripted + PresetName = Debris Performance Stress + Description = Fixed debris waves for local performance measurements. + TeamOfPlayer1 = 0 + MinTeamsRequired = 1 + SceneName = Grasslands + ScriptPath = PerformanceStress.rte/Stress.lua + LuaClassName = PerformanceStress + DefaultFogOfWar = 0 + DefaultDeployUnits = 0 diff --git a/Tools/Benchmarks/PerformanceStress.rte/CallbackCheck.lua b/Tools/Benchmarks/PerformanceStress.rte/CallbackCheck.lua new file mode 100644 index 0000000000..896d3af028 --- /dev/null +++ b/Tools/Benchmarks/PerformanceStress.rte/CallbackCheck.lua @@ -0,0 +1,71 @@ +local probeScript = "PerformanceStress.rte/CollisionProbe.lua" +function PerformanceStress:StartActivity() + self.ActivityState = Activity.RUNNING + self.ticks = 0 + self.probes = {} + self.starts = {} + self.paths = {} + for i = 1, 32 do + local ground = SceneMan:MovePointToGround(Vector(SceneMan.SceneWidth / 2 + (i % 8) * 3, 0), 0, 3) + local particle = CreateMOSRotating("Gib Metal Grey Small A", "Base.rte") + particle.Pos = ground + Vector(0, -4 - math.floor(i / 8) * 2) + particle.GetsHitByMOs = true + particle.MissionCritical = true + particle.GibImpulseLimit = 1000000000 + particle.GibWoundLimit = 1000000 + particle.RestThreshold = -1 + particle.Lifetime = 60000 + self.paths[i] = i % 4 == 0 and "PerformanceStress.rte/EmptyCollisionProbe.lua" or probeScript + particle:AddScript(self.paths[i]) + self.probes[i] = particle + self.starts[i] = Vector(particle.Pos.X, particle.Pos.Y) + MovableMan:AddParticle(particle) + end + self.center = self.starts[1] + print("LUA_CHECK_START") +end +local function totals(self) + local mo = 0 + local terrain = 0 + local creates = 0 + for i, particle in ipairs(self.probes) do + assert(MovableMan:ValidMO(particle), "Probe disappeared") + if i % 4 == 0 then + assert(particle:GetNumberValue("probe_mo") == 0 and particle:GetNumberValue("probe_terrain") == 0, "Missing callback ran") + end + mo = mo + particle:GetNumberValue("probe_mo") + terrain = terrain + particle:GetNumberValue("probe_terrain") + creates = creates + particle:GetNumberValue("probe_create") + end + return mo, terrain, creates +end +function PerformanceStress:UpdateActivity() + self.ticks = self.ticks + 1 + CameraMan:SetScrollTarget(self.center, 1, 0) + for i, particle in ipairs(self.probes) do + assert(MovableMan:ValidMO(particle), "Probe disappeared") + particle.Pos = self.starts[i] + particle.Vel = Vector(i % 2 == 0 and 8 or -8, 20) + particle.AngularVel = 0 + particle.RotAngle = 0 + end + if self.ticks == 80 then + self.disabledMO, self.disabledTerrain, self.creates = totals(self) + assert(self.disabledMO > 0 and self.disabledTerrain > 0, "Enabled callbacks did not run") + assert(self.creates == 32, "Create callback count mismatch") + for i, particle in ipairs(self.probes) do particle:DisableScript(self.paths[i]) end + print("LUA_CHECK_ENABLED mo=" .. self.disabledMO .. " terrain=" .. self.disabledTerrain .. " creates=" .. self.creates) + elseif self.ticks == 160 then + local mo, terrain, creates = totals(self) + assert(mo == self.disabledMO and terrain == self.disabledTerrain, "Disabled callbacks ran") + assert(creates == self.creates, "Create repeated while disabled") + for i, particle in ipairs(self.probes) do particle:EnableScript(self.paths[i]) end + print("LUA_CHECK_DISABLED_PASS") + elseif self.ticks == 239 then + local mo, terrain, creates = totals(self) + assert(mo > self.disabledMO and terrain > self.disabledTerrain, "Re-enabled callbacks did not run") + assert(creates == self.creates, "Create repeated after enable") + print("LUA_CHECK_PASS mo=" .. mo .. " terrain=" .. terrain .. " creates=" .. creates) + print("PERF_DONE") + end +end diff --git a/Tools/Benchmarks/PerformanceStress.rte/CollisionProbe.lua b/Tools/Benchmarks/PerformanceStress.rte/CollisionProbe.lua new file mode 100644 index 0000000000..9f94759fa0 --- /dev/null +++ b/Tools/Benchmarks/PerformanceStress.rte/CollisionProbe.lua @@ -0,0 +1,11 @@ +function Create(self) + self:SetNumberValue("probe_create", self:GetNumberValue("probe_create") + 1) +end +function OnCollideWithMO(self, other, rootMO) + assert(other ~= nil and rootMO ~= nil, "Missing collision arguments") + self:SetNumberValue("probe_mo", self:GetNumberValue("probe_mo") + 1) +end +function OnCollideWithTerrain(self, materialID) + assert(type(materialID) == "number" and materialID > 0, "Invalid terrain argument") + self:SetNumberValue("probe_terrain", self:GetNumberValue("probe_terrain") + 1) +end diff --git a/Tools/Benchmarks/PerformanceStress.rte/EmptyCollisionProbe.lua b/Tools/Benchmarks/PerformanceStress.rte/EmptyCollisionProbe.lua new file mode 100644 index 0000000000..5eda519db9 --- /dev/null +++ b/Tools/Benchmarks/PerformanceStress.rte/EmptyCollisionProbe.lua @@ -0,0 +1,3 @@ +function Create(self) + self:SetNumberValue("probe_create", self:GetNumberValue("probe_create") + 1) +end diff --git a/Tools/Benchmarks/PerformanceStress.rte/Index.ini b/Tools/Benchmarks/PerformanceStress.rte/Index.ini new file mode 100644 index 0000000000..559f7e0fe2 --- /dev/null +++ b/Tools/Benchmarks/PerformanceStress.rte/Index.ini @@ -0,0 +1,4 @@ +DataModule + SupportedGameVersion = 7.0.0 + ModuleName = Performance Stress + IncludeFile = PerformanceStress.rte/Activities.ini diff --git a/Tools/Benchmarks/PerformanceStress.rte/Stress.lua b/Tools/Benchmarks/PerformanceStress.rte/Stress.lua new file mode 100644 index 0000000000..be45eb81c2 --- /dev/null +++ b/Tools/Benchmarks/PerformanceStress.rte/Stress.lua @@ -0,0 +1,35 @@ +function PerformanceStress:StartActivity() + self.ActivityState = Activity.RUNNING + self.ticks = 0 + self.timer = Timer() + self.center = SceneMan:MovePointToGround(Vector(SceneMan.SceneWidth / 2, 0), 0, 3) + self.center.Y = self.center.Y - 100 + PerformanceMan.ShowPerformanceStats = true + print("PERF_START debris_waves") +end + +function PerformanceStress:UpdateActivity() + self.ticks = self.ticks + 1 + CameraMan:SetScrollTarget(self.center, 1, 0) + if self.ticks % 60 == 1 and self.ticks <= 541 then + for i = 1, 400 do + local gib = CreateMOSRotating("Gib Metal Grey Small A", "Base.rte") + gib.Pos = self.center + Vector((i * 17) % 120 - 60, (i * 23) % 80 - 40) + gib.Vel = Vector((i * 7) % 30 - 15, -((i * 11) % 20)) + gib.AngularVel = (i % 20) - 10 + gib.HFlipped = i % 2 == 0 + gib.Lifetime = 3000 + gib.GetsHitByMOs = true + MovableMan:AddParticle(gib) + end + local grenade = CreateTDExplosive("Frag Grenade", "Base.rte") + grenade.Pos = self.center + grenade:GibThis() + end + if self.ticks % 60 == 0 then + print("PERF_SAMPLE ticks=" .. self.ticks .. " real_ms=" .. self.timer.ElapsedRealTimeMS .. " particles=" .. MovableMan:GetParticleCount()) + end + if self.ticks == 600 then + print("PERF_DONE real_ms=" .. self.timer.ElapsedRealTimeMS) + end +end From def40e252da987ff56eb9bc69fa43521846e07fb Mon Sep 17 00:00:00 2001 From: Florian De Rop <55103921+fderop@users.noreply.github.com> Date: Fri, 2 Oct 2026 03:14:37 -0700 Subject: [PATCH 2/3] bench: keep collision fixtures within pooled entity size --- .../CollisionCallbacks-results.json | 633 +++++++++--------- Tools/Benchmarks/CollisionCallbacks.cpp | 8 +- Tools/Benchmarks/CollisionCallbacks.md | 24 +- 3 files changed, 336 insertions(+), 329 deletions(-) diff --git a/Tools/Benchmarks/CollisionCallbacks-results.json b/Tools/Benchmarks/CollisionCallbacks-results.json index eb3aea1e78..699137c7d6 100644 --- a/Tools/Benchmarks/CollisionCallbacks-results.json +++ b/Tools/Benchmarks/CollisionCallbacks-results.json @@ -26,27 +26,27 @@ 120301472 ], "ms": [ - 447.497, - 446.235, - 448.35, - 449.061, - 448.854, - 449.461, - 449.362, - 448.774, - 448.436, - 449.07, - 451.546, - 453.324, - 450.558, - 463.621, - 449.201, - 450.902, - 450.757, - 467.158, - 448.547, - 451.245, - 452.049 + 416.027, + 413.557, + 412.897, + 415.041, + 444.457, + 445.102, + 442.987, + 435.698, + 435.766, + 435.717, + 437.39, + 436.467, + 436.442, + 436.274, + 459.83, + 451.57, + 452.838, + 449.184, + 450.516, + 451.235, + 449.596 ] }, "changed": { @@ -71,32 +71,32 @@ 74199392 ], "ms": [ - 392.087, - 392.121, - 394.902, - 399.071, - 400.116, - 391.631, - 391.788, - 390.463, - 401.463, - 388.651, - 388.538, - 391.346, - 390.874, - 392.405, - 391.131, - 395.341, - 402.408, - 397.021, - 408.959, - 402.276, - 395.864 + 368.842, + 369.804, + 369.078, + 369.797, + 371.288, + 370.216, + 370.647, + 377.02, + 376.861, + 379.546, + 376.265, + 376.139, + 378.97, + 376.144, + 375.669, + 375.15, + 375.357, + 375.067, + 375.184, + 375.08, + 375.559 ] }, - "baseline_median_ms": 449.362, - "changed_median_ms": 392.405, - "time_reduction_percent": 12.675081560078528 + "baseline_median_ms": 437.39, + "changed_median_ms": 375.184, + "time_reduction_percent": 14.222090125517262 }, "dense-8": { "baseline": { @@ -121,27 +121,27 @@ 112031808 ], "ms": [ - 567.417, - 581.624, - 569.528, - 568.91, - 571.32, - 568.585, - 567.605, - 568.296, - 569.229, - 572.319, - 570.493, - 569.199, - 568.692, - 580.558, - 576.332, - 568.775, - 572.084, - 589.427, - 570.135, - 570.642, - 570.621 + 572.89, + 573.598, + 562.251, + 545.297, + 541.289, + 539.696, + 539.535, + 552.158, + 552.575, + 551.608, + 553.343, + 552.089, + 553.217, + 554.914, + 567.573, + 567.932, + 568.065, + 569.169, + 568.138, + 572.179, + 568.36 ] }, "changed": { @@ -166,32 +166,32 @@ 67334624 ], "ms": [ - 517.821, - 513.658, - 514.637, - 512.494, - 512.828, - 513.3, - 513.978, - 517.383, - 518.366, - 518.94, - 518.01, - 528.781, - 518.303, - 516.892, - 525.315, - 519.82, - 524.762, - 512.832, - 546.846, - 515.815, - 536.198 + 493.21, + 498.351, + 496.138, + 507.9, + 507.478, + 508.469, + 505.506, + 509.758, + 508.33, + 501.844, + 499.318, + 503.7, + 498.52, + 498.876, + 509.474, + 511.22, + 509.611, + 508.148, + 507.603, + 507.584, + 508.492 ] }, - "baseline_median_ms": 570.135, - "changed_median_ms": 517.821, - "time_reduction_percent": 9.17572153963534 + "baseline_median_ms": 554.914, + "changed_median_ms": 507.584, + "time_reduction_percent": 8.52924957741199 }, "dense-32": { "baseline": { @@ -216,27 +216,27 @@ 86475680 ], "ms": [ - 855.329, - 840.286, - 836.947, - 835.584, - 839.985, - 852.704, - 839.677, - 833.841, - 839.712, - 850.79, - 836.581, - 839.276, - 839.029, - 832.138, - 834.341, - 836.198, - 853.546, - 843.917, - 839.132, - 838.003, - 838.655 + 798.775, + 803.786, + 805.391, + 805.139, + 804.295, + 804.914, + 805.122, + 823.378, + 820.131, + 819.375, + 820.828, + 822.616, + 829.156, + 826.044, + 849.515, + 825.878, + 821.148, + 820.327, + 821.055, + 821.388, + 820.951 ] }, "changed": { @@ -261,32 +261,32 @@ 43413664 ], "ms": [ - 779.985, - 780.796, - 777.219, - 780.675, - 782.868, - 793.508, - 782.867, - 785.561, - 783.126, - 799.869, - 784.236, - 783.144, - 784.145, - 781.947, - 784.856, - 786.438, - 783.234, - 794.957, - 783.827, - 786.091, - 781.165 + 777.41, + 756.486, + 755.365, + 758.975, + 754.549, + 760.124, + 773.193, + 763.122, + 763.607, + 763.786, + 763.919, + 762.362, + 761.089, + 763.501, + 766.66, + 766.942, + 771.89, + 767.262, + 767.055, + 768.087, + 769.086 ] }, - "baseline_median_ms": 839.132, - "changed_median_ms": 783.234, - "time_reduction_percent": 6.661407263696284 + "baseline_median_ms": 820.828, + "changed_median_ms": 763.786, + "time_reduction_percent": 6.9493243407876 }, "terrain": { "baseline": { @@ -311,27 +311,27 @@ 44524172 ], "ms": [ - 135.477, - 135.481, - 135.641, - 135.572, - 135.444, - 135.502, - 135.496, - 135.889, - 148.369, - 136.545, - 135.817, - 135.471, - 135.315, - 135.801, - 136.876, - 136.858, - 135.579, - 135.726, - 150.523, - 138.244, - 136.817 + 130.012, + 128.807, + 129.062, + 128.399, + 129.014, + 131.828, + 136.575, + 133.383, + 132.939, + 133.791, + 132.818, + 132.846, + 133.605, + 132.862, + 133.224, + 133.025, + 132.778, + 132.868, + 132.693, + 132.727, + 132.728 ] }, "changed": { @@ -356,32 +356,32 @@ 0 ], "ms": [ - 92.089, - 92.475, - 92.124, - 92.759, - 92.616, - 92.127, - 92.727, - 92.416, - 92.702, - 92.541, - 92.704, - 92.73, - 92.674, - 92.316, - 91.928, - 91.988, - 92.532, - 93.189, - 92.106, - 92.412, - 92.664 + 91.74, + 92.109, + 91.483, + 91.353, + 91.756, + 91.276, + 91.24, + 89.744, + 88.85, + 89.616, + 88.85, + 88.71, + 88.65, + 88.436, + 90.366, + 90.717, + 90.322, + 90.344, + 90.076, + 90.306, + 90.898 ] }, - "baseline_median_ms": 135.726, - "changed_median_ms": 92.532, - "time_reduction_percent": 31.824410945581537 + "baseline_median_ms": 132.818, + "changed_median_ms": 90.344, + "time_reduction_percent": 31.979099218479433 }, "air": { "baseline": { @@ -406,27 +406,27 @@ 0 ], "ms": [ - 58.863, - 58.827, - 58.855, - 58.654, - 58.819, - 58.772, - 58.714, - 58.762, - 59.058, - 58.466, - 58.85, - 58.543, - 58.803, - 58.517, - 59.216, - 59.063, - 59.045, - 58.91, - 58.66, - 58.809, - 58.629 + 58.455, + 58.162, + 58.853, + 58.154, + 58.289, + 58.216, + 58.09, + 59.166, + 59.009, + 57.972, + 57.812, + 57.924, + 57.885, + 57.865, + 58.072, + 58.23, + 58.589, + 58.091, + 58.366, + 58.111, + 58.671 ] }, "changed": { @@ -451,32 +451,32 @@ 0 ], "ms": [ - 60.482, - 59.634, - 60.004, - 59.884, - 59.868, - 59.955, - 60.199, - 59.958, - 59.898, - 59.86, - 59.826, - 59.791, - 59.585, - 59.509, - 59.477, - 59.666, - 59.922, - 59.704, - 60.021, - 59.615, - 60.108 + 58.268, + 58.263, + 58.294, + 58.226, + 58.305, + 58.283, + 58.211, + 57.328, + 57.297, + 57.22, + 57.315, + 57.217, + 57.704, + 57.28, + 58.155, + 58.554, + 58.205, + 57.869, + 57.899, + 57.878, + 57.846 ] }, - "baseline_median_ms": 58.809, - "changed_median_ms": 59.868, - "time_reduction_percent": -1.8007447839616386 + "baseline_median_ms": 58.162, + "changed_median_ms": 57.899, + "time_reduction_percent": 0.45218527560949573 }, "push-terrain": { "baseline": { @@ -501,27 +501,27 @@ 18579456 ], "ms": [ - 30.678, - 30.933, - 30.941, - 31.257, - 30.99, - 31.062, - 30.96, - 31.198, - 31.162, - 31.051, - 31.269, - 31.314, - 32.112, - 31.478, - 31.11, - 31.117, - 31.056, - 31.423, - 31.971, - 31.846, - 31.623 + 31.16, + 30.958, + 30.988, + 30.964, + 30.965, + 30.961, + 30.979, + 30.982, + 30.837, + 30.866, + 30.946, + 30.837, + 30.915, + 30.807, + 30.958, + 30.925, + 30.93, + 30.969, + 30.913, + 30.91, + 30.876 ] }, "changed": { @@ -546,32 +546,32 @@ 18579456 ], "ms": [ - 32.614, - 32.542, - 32.578, - 32.694, - 32.879, - 32.684, - 32.655, - 32.629, - 32.613, - 32.722, - 32.584, - 32.593, - 32.568, - 43.228, - 35.713, - 33.098, - 33.434, - 32.718, - 32.358, - 32.256, - 32.28 + 30.812, + 31.173, + 30.807, + 30.838, + 30.789, + 30.845, + 30.796, + 30.696, + 30.752, + 30.847, + 30.824, + 30.822, + 30.646, + 30.654, + 30.714, + 30.719, + 30.768, + 30.718, + 30.712, + 30.744, + 30.777 ] }, - "baseline_median_ms": 31.162, - "changed_median_ms": 32.629, - "time_reduction_percent": -4.707656761440204 + "baseline_median_ms": 30.946, + "changed_median_ms": 30.777, + "time_reduction_percent": 0.5461125832094682 }, "push-air": { "baseline": { @@ -596,27 +596,27 @@ 0 ], "ms": [ - 18.042, - 18.0, - 17.948, - 18.051, - 17.964, - 18.123, - 18.059, - 18.041, - 17.963, - 18.04, - 17.99, - 18.054, - 18.049, - 17.982, - 18.645, - 18.441, - 18.113, - 18.236, - 18.173, - 18.297, - 18.648 + 17.878, + 17.844, + 17.879, + 17.826, + 17.923, + 17.897, + 17.895, + 17.88, + 17.85, + 17.862, + 17.848, + 17.866, + 17.858, + 17.865, + 17.846, + 17.841, + 17.844, + 17.833, + 17.864, + 17.82, + 17.856 ] }, "changed": { @@ -641,34 +641,35 @@ 0 ], "ms": [ - 18.954, - 18.889, - 19.044, - 19.02, - 18.939, - 18.923, - 18.853, - 19.522, - 19.055, - 18.981, - 18.951, - 18.961, - 18.918, - 19.017, - 19.062, - 18.877, - 18.904, - 18.847, - 18.938, - 19.006, - 18.913 + 17.959, + 17.895, + 17.885, + 17.874, + 17.885, + 17.824, + 17.946, + 17.566, + 17.578, + 17.545, + 17.574, + 17.572, + 17.565, + 17.538, + 17.894, + 17.882, + 17.865, + 17.9, + 17.897, + 17.938, + 17.85 ] }, - "baseline_median_ms": 18.051, - "changed_median_ms": 18.951, - "time_reduction_percent": -4.985873358816706 + "baseline_median_ms": 17.858, + "changed_median_ms": 17.874, + "time_reduction_percent": -0.08959569940640844 } }, "platform": "Apple M3 Pro, macOS, GCC 15.2, C++20, release -O3", - "modified_engine_source": "Source/Entities/MovableObject.cpp" + "modified_engine_source": "Source/Entities/MovableObject.cpp", + "fixture_storage": "Target reset positions use external storage; DebrisFixture size equals MOSRotating." } diff --git a/Tools/Benchmarks/CollisionCallbacks.cpp b/Tools/Benchmarks/CollisionCallbacks.cpp index 9cb2a734ad..980c167675 100644 --- a/Tools/Benchmarks/CollisionCallbacks.cpp +++ b/Tools/Benchmarks/CollisionCallbacks.cpp @@ -102,8 +102,7 @@ uint64_t responses = 0; bool recordBehavior = false; struct DebrisFixture : MOSRotating { - Vector fixturePosition; - DebrisFixture(BITMAP* bitmap, Material* material, MOID id, Vector position, int atoms) : fixturePosition(position) { + DebrisFixture(BITMAP* bitmap, Material* material, MOID id, Vector position, int atoms) { m_aSprite = {bitmap}; m_SpriteOffset = Vector(0, -bitmap->h / 2.0F); m_SpriteRadius = 40; @@ -155,6 +154,7 @@ struct DebrisFixture : MOSRotating { } } }; +static_assert(sizeof(DebrisFixture) == sizeof(MOSRotating)); int main(int argc, char** argv) { const int calls = argc > 1 ? std::stoi(argv[1]) : 4096; @@ -189,6 +189,7 @@ int main(int argc, char** argv) { const bool push = mode.starts_with("push"); const int targets = dense ? std::stoi(mode.substr(6)) : 0; std::vector> debris; + std::vector targetPositions; BITMAP* targetBitmap = create_bitmap_ex(8, 8, dense ? 64 / targets : 64); clear_to_color(targetBitmap, 21); for (int i = 0; i < targets; ++i) { @@ -196,13 +197,14 @@ int main(int argc, char** argv) { Vector(132, 96 + (i + 0.5F) * (64 / targets)), 8); index[i + 1] = object.get(); grid.Add(IntRect(132, 96 + i * (64 / targets), 140, 96 + (i + 1) * (64 / targets) - 1), *object); + targetPositions.push_back(object->GetPos()); debris.push_back(std::move(object)); } clear_to_color(terrain->GetMaterialBitmap(), g_MaterialAir); if (terrainOnly) rectfill(terrain->GetMaterialBitmap(), 132, 64, 140, 192, 1); auto run = [&](int count) { for (int i = 0; i < count; ++i) { - for (auto& target : debris) target->SetPos(target->fixturePosition); + for (size_t target = 0; target < debris.size(); ++target) debris[target]->SetPos(targetPositions[target]); mover.reset(i, !push); mover.travel(push); for (auto& target : debris) { diff --git a/Tools/Benchmarks/CollisionCallbacks.md b/Tools/Benchmarks/CollisionCallbacks.md index 5e5a40267c..b7d6615e4a 100644 --- a/Tools/Benchmarks/CollisionCallbacks.md +++ b/Tools/Benchmarks/CollisionCallbacks.md @@ -66,23 +66,27 @@ Local macOS dependency and runtime support changes remain outside this commit. The recorded results appear in `CollisionCallbacks-results.json`. +These CPU measurements replace the invalid results from the original fixture. +The original debris fixture added a field beyond the fixed allocation size inherited from `MOSRotating`. +The corrected fixture stores reset positions outside the entity and requires equal fixture and base sizes at compilation. +The other heap fixtures add no fields. +The full-game and Lua captures use actual engine entities and remain valid. + Times are medians across 21 samples. Allocation counts cover one separate pass of 4096 calls and match across all three runs. | Fixture | Baseline ms | Changed ms | Time reduction | Baseline allocations | Changed allocations | | --- | ---: | ---: | ---: | ---: | ---: | -| Dense, one body | 449.362 | 392.405 | 12.68% | 3,190,671 | 309,291 | -| Dense, eight bodies | 570.135 | 517.821 | 9.18% | 3,765,537 | 971,963 | -| Dense, 32 bodies | 839.132 | 783.234 | 6.66% | 4,719,757 | 2,028,381 | -| Terrain | 135.726 | 92.532 | 31.82% | 2,406,712 | 0 | -| Empty space | 58.809 | 59.868 | -1.80% | 0 | 0 | -| Push, terrain | 31.162 | 32.629 | -4.71% | 36,864 | 36,864 | -| Push, empty space | 18.051 | 18.951 | -4.99% | 0 | 0 | +| Dense, one body | 437.390 | 375.184 | 14.22% | 3,190,671 | 309,291 | +| Dense, eight bodies | 554.914 | 507.584 | 8.53% | 3,765,537 | 971,963 | +| Dense, 32 bodies | 820.828 | 763.786 | 6.95% | 4,719,757 | 2,028,381 | +| Terrain | 132.818 | 90.344 | 31.98% | 2,406,712 | 0 | +| Empty space | 58.162 | 57.899 | 0.45% | 0 | 0 | +| Push, terrain | 30.946 | 30.777 | 0.55% | 36,864 | 36,864 | +| Push, empty space | 17.858 | 17.874 | -0.09% | 0 | 0 | Every paired process produces identical physics hashes and collision response counts. The dense time reductions persist across all three pairs. -The empty-space and push fixtures take 1.8–5.0% more time with the changed executable. -Their allocation counts remain identical. -The changed executable also changes code layout, but these measurements do not isolate the cause of those timing increases. +The control medians differ by less than 0.6%, and their allocation counts remain identical. These fixtures measure the real physics routines with synthetic geometry. They do not measure a full game frame or prove a frame-rate increase. From 8af3c828018471602705d464245e235035fbc5f1 Mon Sep 17 00:00:00 2001 From: Florian De Rop <55103921+fderop@users.noreply.github.com> Date: Fri, 2 Oct 2026 10:31:58 -0700 Subject: [PATCH 3/3] bench: preserve rejected contact pool candidate and provenance --- Tools/Benchmarks/ContactPool-provenance.json | 34 + Tools/Benchmarks/ContactPool-results.json | 673 +++++++++++++++++++ Tools/Benchmarks/ContactPool.md | 82 +++ Tools/Benchmarks/ContactPool.patch | 10 + Tools/Benchmarks/ContactPool.py | 85 +++ 5 files changed, 884 insertions(+) create mode 100644 Tools/Benchmarks/ContactPool-provenance.json create mode 100644 Tools/Benchmarks/ContactPool-results.json create mode 100644 Tools/Benchmarks/ContactPool.md create mode 100644 Tools/Benchmarks/ContactPool.patch create mode 100644 Tools/Benchmarks/ContactPool.py diff --git a/Tools/Benchmarks/ContactPool-provenance.json b/Tools/Benchmarks/ContactPool-provenance.json new file mode 100644 index 0000000000..6599cd1ead --- /dev/null +++ b/Tools/Benchmarks/ContactPool-provenance.json @@ -0,0 +1,34 @@ +{ + "saved_object_sha256": "9e08efad525476ff0db310aae8b156943d58826f8508ad14fb4d7c8c68743b85", + "rebuilt_object_sha256": "c4af01ff4dbcb2c9b0d1c504eb4dcb37aa902677a3541e73d8588b175054ce51", + "whole_object_equal": false, + "instruction_sections": { + "__TEXT,__text": { + "bytes": 59272, + "code_equal": true, + "relocations_equal": true, + "sha256": "1f194f6867930235336d03b4b768a312fa8abf755662594df50bac9bdf21133e" + }, + "__TEXT,__text_startup": { + "bytes": 48, + "code_equal": true, + "relocations_equal": true, + "sha256": "e675f19bfa06a8c1ab203f21d8b2ab1137bf4360e29bc1619df4bbb64197b4b6" + }, + "__TEXT,__StaticInit": { + "bytes": 2664, + "code_equal": true, + "relocations_equal": true, + "sha256": "8c28cf49029df8a2a1710c63fbdc2c18697a5850d1f152df69f63d2263414d68" + } + }, + "section_differences": {}, + "all_section_contents_and_relocations_equal": true, + "symbol_table_equal": false, + "runner_build_check": { + "generated_source_equals_recovered_source": true, + "all_section_contents_and_relocations_equal_saved_object": true, + "object_sha256": "3129448554f830f01703eeb9b90168102b786ebaef7f5d92732079112be251b1", + "performance_suite_repeated": false + } +} diff --git a/Tools/Benchmarks/ContactPool-results.json b/Tools/Benchmarks/ContactPool-results.json new file mode 100644 index 0000000000..7d11541875 --- /dev/null +++ b/Tools/Benchmarks/ContactPool-results.json @@ -0,0 +1,673 @@ +{ + "baseline_ref": "41e6b7010817936e09d2929330b7519852a1fcca", + "experiment": "original Travel PMR pool, corrected fixed-size fixture", + "calls_per_sample": 4096, + "runs": 3, + "modes": { + "dense-1": { + "baseline": { + "behavior_hashes": [ + 1015060394229153914, + 1015060394229153914, + 1015060394229153914 + ], + "responses": [ + 89956, + 89956, + 89956 + ], + "allocations": [ + 3190671, + 3190671, + 3190671 + ], + "bytes": [ + 120301472, + 120301472, + 120301472 + ], + "ms": [ + 442.243, + 443.501, + 443.1, + 444.005, + 443.591, + 443.738, + 444.001, + 437.118, + 434.574, + 434.595, + 434.415, + 434.945, + 438.733, + 436.839, + 443.911, + 443.463, + 442.805, + 443.506, + 446.23, + 443.505, + 443.401 + ] + }, + "changed": { + "behavior_hashes": [ + 1015060394229153914, + 1015060394229153914, + 1015060394229153914 + ], + "responses": [ + 89956, + 89956, + 89956 + ], + "allocations": [ + 2999573, + 2999573, + 2999573 + ], + "bytes": [ + 96098368, + 96098368, + 96098368 + ], + "ms": [ + 448.948, + 440.546, + 439.886, + 437.84, + 437.167, + 436.968, + 436.832, + 433.833, + 432.925, + 434.392, + 434.184, + 438.216, + 444.031, + 441.189, + 450.646, + 449.176, + 448.872, + 450.302, + 447.735, + 447.788, + 447.775 + ] + }, + "baseline_median_ms": 443.401, + "changed_median_ms": 440.546, + "time_reduction_percent": 0.6438866849646341 + }, + "dense-8": { + "baseline": { + "behavior_hashes": [ + 10678557251391008333, + 10678557251391008333, + 10678557251391008333 + ], + "responses": [ + 87690, + 87690, + 87690 + ], + "allocations": [ + 3765537, + 3765537, + 3765537 + ], + "bytes": [ + 112031808, + 112031808, + 112031808 + ], + "ms": [ + 570.318, + 558.114, + 552.793, + 552.017, + 552.43, + 553.608, + 552.714, + 562.28, + 565.2, + 563.249, + 562.787, + 563.301, + 562.187, + 568.909, + 564.936, + 562.396, + 563.78, + 562.073, + 560.921, + 563.006, + 562.073 + ] + }, + "changed": { + "behavior_hashes": [ + 10678557251391008333, + 10678557251391008333, + 10678557251391008333 + ], + "responses": [ + 87690, + 87690, + 87690 + ], + "allocations": [ + 2885595, + 2885595, + 2885595 + ], + "bytes": [ + 83914848, + 83914848, + 83914848 + ], + "ms": [ + 563.121, + 562.206, + 560.956, + 560.277, + 561.149, + 560.418, + 559.568, + 559.132, + 560.774, + 560.554, + 560.616, + 560.888, + 560.745, + 563.097, + 563.694, + 561.738, + 562.971, + 560.56, + 561.891, + 561.461, + 560.688 + ] + }, + "baseline_median_ms": 562.28, + "changed_median_ms": 560.888, + "time_reduction_percent": 0.24756349149888113 + }, + "dense-32": { + "baseline": { + "behavior_hashes": [ + 6501605824948049464, + 6501605824948049464, + 6501605824948049464 + ], + "responses": [ + 84035, + 84035, + 84035 + ], + "allocations": [ + 4719757, + 4719757, + 4719757 + ], + "bytes": [ + 86475680, + 86475680, + 86475680 + ], + "ms": [ + 821.268, + 828.709, + 827.623, + 827.631, + 826.921, + 826.317, + 829.238, + 824.909, + 822.329, + 821.49, + 821.475, + 827.004, + 823.01, + 822.689, + 822.952, + 819.612, + 821.097, + 820.921, + 821.98, + 822.813, + 819.734 + ] + }, + "changed": { + "behavior_hashes": [ + 6501605824948049464, + 6501605824948049464, + 6501605824948049464 + ], + "responses": [ + 84035, + 84035, + 84035 + ], + "allocations": [ + 2699957, + 2699957, + 2699957 + ], + "bytes": [ + 43379328, + 43379328, + 43379328 + ], + "ms": [ + 827.921, + 821.082, + 820.02, + 822.29, + 825.532, + 823.772, + 824.152, + 825.648, + 825.014, + 825.266, + 826.056, + 826.092, + 824.266, + 827.998, + 823.382, + 823.716, + 822.447, + 821.761, + 823.181, + 821.196, + 821.429 + ] + }, + "baseline_median_ms": 822.813, + "changed_median_ms": 823.772, + "time_reduction_percent": -0.11655139138542214 + }, + "terrain": { + "baseline": { + "behavior_hashes": [ + 15405424348098678617, + 15405424348098678617, + 15405424348098678617 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 2406712, + 2406712, + 2406712 + ], + "bytes": [ + 44524172, + 44524172, + 44524172 + ], + "ms": [ + 135.239, + 134.962, + 134.83, + 135.017, + 135.096, + 135.295, + 135.412, + 135.141, + 136.555, + 135.544, + 134.74, + 135.46, + 134.621, + 135.208, + 134.125, + 134.441, + 134.114, + 134.579, + 134.517, + 134.423, + 134.471 + ] + }, + "changed": { + "behavior_hashes": [ + 15405424348098678617, + 15405424348098678617, + 15405424348098678617 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 2406712, + 2406712, + 2406712 + ], + "bytes": [ + 44524172, + 44524172, + 44524172 + ], + "ms": [ + 132.125, + 133.468, + 133.137, + 131.947, + 131.879, + 131.765, + 131.81, + 134.004, + 132.711, + 132.708, + 132.063, + 132.125, + 131.952, + 131.96, + 135.688, + 134.684, + 134.16, + 134.159, + 134.257, + 134.334, + 134.316 + ] + }, + "baseline_median_ms": 134.962, + "changed_median_ms": 132.711, + "time_reduction_percent": 1.6678768838635838 + }, + "air": { + "baseline": { + "behavior_hashes": [ + 16994007857569456424, + 16994007857569456424, + 16994007857569456424 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 58.366, + 58.325, + 58.516, + 58.25, + 58.332, + 58.308, + 58.274, + 58.25, + 58.35, + 58.403, + 59.322, + 59.383, + 59.663, + 59.07, + 58.333, + 58.296, + 58.398, + 58.225, + 59.789, + 61.182, + 60.07 + ] + }, + "changed": { + "behavior_hashes": [ + 16994007857569456424, + 16994007857569456424, + 16994007857569456424 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 57.128, + 56.96, + 57.075, + 57.069, + 56.974, + 57.127, + 56.999, + 57.162, + 57.205, + 57.105, + 57.208, + 57.118, + 57.122, + 57.145, + 57.542, + 57.499, + 57.515, + 57.471, + 57.511, + 57.458, + 57.937 + ] + }, + "baseline_median_ms": 58.366, + "changed_median_ms": 57.145, + "time_reduction_percent": 2.0919713531850626 + }, + "push-terrain": { + "baseline": { + "behavior_hashes": [ + 18279129362497917356, + 18279129362497917356, + 18279129362497917356 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 36864, + 36864, + 36864 + ], + "bytes": [ + 18579456, + 18579456, + 18579456 + ], + "ms": [ + 31.258, + 31.184, + 30.982, + 31.086, + 31.2, + 31.01, + 31.029, + 30.878, + 30.973, + 30.883, + 30.861, + 30.907, + 30.97, + 30.83, + 30.799, + 30.81, + 30.851, + 30.824, + 30.793, + 30.809, + 30.869 + ] + }, + "changed": { + "behavior_hashes": [ + 18279129362497917356, + 18279129362497917356, + 18279129362497917356 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 36864, + 36864, + 36864 + ], + "bytes": [ + 18579456, + 18579456, + 18579456 + ], + "ms": [ + 30.942, + 30.881, + 30.837, + 30.891, + 30.82, + 30.087, + 30.28, + 30.853, + 30.918, + 30.877, + 30.871, + 30.948, + 30.817, + 30.85, + 30.987, + 31.007, + 30.971, + 31.053, + 30.994, + 31.01, + 31.565 + ] + }, + "baseline_median_ms": 30.883, + "changed_median_ms": 30.891, + "time_reduction_percent": -0.025904219149697205 + }, + "push-air": { + "baseline": { + "behavior_hashes": [ + 12725381982755921557, + 12725381982755921557, + 12725381982755921557 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 18.37, + 18.846, + 18.592, + 18.182, + 17.854, + 17.892, + 17.843, + 17.944, + 17.879, + 17.931, + 17.933, + 17.924, + 17.888, + 17.898, + 17.952, + 17.874, + 17.904, + 17.91, + 17.934, + 17.948, + 17.879 + ] + }, + "changed": { + "behavior_hashes": [ + 12725381982755921557, + 12725381982755921557, + 12725381982755921557 + ], + "responses": [ + 0, + 0, + 0 + ], + "allocations": [ + 0, + 0, + 0 + ], + "bytes": [ + 0, + 0, + 0 + ], + "ms": [ + 17.553, + 17.577, + 17.618, + 17.581, + 17.581, + 17.616, + 17.617, + 17.862, + 17.818, + 17.87, + 17.819, + 17.851, + 17.807, + 17.844, + 17.868, + 17.822, + 17.85, + 17.824, + 17.851, + 17.841, + 17.841 + ] + }, + "baseline_median_ms": 17.924, + "changed_median_ms": 17.822, + "time_reduction_percent": 0.569069404150857 + } + } +} diff --git a/Tools/Benchmarks/ContactPool.md b/Tools/Benchmarks/ContactPool.md new file mode 100644 index 0000000000..01c150c8fb --- /dev/null +++ b/Tools/Benchmarks/ContactPool.md @@ -0,0 +1,82 @@ +# Rejected contact storage pool + +`ContactPool.patch` preserves the tested candidate for `AtomGroup::Travel`. +It replaces the contact map and nested vectors with standard PMR containers and a thread-local `unsynchronized_pool_resource`. +The routine still clears the map before each call and uses the existing insertion and iteration operations. +`PushTravel` remains unchanged. +The accepted callback optimization does not include this candidate. + +## Corrected results + +`ContactPool-results.json` contains every sample from three alternating process pairs. +Each process records seven samples per fixture, with 4096 calls per sample. +The benchmark calls the compiled production routines through `CollisionCallbacks.cpp`. +Reset positions use external storage, and a size assertion prevents allocation through an undersized inherited entity pool. +All paired physics hashes and response counts match. +Allocation counts come from separate passes and match across all three runs. + +Measurements use an Apple M3 Pro, GCC 15.2, C++20, and `-O3`. +Both links use the original `MovableObject.cpp`, `Matrix.cpp`, and `MOSprite.cpp` versions from baseline `41e6b7010817936e09d2929330b7519852a1fcca`. +Times are medians across 21 samples. + +| Fixture | Baseline ms | PMR ms | Time reduction | Baseline allocations | PMR allocations | +| --- | ---: | ---: | ---: | ---: | ---: | +| Dense, one body | 443.401 | 440.546 | 0.64% | 3,190,671 | 2,999,573 | +| Dense, eight bodies | 562.280 | 560.888 | 0.25% | 3,765,537 | 2,885,595 | +| Dense, 32 bodies | 822.813 | 823.772 | -0.12% | 4,719,757 | 2,699,957 | +| Terrain | 134.962 | 132.711 | 1.67% | 2,406,712 | 2,406,712 | +| Empty space | 58.366 | 57.145 | 2.09% | 0 | 0 | +| Push, terrain | 30.883 | 30.891 | -0.03% | 36,864 | 36,864 | +| Push, empty space | 17.924 | 17.822 | 0.57% | 0 | 0 | + +The pool reduces dense allocation counts by 6–43%, but dense CPU differences remain smaller than the empty-space control difference. +The densest fixture becomes slightly slower. +These results do not support a useful CPU improvement, so the production patch excludes this candidate. +Original measurements from the undersized fixture remain invalid and are not included here. + +## Source provenance + +The recovered patch reconstructs the original saved candidate, with the resource named `hitMOStorage` in `Travel` only. +A rebuild uses the original release flags and maps the temporary source path to the original translation unit path. +Every Mach-O section has byte-identical contents and relocations between the recovered and saved objects. +This includes all three instruction sections, with 61,984 instruction bytes in total. +`ContactPool-provenance.json` records section hashes and both object hashes. + +The complete object files differ, and their symbol tables differ. +This check establishes section contents and relocation parity, not complete container identity. +The corrected measurements above used the saved candidate object. +The reproduction runner completed a build-only check and produced the same section contents and relocations. +The source reconstruction required no additional timing runs. + +## Reproduce + +Create a release build with the normal engine dependencies. +Run the following command from the repository root: + +```sh +python3 Tools/Benchmarks/ContactPool.py build /tmp/contact-pool \ + --baseline-ref 41e6b7010817936e09d2929330b7519852a1fcca \ + --runs 3 --calls 4096 +``` + +On macOS, expose the FMOD library before you run the command: + +```sh +export DYLD_LIBRARY_PATH="$PWD/external/lib/macos" +``` + +The runner applies the patch to a temporary baseline source and compiles both versions with the same flags. +It also compiles the corrected fixture and the baseline callback implementation. +The repository's production files remain unchanged. +The output directory contains generated sources, objects, executables, raw logs, and `summary.json`. +The runner requires equal physics hashes and response counts. + +For a build with other local optimizations, supply the pristine objects in both links: + +```sh +python3 Tools/Benchmarks/ContactPool.py /path/to/build /tmp/contact-pool \ + --baseline-ref 41e6b7010817936e09d2929330b7519852a1fcca \ + --replace-object Source_System_Matrix.cpp.o /path/to/baseline-Matrix.cpp.o \ + --replace-object Source_Entities_MOSprite.cpp.o /path/to/baseline-MOSprite.cpp.o \ + --runs 3 --calls 4096 +``` diff --git a/Tools/Benchmarks/ContactPool.patch b/Tools/Benchmarks/ContactPool.patch new file mode 100644 index 0000000000..49dc544cd9 --- /dev/null +++ b/Tools/Benchmarks/ContactPool.patch @@ -0,0 +1,10 @@ +--- a/Source/Entities/AtomGroup.cpp ++++ b/Source/Entities/AtomGroup.cpp +@@ -7,0 +8,2 @@ ++ ++#include +@@ -358 +360,3 @@ +- thread_local std::unordered_map> hitMOAtoms; ++ // Keep the usual map insertion/iteration order while recycling nodes and atom buffers after clear(). ++ thread_local std::pmr::unsynchronized_pool_resource hitMOStorage; ++ thread_local std::pmr::unordered_map> hitMOAtoms{&hitMOStorage}; diff --git a/Tools/Benchmarks/ContactPool.py b/Tools/Benchmarks/ContactPool.py new file mode 100644 index 0000000000..28a86b4ce2 --- /dev/null +++ b/Tools/Benchmarks/ContactPool.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +import argparse +import json +import pathlib +import shlex +import statistics +import subprocess + +parser = argparse.ArgumentParser(description='Build the rejected Travel contact pool experiment with compiled engine objects.') +parser.add_argument('build_directory', type=pathlib.Path) +parser.add_argument('output_directory', type=pathlib.Path) +parser.add_argument('--baseline-ref', required=True) +parser.add_argument('--replace-object', nargs=2, action='append', default=[], metavar=('NAME', 'PATH')) +parser.add_argument('--runs', type=int, default=0, help='Run this many alternating baseline/changed pairs after compilation.') +parser.add_argument('--calls', type=int, default=4096) +args = parser.parse_args() +build = args.build_directory.resolve() +output = args.output_directory.resolve() +output.mkdir(parents=True, exist_ok=True) +source = pathlib.Path(__file__).with_name('CollisionCallbacks.cpp').resolve() +repository = source.parents[2] +commands = json.loads((build / 'compile_commands.json').read_text()) +command = shlex.split(next(x['command'] for x in commands if x['file'].endswith('AtomGroup.cpp'))) +command = command[:command.index('-MD')] +# Compile against the engine headers without the generated precompiled header. +command = [arg for arg in command if arg not in {'-ICortexCommand.p', '-fpch-preprocess'}] +command = ['-I' + str(repository / arg[5:]) if arg.startswith('-I../Source') else arg for arg in command] +index = command.index('-include') +del command[index:index + 2] +benchmark_object = str(output / 'CollisionCallbacks.o') +subprocess.run(command + ['-c', str(source), '-o', benchmark_object], cwd=build, check=True) +baseline_movable = output / 'MovableObject-baseline.cpp' +baseline_movable.write_bytes(subprocess.check_output( + ['git', 'show', args.baseline_ref + ':Source/Entities/MovableObject.cpp'], cwd=repository)) +movable_object = str(output / 'MovableObject-baseline.o') +forced_header = ['-include', str(repository / 'Source/System/StandardIncludes.h')] +subprocess.run(command + forced_header + ['-c', str(baseline_movable), '-o', movable_object], + cwd=build, check=True) +baseline_source = output / 'AtomGroup-baseline.cpp' +baseline_source.write_bytes(subprocess.check_output( + ['git', 'show', args.baseline_ref + ':Source/Entities/AtomGroup.cpp'], cwd=repository)) +changed_source = output / 'AtomGroup-changed.cpp' +changed_source.write_bytes(baseline_source.read_bytes()) +subprocess.run(['patch', '-s', str(changed_source), str(source.with_name('ContactPool.patch'))], check=True) +link = subprocess.check_output(['ninja', '-C', str(build), '-t', 'commands', 'CortexCommand'], text=True).splitlines()[-1] +for variant, engine_source in [('baseline', baseline_source), ('changed', changed_source)]: + engine_object = str(output / ('AtomGroup-' + variant + '.o')) + # Map the generated source path to the original translation unit's path. + path_map = '-ffile-prefix-map=' + str(engine_source) + '=' + str(repository / 'Source/Entities/AtomGroup.cpp') + subprocess.run(command + forced_header + [path_map, '-c', str(engine_source), '-o', engine_object], + cwd=build, check=True) + link_command = shlex.split(link) + link_command[link_command.index('-o') + 1] = str(output / variant) + replacements = [('Source_Main.cpp.o', benchmark_object), + ('Source_Entities_MovableObject.cpp.o', movable_object), + ('Source_Entities_AtomGroup.cpp.o', engine_object)] + args.replace_object + for name, replacement in replacements: + link_command[link_command.index('CortexCommand.p/' + name)] = str(pathlib.Path(replacement).resolve()) + subprocess.run(link_command, cwd=build, check=True) + +results = {'baseline_ref': args.baseline_ref, 'experiment': 'Travel PMR pool, corrected fixed-size fixture', 'calls_per_sample': args.calls, 'runs': args.runs, 'modes': {}} +for run in range(args.runs): + for variant in (['baseline', 'changed'] if run % 2 == 0 else ['changed', 'baseline']): + log = subprocess.check_output([str(output / variant), str(args.calls)], text=True) + (output / (variant + '-run' + str(run + 1) + '.txt')).write_text(log) + for line in log.splitlines(): + row = dict(item.split('=', 1) for item in line.split()) + mode = results['modes'].setdefault(row['mode'], {}) + entry = mode.setdefault(variant, {'behavior_hashes': [], 'responses': [], 'allocations': [], 'bytes': [], 'ms': []}) + for key in entry: + field = 'behavior_hash' if key == 'behavior_hashes' else key + if field in row: + entry[key].append(float(row[field]) if key == 'ms' else int(row[field])) +for mode, values in results['modes'].items(): + baseline, changed = values['baseline'], values['changed'] + assert baseline['behavior_hashes'] == changed['behavior_hashes'], mode + ': outcome or collision order differs' + assert baseline['responses'] == changed['responses'], mode + ': response count differs' + values['baseline_median_ms'] = statistics.median(baseline['ms']) + values['changed_median_ms'] = statistics.median(changed['ms']) + values['time_reduction_percent'] = 100 * (1 - values['changed_median_ms'] / values['baseline_median_ms']) + print(mode, 'baseline_ms=', round(values['baseline_median_ms'], 3), + 'changed_ms=', round(values['changed_median_ms'], 3), + 'time_reduction_percent=', round(values['time_reduction_percent'], 2)) +if args.runs: + (output / 'summary.json').write_text(json.dumps(results, indent=2) + '\n')