perf: Prediction.CorrectHistory removed O(N) insertion. adjusting successive values is enough.

This commit is contained in:
mischa 2024-03-18 15:59:36 +08:00 committed by MrGadget
parent 1d004dcd21
commit a3e6dd3cb5
3 changed files with 39 additions and 30 deletions

View File

@ -117,10 +117,15 @@ public static T CorrectHistory<T>(
afterIndex -= 1; // we removed the first value so all indices are off by one now afterIndex -= 1; // we removed the first value so all indices are off by one now
} }
// insert the corrected state into the history, or overwrite if already exists // PERFORMANCE OPTIMIZATION: avoid O(N) insertion, only readjust all values after.
// SortedList insertions are O(N)! // the end result is the same since after.delta and after.position are both recalculated.
history[corrected.timestamp] = corrected; // it's technically not correct if we were to reconstruct final position from 0..after..end but
afterIndex += 1; // we inserted the corrected value before the previous index // we never do, we only ever iterate from after..end!
//
// insert the corrected state into the history, or overwrite if already exists
// SortedList insertions are O(N)!
// history[corrected.timestamp] = corrected;
// afterIndex += 1; // we inserted the corrected value before the previous index
// the entry behind the inserted one still has the delta from (before, after). // the entry behind the inserted one still has the delta from (before, after).
// we need to correct it to (corrected, after). // we need to correct it to (corrected, after).

View File

@ -20,3 +20,4 @@ Predicted:
2024-03-14: 590 FPS Client, 1700 FPS Server // UpdateGhosting() every 4th frame 2024-03-14: 590 FPS Client, 1700 FPS Server // UpdateGhosting() every 4th frame
2024-03-14: 615 FPS Client, 1700 FPS Server // predictedRigidbodyTransform.GetPositionAndRotation() 2024-03-14: 615 FPS Client, 1700 FPS Server // predictedRigidbodyTransform.GetPositionAndRotation()
2024-03-15: 625 FPS Client, 1700 FPS Server // Vector3.MoveTowardsCustom() 2024-03-15: 625 FPS Client, 1700 FPS Server // Vector3.MoveTowardsCustom()
2024-03-18: 628 FPS Client, 1700 FPS Server // removed O(N) insertion from CorrectHistory()

View File

