Skip to content

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

Merged
merged 5 commits into from
Jul 19, 2021

Conversation

jeffreyrainy
Copy link
Contributor

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.


namespace MLAPI
{
internal class ClientRtt
Copy link
Contributor Author

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();
Copy link
Contributor

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?

Copy link
Contributor

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)
Copy link
Contributor

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 :)

Copy link
Contributor Author

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?

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor

@mattwalsh-unity mattwalsh-unity left a 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));
Copy link
Contributor

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()
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

@0xFA11 0xFA11 Jul 17, 2021

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
Copy link
Contributor

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)
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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 ? 👀

Copy link
Contributor

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.

Copy link
Contributor

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 ? 👀

Copy link
Contributor Author

@jeffreyrainy jeffreyrainy Jul 19, 2021

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.

Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Contributor

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

Copy link
Contributor

@LukeStampfli LukeStampfli left a 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.

@jeffreyrainy jeffreyrainy changed the title Experimental/rtt computation snapshot feat: snapshot. Adding RTT computation API. Jul 19, 2021
{
var snapshot = new SnapshotSystem();
var client1 = snapshot.Rtt(0);
var iterationCount = 200;
Copy link
Contributor

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 ?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var iterationCount = 200;
var iterationCount = k_RingSize * 3;

Copy link
Contributor Author

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?

Copy link
Contributor

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;
Copy link
Contributor

@mattwalsh-unity mattwalsh-unity Jul 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var extraCount = 100;
var extraCount = iterationCound / 2;

@jeffreyrainy jeffreyrainy merged commit fa2be6f into develop Jul 19, 2021
@jeffreyrainy jeffreyrainy deleted the experimental/rtt-computation-snapshot branch July 19, 2021 22:07
SamuelBellomo added a commit that referenced this pull request Jul 23, 2021
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants