-
Notifications
You must be signed in to change notification settings - Fork 450
feat: Extract Metrics from Network Pipeline Stage #1583
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
You probably want to reset the counters in the |
|
||
|
||
//TODO: Get the correct connection instead of m_ServerConnection | ||
m_Driver.GetPipelineBuffers(m_LastSendPipeline, NetworkPipelineStageCollection.GetStageId(typeof(NetworkMetricsPipelineStage)), |
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.
What is the reason for doing this only for the last pipeline sent to? There could have been sends on multiple pipelines between two Update
calls.
Also for reference, every pipeline gets its own buffers. In UnityTransport
there are three pipelines defined (m_UnreliableFragmentedPipeline
, m_UnreliableSequencedFragmentedPipeline
, and m_ReliableSequencedFragmentedPipeline
). They'll each have their own network metrics context. So basically every connection will get three different metrics contexts.
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.
Oh, I was under the impression it was a single pipeline. That's good to know.
{ | ||
var networkConnection = ParseClientId(transportClientId); | ||
|
||
//Two pipelines, unreliable sequence and reliable fragmented |
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.
There are actually 3 pipelines now. I added another one for unreliable (non-sequenced) delivery. You'll also need the change from PR #1569 for it to work properly with the metrics pipeline stage. (I guess you could just apply the changes to this PR and I'll close the other 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.
I'll apply the change. It makes sense. Will fetch the last develop with the third pipeline.
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.
Comment needs updated (or maybe just removed?)
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 don't see that the change from #1569 has been applied to this branch. Without it, trying to access the metrics pipeline buffers from a development will fail.
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 would just remove the comment. Better to have this kind of thing in an enum than a comment.
@@ -517,13 +517,67 @@ private void Update() | |||
{ | |||
; | |||
} | |||
|
|||
#if MULTIPLAYER_TOOLS | |||
if (NetworkManager.Singleton.IsServer) |
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.
Might be worth breaking into a named function to avoid overloading Update
{ | ||
var pipeline = i == 0 ? m_UnreliableSequencedPipeline : m_ReliableSequencedFragmentedPipeline; | ||
|
||
#if ENABLE_UNITY_COLLECTIONS_CHECKS |
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.
Is this an existing pattern? Definitely gets the job done, a little awkward though
var networkConnection = ParseClientId(transportClientId); | ||
|
||
//Two pipelines, unreliable sequence and reliable fragmented | ||
for (byte i = 0; i < 2; ++i) |
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 2
be a const instead of leaving the comment?
If there's only 2 pipelines, should this just be two explicit function calls and pass in the pipeline as a parameter? The for loop seems kind of unnecessarily complex control flow since we have a very small and const-size set of pipelines that aren't actually in a collection that we can iterate over.
#endif | ||
{ | ||
//Don't need to dispose of the buffers, they are filled with data pointers. | ||
m_Driver.GetPipelineBuffers(pipeline, NetworkPipelineStageCollection.GetStageId(typeof(NetworkMetricsPipelineStage)), |
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.
nit, suggest breaking into 1 line per parameter for better readability
#if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
catch (InvalidOperationException e) | ||
{ | ||
//Can ignore, may happen if a pipeline is not used |
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.
Concerned about this - we ignore the exception when ENABLE_UNITY_COLLECTION_CHECKS is enabled, but when it's not enabled it will throw right? What happens when it throws?
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.
This gets throwned only if this is defined.
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.
Ohh I see. That's worth a comment I think to avoid others getting confused the same (if you keep the try/catch at least, I know simon has called it into question in another comment)
var pipeline = i == 0 ? m_UnreliableSequencedPipeline : m_ReliableSequencedFragmentedPipeline; | ||
|
||
#if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
try |
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.
Is the try-catch to ignore the exception really necessary? It seems to me the only way to raise InvalidOperationException
from GetPipelineBuffers
is if the connection is invalid. In the rest of the adapter we usually don't bother catching that one and assume our connections are always valid.
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 my early test, it was. I have changed some stuff around, so might not be anymore. Will test.
{ | ||
//Don't need to dispose of the buffers, they are filled with data pointers. | ||
m_Driver.GetPipelineBuffers(pipeline, NetworkPipelineStageCollection.GetStageId(typeof(NetworkMetricsPipelineStage)), | ||
networkConnection, out var readProcessingBuffer, out var writeProcessingBuffer, out var sharedBuffer); |
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.
Minor, but you can explicitly ignore/discard some out
parameters by passing out _
.
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.
generally looks good to me — I'd like to defer to Beck & Simon.
@@ -161,6 +161,7 @@ public struct ConnectionAddressData | |||
private NetworkPipeline m_UnreliableFragmentedPipeline; | |||
private NetworkPipeline m_UnreliableSequencedFragmentedPipeline; | |||
private NetworkPipeline m_ReliableSequencedFragmentedPipeline; | |||
private const byte k_PipelineCount = 3; //Represented the number of existing pipeline |
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.
Any particular reason to use byte
for this constant?
var networkConnection = ParseClientId(transportClientId); | ||
|
||
//Two pipelines, unreliable sequence and reliable fragmented | ||
for (byte i = 0; i < k_PipelineCount; ++i) |
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.
Why are you using byte
as the type of the loop variable? Most likely the machine doesn't have registers that small in any case, and this will just result in greater risk of overflow without reducing resource usage 🤔
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 defer to @simon-lemay-unity for this. It does represent the small value well enough and I'm fine with it.
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 don't really have any strong opinion on the matter. Usually it's advised to use register-sized variables for loop counters since that's what will be fastest. But here it's a loop with 3 iterations, so unlikely to have any effect. If only for consistency with other loops in the code I'd go with an int
.
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.
Still not sure I understand why we have a loop in the first place. Why isn't it just:
ProcessPipeline(m_UnreliableFragmentedPipeline);
ProcessPipeline(m_UnreliableSequencedFragmentedPipeline);
ProcessPipeline(m_ReliableSequencedFragmentedPipeline);
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.
Because I forgot your comment and this makes a lot more sense.
private NetworkPipeline GetPipelineFromIndex(byte index) | ||
{ | ||
switch (index) | ||
{ | ||
case 0: | ||
return m_UnreliableFragmentedPipeline; | ||
case 1: | ||
return m_UnreliableSequencedFragmentedPipeline; | ||
case 2: | ||
return m_ReliableSequencedFragmentedPipeline; | ||
default: | ||
throw new InvalidOperationException("Invalid Pipeline Index"); | ||
} | ||
} |
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 think this would be better using an enum, rather than cases with the raw integer literals. I added a comment with an enum that could be used above on the k_PipelineCount
constant.
@@ -161,6 +161,7 @@ public struct ConnectionAddressData | |||
private NetworkPipeline m_UnreliableFragmentedPipeline; | |||
private NetworkPipeline m_UnreliableSequencedFragmentedPipeline; | |||
private NetworkPipeline m_ReliableSequencedFragmentedPipeline; | |||
private const byte k_PipelineCount = 3; //Represented the number of existing pipeline |
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 think it would be best to have an enum for this, rather than referring to these stages using raw numbers in the switch statement later. Something like:
enum PipelineType
{
UnreliableFragmented,
UnreliableSequenceFragmented,
ReliableSequenceFragmented,
Count,
}
This would replace count with (int)PipelineType.Count
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. I just wanted something simple in case things pipeline would change but you're right.
//Connection 0 when host is self and bypass the transport so would be invalid, skip it. | ||
if (ngoConnectionId > 0 || (ngoConnectionId == 0 && !NetworkManager.Singleton.IsHost)) | ||
{ | ||
ExtractNetworkMetricsForClient(NetworkManager.Singleton.ClientIdToTransportId(ngoConnectionId)); | ||
} |
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.
Rather than explaining the logic in a comment, you could write something like the following:
var isConnectionToSelf = ngoConnectionId == 0 && NetworkManager.Singleton.IsHost;
if (isConnectionToSelf)
{
// Connection 0 when host is self bypasses the transport, so skip it.
continue;
}
ExtractNetworkMetricsForClient(NetworkManager.Singleton.ClientIdToTransportId(ngoConnectionId));
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.
This also reduces the complexity of the condition a bit.
public void TrackPacketSent(uint packetCount) | ||
{ | ||
} | ||
|
||
public void TrackPacketReceived(uint packetCount) | ||
{ | ||
} | ||
|
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.
Is this waiting for some changes in the tools package?
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.
This will be part of the proper JIRA Ticket that dispatch it. With tests and all. It's there just to make sure we have functions.
Move the data from pipeline stage to UnityTransport adapter and network metrics.
MTT-1855
Testing and Documentation