@ -160,8 +160,10 @@ public void CorrectHistory()
const int historyLimit = 32; const int historyLimit = 32;
Prediction.CorrectHistory(history, historyLimit, correction, before, after, afterIndex); Prediction.CorrectHistory(history, historyLimit, correction, before, after, afterIndex);
// there should be 4 initial + 1 corrected = 5 entries now // PERFORMANCE OPTIMIZATION: nothing is inserted anymore, values are only adjusted.
Assert.That(history.Count, Is.EqualTo(5)); // there should be 4 initial + 1 corrected = 5 entries now
// Assert.That(history.Count, Is.EqualTo(5));
Assert.That(history.Count, Is.EqualTo(4));
// first entry at t=0 should be unchanged, since we corrected after that one. // first entry at t=0 should be unchanged, since we corrected after that one.
Assert.That(history.Keys[0], Is.EqualTo(0)); Assert.That(history.Keys[0], Is.EqualTo(0));
@ -181,16 +183,17 @@ public void CorrectHistory()
Assert.That(history.Values[1].angularVelocity.x, Is.EqualTo(1)); Assert.That(history.Values[1].angularVelocity.x, Is.EqualTo(1));
Assert.That(history.Values[1].angularVelocityDelta.x, Is.EqualTo(1)); Assert.That(history.Values[1].angularVelocityDelta.x, Is.EqualTo(1));
// third entry at t=1.5 should be the received state. // PERFORMANCE OPTIMIZATION: nothing is inserted anymore, values are only adjusted.
// absolute values should be the correction, without any deltas since // third entry at t=1.5 should be the received state.
// server doesn't send those and we don't need them. // absolute values should be the correction, without any deltas since
Assert.That(history.Keys[2], Is.EqualTo(1.5)); // server doesn't send those and we don't need them.
Assert.That(history.Values[2].position.x, Is.EqualTo(1.6f).Within(0.001f)); // Assert.That(history.Keys[2], Is.EqualTo(1.5));
Assert.That(history.Values[2].positionDelta.x, Is.EqualTo(0)); // Assert.That(history.Values[2].position.x, Is.EqualTo(1.6f).Within(0.001f));
Assert.That(history.Values[2].velocity.x, Is.EqualTo(1.6f).Within(0.001f)); // Assert.That(history.Values[2].positionDelta.x, Is.EqualTo(0));
Assert.That(history.Values[2].velocityDelta.x, Is.EqualTo(0)); // Assert.That(history.Values[2].velocity.x, Is.EqualTo(1.6f).Within(0.001f));
Assert.That(history.Values[2].angularVelocity.x, Is.EqualTo(1.6f).Within(0.001f)); // Assert.That(history.Values[2].velocityDelta.x, Is.EqualTo(0));
Assert.That(history.Values[2].angularVelocityDelta.x, Is.EqualTo(0)); // Assert.That(history.Values[2].angularVelocity.x, Is.EqualTo(1.6f).Within(0.001f));
// Assert.That(history.Values[2].angularVelocityDelta.x, Is.EqualTo(0));
// fourth entry at t=2: // fourth entry at t=2:
// delta was from t=1.0 @ 1 to t=2.0 @ 2 = 1.0 // delta was from t=1.0 @ 1 to t=2.0 @ 2 = 1.0
@ -198,25 +201,25 @@ public void CorrectHistory()
// the delta at t=1.5 would've been 0.5. // the delta at t=1.5 would've been 0.5.
// => the inserted position is at t=1.6 // => the inserted position is at t=1.6
// => add the relative delta of 0.5 = 2.1 // => add the relative delta of 0.5 = 2.1
Assert.That(history.Keys[3], Is.EqualTo(2.0)); Assert.That(history.Keys[2], Is.EqualTo(2.0));
Assert.That(history.Values[3].position.x, Is.EqualTo(2.1).Within(0.001f)); Assert.That(history.Values[2].position.x, Is.EqualTo(2.1).Within(0.001f));
Assert.That(history.Values[3].positionDelta.x, Is.EqualTo(0.5).Within(0.001f)); Assert.That(history.Values[2].positionDelta.x, Is.EqualTo(0.5).Within(0.001f));
Assert.That(history.Values[3].velocity.x, Is.EqualTo(2.1).Within(0.001f)); Assert.That(history.Values[2].velocity.x, Is.EqualTo(2.1).Within(0.001f));
Assert.That(history.Values[3].velocityDelta.x, Is.EqualTo(0.5).Within(0.001f)); Assert.That(history.Values[2].velocityDelta.x, Is.EqualTo(0.5).Within(0.001f));
Assert.That(history.Values[3].angularVelocity.x, Is.EqualTo(2.1).Within(0.001f)); Assert.That(history.Values[2].angularVelocity.x, Is.EqualTo(2.1).Within(0.001f));
Assert.That(history.Values[3].angularVelocityDelta.x, Is.EqualTo(0.5)); Assert.That(history.Values[2].angularVelocityDelta.x, Is.EqualTo(0.5));
// fifth entry at t=3: // fifth entry at t=3:
// client moved by a delta of 1 here, and that remains unchanged. // client moved by a delta of 1 here, and that remains unchanged.
// absolute position was 3.0 but if we apply the delta of 1 to the one before at 2.1, // absolute position was 3.0 but if we apply the delta of 1 to the one before at 2.1,
// we get the new position of 3.1 // we get the new position of 3.1
Assert.That(history.Keys[4], Is.EqualTo(3.0)); Assert.That(history.Keys[3], Is.EqualTo(3.0));
Assert.That(history.Values[4].position.x, Is.EqualTo(3.1).Within(0.001f)); Assert.That(history.Values[3].position.x, Is.EqualTo(3.1).Within(0.001f));
Assert.That(history.Values[4].positionDelta.x, Is.EqualTo(1.0).Within(0.001f)); Assert.That(history.Values[3].positionDelta.x, Is.EqualTo(1.0).Within(0.001f));
Assert.That(history.Values[4].velocity.x, Is.EqualTo(3.1).Within(0.001f)); Assert.That(history.Values[3].velocity.x, Is.EqualTo(3.1).Within(0.001f));
Assert.That(history.Values[4].velocityDelta.x, Is.EqualTo(1.0).Within(0.001f)); Assert.That(history.Values[3].velocityDelta.x, Is.EqualTo(1.0).Within(0.001f));
Assert.That(history.Values[4].angularVelocity.x, Is.EqualTo(3.1).Within(0.001f)); Assert.That(history.Values[3].angularVelocity.x, Is.EqualTo(3.1).Within(0.001f));
Assert.That(history.Values[4].angularVelocityDelta.x, Is.EqualTo(1.0).Within(0.001f)); Assert.That(history.Values[3].angularVelocityDelta.x, Is.EqualTo(1.0).Within(0.001f));
} }
} }
} }