-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Conversation
/azp run |
Azure Pipelines successfully started running 4 pipeline(s). |
How should I read the table? It seems to read that "fix" is slower (160,548,952.9 vs 31,173,248.4) |
The interesting case is GetHostAddresses/manicka-pc. ibm.com will depend on external servers and results will be unreliable. We start measuring them as bulk compassion to local processing. |
ConvertInAddrToByteArray(addressList->Address, NUM_BYTES_IN_IPV4_ADDRESS, &inetSockAddr->sin_addr); | ||
addressList->IsIPv6 = 0; | ||
++addressList; | ||
continue; |
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 use continue;
in both of these blocks rather than just making the next if into an else if?
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 like the symmetricity of it and I'm not a fan of if {} else if {}
without final else {}
. However, if you think that if {} else if {}
is generally more readable I'll gladly change 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.
seems like the block are very similar. You may get away with ternary operator in few places where it differs.
@@ -70,19 +70,15 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool | |||
// is likely to involve extra allocations, hashing, etc., and so will probably be more expensive than | |||
// this one in the typical (short list) case. | |||
|
|||
var nativeAddresses = new Interop.Sys.IPAddress[hostEntry.IPAddressCount]; | |||
Interop.Sys.IPAddress* nativeAddresses = stackalloc Interop.Sys.IPAddress[(int)hostEntry.AddressCount]; |
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.
Can we use spans instead of adding unsafe code?
Also, how do we know that the AddressCount will be small enough for this to go on the stack?
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.
Can we use spans instead of adding unsafe code?
I agree we shouldn't be needing to add unsafe code anymore now that Span<T>
and friends are available.
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 was a mistake, I lost some changes somewhere in between the multiple repo clones I have. Here should have been Span
and lower Slice.IndexOf
.
Anyway, I've changed it back to dynamic allocation. According to DNS RFC, it may use TCP (ch 4.2.2) to transmit the data, thus effectively bypassing the UDP limit for the datagram size (ch 2.3.4). So I cannot safely say that it will be small enough to fit on the stack.
src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs
Outdated
Show resolved
Hide resolved
@danmosemsft
I updated the PR summary, picked the important numbers and added explanation for the complete benchmark tables. |
/azp run |
Azure Pipelines successfully started running 4 pipeline(s). |
@stephentoub Could you please explicitly state whether you are OK with the performance regression brought by this change? I've updated the description to be more readable:
|
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.
few comment. generally looks good.
ConvertInAddrToByteArray(addressList->Address, NUM_BYTES_IN_IPV4_ADDRESS, &inetSockAddr->sin_addr); | ||
addressList->IsIPv6 = 0; | ||
++addressList; | ||
continue; |
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.
seems like the block are very similar. You may get away with ternary operator in few places where it differs.
} | ||
#if HAVE_GETIFADDRS | ||
char name[MAX_HOST_NAME + 1]; | ||
result = gethostname((char*)name, MAX_HOST_NAME); |
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 will call this for every lookup, right? e.g. we will slow down common case when trying to resolve names other than ours. Since it is unlikely this will change during process run it may be worth of some caching.
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.
The same behavior, without caching, is on Mono.
Functional correctness is more important than performance. So the question is really, is this change necessary for functional correctness? What user scenarios break if we don't do it? If it is necessary, are there use cases where the perf hit measurably impacts real usage? e.g. if someone is doing microservices between processes on the same machine and for some reason ends up creating a new connection for each request, will this show up? |
/azp run |
Azure Pipelines successfully started running 4 pipeline(s). |
I think the perf hit is reasonable here.
If you're doing something like this, it seems like you'd probably use "localhost" or loopback addresses rather than the actual host name. |
# Conflicts: # src/Native/Unix/System.Native/pal_networking.c
@wfurt @stephentoub Could any of you please either do another round of review or approve this? |
src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs
Outdated
Show resolved
Hide resolved
/azp run |
Azure Pipelines successfully started running 4 pipeline(s). |
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.
LGTM
…/corefx#41764) Unifies behavior of GetHostAddresses for local host name on Linux with Windows and Mono versions by adding the result of getifaddrs. Commit migrated from dotnet/corefx@d8cfa3b
As per discussion in the #32611, for local host name return also
getifaddrs
IPs and not justgetaddrinfo
ones.The current fix has been benchmarked against the master.
The fixed version is ~40us slower. This is an intentional performance regression in order to make the behavior more consistent across OSes.
Fixes #32611
Complete numbers
Explanation
The original
GetHostAddresses
only calledgetaddrinfo
which essentially either takes data frometc/hosts
(getaddrinfo
for microsoft.com) or does a DNS query (getaddrinfo
for ibm.com). You can clearly see the performance difference in between those two (10us vs 100ms). For local hostgetaddrinfo
usually returned only the single IP listed inetc/hosts
, which was the main difference between Windows and Linux version (see original issue #32611). Yes, you can manually add more addresses to the file and they will be returned. I guess it can do a DNS query for local host name as well depending on the nsswitch.conf. Tough the proposed solution was to add the IP addresses of all network interfaces (viagetifaddrs
) ifGetHostAddresses
is called for the local host name. So the performance regression is premeditated and has been measured in the original issue. This can be seen on the line withGetHostAddresses
for manicka-pc, the number there shows that the method, in that particular case, has slowed and the difference corresponds to the system call ofgetiffaddrs
(3rd line from the top).I also ran PerfCollect on the fixed version and did direct time measurement with
Stopwatch
on the master and fixed version and haven't found any obvious performance problems. Actually, the fixed version was a bit faster in theParseHostEntry
. Probably since it doesn't fetch and convert IP address one by one through interop, but rather get's them all at once.The fix:
The master: