-
Notifications
You must be signed in to change notification settings - Fork 450
feat: snapshot. Adding RTT computation API. #963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
||
namespace MLAPI | ||
{ | ||
internal class ClientRtt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll revise naming. Good point
[Test] | ||
public void TestRtt() | ||
{ | ||
var snapshot = new SnapshotSystem(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about
- a test that does > k_RingSize stores?
- a test that ack's something that wasn't sent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I'd rename this test to be something that communicates "send more, receive less".
also vice-versa option would be "a test that ack's something that wasn't sent?" aka "send less, receive more"
I'd also add:
- send 2x more than
k_RingSize
- randomization: out-of-order send/recv variations (if ooo is ever possible, IDK)
- randomization: sending/receiving (notify/ack) intentionally with varying artificatial latencies (WaitForSeconds)
at least, these are the test cases comes to my mind in a minute — perhaps more tests if applicable/sensible?
ret.Best = m_MeasuredLatencies[m_LatenciesBegin]; | ||
ret.Worst = m_MeasuredLatencies[m_LatenciesBegin]; | ||
|
||
while (index != m_LatenciesEnd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll bet there is a Google interview question that asks how to do this operation in constant time :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm familiar with keeping a running sum and subtracting the element that are removed at the other end of the ring buffer. Then, you can divide by the number of elements.
However, I don't know of a good way to do it for the best
and worst
entries. I believe we can only do O(log(n))
but that would require an associative container for a handful of latencies. Seemed Overkill. Is it possible your comment forgot to consider min/max latency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For min/max just keep track of the min/max and then compare < & > when adding / removing from the ring buffer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, let's say stored latencies are [10, 60, 20, 60, 11, 80 ]
, min is 10, max is 80, a new latency of 15 needs to be stored. We now have [60, 20, 60, 11, 80, 15]
(not in those positions, though). I can remove the min: 10
, I can keep the max 80
, but finding min: 11
requires a traversal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you could use 2 priority queues or heaps, one for min. But to your point we can save this for benchmarking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually good with the exception of the 2 tests I called out
if (m_SendKey[key % k_RingSize] == key) | ||
{ | ||
double latency = when - m_RttSendTimes[key % k_RingSize]; | ||
Debug.Log(string.Format("Measured latency of {0}", latency)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you don't want this checked in tho
/// <summary> | ||
/// Returns the Round-trip-time computation for this client | ||
/// </summary> | ||
public Rtt GetRtt() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess one thing to think about - how many times do you think you might call GetRtt in a given frame? if you call it a bunch, you might have a dirty flag here and usually return a cached value. OR, if you need RTT almost every frame and call this multiple times, maybe the computation of the RTT could be in the NotifyAck? I guess that depends on how often you don't actually need the RTT data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I lean this being "premature optimization" at the moment. I don't want to be cavalier about the feedback, it's good and very valid. But I'd wait until it shows in a profile. With the irons we already have in the fire, I'd wait before adding complexity here. Would that be workable to you?
} | ||
} | ||
|
||
private const int k_RttSize = 5; // number of RTT to keep an average of (plus one) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe RttCount
vs MaxRttCount
vs RttCacheSize
? "size" sounds too generic and at the first glance, I thought this was some sort of RTT packet size :P
private const int | ||
k_RingSize = 64; // number of slots to use for RTT computations (max number of in-flight packets) | ||
|
||
private double[] m_RttSendTimes; // times at which packet were sent for RTT computations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in seconds? perhaps m_RttSendTimeSecs
?
|
||
private double[] m_RttSendTimes; // times at which packet were sent for RTT computations | ||
private int[] m_SendKey; // tick (or other key) at which packets were sent (to allow matching) | ||
private double[] m_MeasuredLatencies; // measured latencies (ring buffer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seconds? :)
|
||
private ushort m_CurrentTick = 0; | ||
|
||
internal ClientRtt Rtt(ulong clientId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't call NotifySend
or NotifyAck
from anywhere yet.
are you planning to have RTT & SnapshotSystem integration in this PR too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, just saw this:
For now, those values are not filled, they will be in a future PR. But it should allow other development to proceed as it sets the API they will use.
AFAIK, we primarily wanted to have RTT for snapshots and NetVars. are there any other systems being unblocked by this PR? If not, personally, I think RTT integration with snapshot should go under one single PR — otherwise, we're not really shipping this as a feature but as a boilerplate. what would you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there is some other systems blocked by this this. Luke's work on Time System will integrate with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alright. just double-checking: without having this functioning (yet) but still having its boilerplate in-place would unblock Luke because his work on TimeSystem is not dependant to RTT calculation but merely its existence? I'm confused. @LukeStampfli ? 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So based on your feedback on the time system I got, I'm exploring using the existing time sync for the time system until this is ready @MFatihMAR. I'm not sure yet whether that will work well and for release we will want to use Jeff's system. knowing the API with which I will integrate with makes it easier for me to plan ahead.
But yeah just an API which doesnt' work yet does not "unblock" me but it allows me to push my work with the confidence that there is a high probability that I will be able to integrate the time system in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, having this "agreed in principle" would unblock you right? if so, again, I think this PR should go in with its snapshot integration/implementation in a functioning state, otherwise we're shipping a boilerplate, not a feature. I do not expect public APIs to change at this point, Luke is already unblocked from that standpoint then. what would you think @jeffreyrainy ? 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My position is unchanged. I'd still like to PR this. Thanks for raising your concerns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, fair enough.
|
||
namespace MLAPI | ||
{ | ||
internal class ClientRtt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be a struct instead of a class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed this should be a struct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implements what was discussed in the RFC so looks good to me.
{ | ||
var snapshot = new SnapshotSystem(); | ||
var client1 = snapshot.Rtt(0); | ||
var iterationCount = 200; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be in terms of k_RingSize
perhaps? (like k_RingSize * 3
?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var iterationCount = 200; | |
var iterationCount = k_RingSize * 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k_RingSize
is private, and afaik, c# doesn't have friend
. This raise a few possibilities:
- make the ring size a publicly available setting with a setter, and getter
- only expose the ring size with a readonly getter
- leave the test with hard-coded values, as-is.
Do you have a preference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have internal
in C# and internals are exposed to test asmdefs IIRC
var snapshot = new SnapshotSystem(); | ||
var client1 = snapshot.Rtt(0); | ||
var iterationCount = 200; | ||
var extraCount = 100; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var extraCount = 100; | |
var extraCount = iterationCound / 2; |
…ngSize and RttSize
…nsform * develop: feat: snapshot. Adding RTT computation API. (#963) feat: snapshot. Milestone 1b. Testproject "manual test" "scene transitioning" working with snapshot. Disabled by default. (#862) test: multiprocess tests part 6: fixing issues runnings all tests together (#957) docs: Perf tests part 5. Adding documentation and instructions (#952) test: Perf tests part 4. Adding example of performance test with spawning x network objects at once (#925) test: Correctly teardown OnNetworkSpawn/Despawn tests.
This adds the API exposed by the Snapshot System for round-trip times. For now, those values are not filled, they will be in a future PR. But it should allow other development to proceed as it sets the API they will use.