Skip to content
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

System.Net.Dns.GetHostAddresses() does not return all local ip addresses on Linux/.NET Core (it works in .NET Framework) #27534

Closed
bpress opened this issue Oct 4, 2018 · 30 comments · Fixed by dotnet/corefx#41764
Assignees
Labels
area-System.Net bug help wanted [up-for-grabs] Good issue for external contributors os-linux Linux OS (any supported distro)
Milestone

Comments

@bpress
Copy link

bpress commented Oct 4, 2018

A call to

System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName())

returns only the first local IP address when running on Linux.

Tested with .NET Core 2.1.3

Here is the repro code:

using System;
using System.Net;
using System.IO;

namespace ConsoleDnsTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string hostname = Dns.GetHostName();
            Console.WriteLine("My hostname is '{0}'", hostname);
            Console.WriteLine("My ip addresses are:");
            foreach (var address in Dns.GetHostAddresses(hostname))
                Console.WriteLine("\t{0}", address);
        }
    }
}

Here is the (wrong) output I get on my Linux Debian 9:

root@inim-test:~# dotnet ConsoleDnsTest.dll
My hostname is 'inim-test'
My ip addresses are:
	172.22.28.132
root@inim-test:~#

If I run the very same code under Mono, it works, and this is the correct output I get:

root@inim-test:~# mono ConsoleDnsTest.exe
My hostname is 'inim-test'
My ip addresses are:
	172.22.28.132
	172.22.28.133
	172.22.28.134
	172.22.28.138
	10.128.0.1
root@inim-test:~#

On Windows both .NET Core and .NET Framework versions work well.

@bpress bpress changed the title System.Net.Dns.GetHostAddresses() does not return all local ip addresses on Linux System.Net.Dns.GetHostAddresses() does not return all local ip addresses on Linux/.NET Core (works in .NET Framework) Oct 4, 2018
@bpress bpress changed the title System.Net.Dns.GetHostAddresses() does not return all local ip addresses on Linux/.NET Core (works in .NET Framework) System.Net.Dns.GetHostAddresses() does not return all local ip addresses on Linux/.NET Core (it works in .NET Framework) Oct 4, 2018
@bpress
Copy link
Author

bpress commented Oct 5, 2018

It looks like something is wrong in

Interop.Sys.GetHostEntryForName()

which in turn makes a call to

SystemNative_GetHostEntryForName

from pal_networking.c

This function makes use of the standard getaddrinfo() call, however it looks like something is wrong with this method.

I've created a small C code snippet that tries to get local ip addresses using getaddrinfo() in the same fashion as SystemNative_GetHostEntryForName(), and this piece of code gives exactly the same odd result (in corefx identical duplicated IP addresses get discarded):

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>


#define HOSTNAME_LEN 255


void main()
{
    char *my_hostname = malloc(HOSTNAME_LEN);
    struct addrinfo hint;
    struct addrinfo* info = NULL;
    int result;

    result = gethostname(my_hostname, HOSTNAME_LEN);
    if (result != 0)
    {
        printf("Error in gethostname() %d\n", result);
        return;
    }

    printf("My hostname is %s\n", my_hostname);

    memset(&hint, 0, sizeof(struct addrinfo));

    hint.ai_family = AF_UNSPEC;
    hint.ai_flags = AI_CANONNAME;

    result = getaddrinfo((const char*)my_hostname, NULL, &hint, &info);
    if (result != 0)
    {
        printf("Error in getaddrinfo() %d\n", result);
        return;
    }

    printf("My IP addresses are:\n");
    for (struct addrinfo *ai = info; ai != NULL; ai = ai->ai_next)
    {
        if (ai->ai_family != AF_INET) continue;
        printf("\t%s (0x%08X)\n", inet_ntoa(((struct sockaddr_in *)ai->ai_addr)->sin_addr), ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr);
    }
}

This is the result on my test server:

root@inim-test:~/albytest/gethostentry# ./gethostentry
My hostname is inim-test
My IP addresses are:
	172.22.28.132 (0x841C16AC)
	172.22.28.132 (0x841C16AC)
	172.22.28.132 (0x841C16AC)
root@inim-test:~/albytest/gethostentry#

I will dig further into it.

@bpress
Copy link
Author

bpress commented Oct 5, 2018

The Mono implementation of GetHostAddresses() is very different.

If the hostname passed to the function matches the local hostname, then the function ends up calling a specific internal routine which enumerates local ipaddresses through a SIOCGIFCONF ioctl call on a local socket.

I believe that the same approach should be taken here, or at least make use of getifaddr(), although I'm not sure if this call is available on any unix flavor or linux version.

@mentianyi
Copy link

GetHostXXX will get incorrect result.You can use System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces() to get ip address.

@bpress
Copy link
Author

bpress commented Nov 23, 2018

GetHostXXX will get incorrect result.You can use System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces() to get ip address.

Thanks, unfortunately in my project GetHostXXX is called inside an external library which I have no control over, I'll ask for a fix to the authors.

@SystemKeeper
Copy link

I'm seeing the same problem in .Net Core 2.2.5. Was there ever a fix for this?

@ManickaP ManickaP self-assigned this Sep 18, 2019
@ManickaP
Copy link
Member

So I'm seeing the same problem on Ubuntu WSL, however on Debian 9 (VM in Azure) it fails with:

Unhandled Exception: System.Net.Internals.SocketExceptionFactory+ExtendedSocketException: Resource temporarily unavailable
   at System.Net.Dns.InternalGetHostByName(String hostName)
   at System.Net.Dns.GetHostAddresses(String hostNameOrAddress)
   at issue_32611.Program.Main(String[] args) in /home/manicka/BugReproductions/issue_32611/Program.cs:line 14

The exception is on dotnet core 2.1.802 as well as 3.0.100 preview 9. I don't know the root cause for the exception yet.

@wfurt
Copy link
Member

wfurt commented Sep 18, 2019

This is because it depends on network configuration @ManickaP. Local hostname may or may not be in DNS. And it also depends on what DNS server you are pointing at.
According to documentation https://docs.microsoft.com/en-us/dotnet/api/system.net.dns.gethostaddresses?view=netcore-3.0#System_Net_Dns_GetHostAddresses_System_String_

The GetHostAddresses method queries the DNS subsystem for the IP addresses associated with a host name. If hostNameOrAddress is an IP address, this address is returned without querying the DNS server.

So I'm not sure if it is reasonable to expect all local addresses if they are not in DNS.
I think it works on Windows because it is part of name resolution service.

If we want to change this, I think we should take path @bpress suggested. That would make it more consistent across OSes. Also as Mono will blend in in 5.0 it would be reasonable to preserve their behavior. That should probably also work on OSX.

@wfurt
Copy link
Member

wfurt commented Sep 18, 2019

BTW do you have any recommendation here @davidsh?

@ManickaP
Copy link
Member

@wfurt Yeah you're right, it fails on Mono as well (in the Azure VM), though it gives much better message:

Unhandled Exception:
System.Net.Sockets.SocketException (0x80004005): Could not resolve host 'ManickaDebian9'
  at System.Net.Dns.Error_11001 (System.String hostName) [0x00015] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at System.Net.Dns.GetHostByName (System.String hostName) [0x00024] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at System.Net.Dns.GetHostEntry (System.String hostNameOrAddress) [0x00061] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at System.Net.Dns.GetHostAddresses (System.String hostNameOrAddress) [0x00065] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at issue_32611.Program.Main (System.String[] args) [0x0001b] in <45b1cac287a44c9296f7c1d01cb3db3c>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.Net.Sockets.SocketException (0x80004005): Could not resolve host 'ManickaDebian9'
  at System.Net.Dns.Error_11001 (System.String hostName) [0x00015] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at System.Net.Dns.GetHostByName (System.String hostName) [0x00024] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at System.Net.Dns.GetHostEntry (System.String hostNameOrAddress) [0x00061] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at System.Net.Dns.GetHostAddresses (System.String hostNameOrAddress) [0x00065] in <bd46d4d4f7964dfa9beea098499ab597>:0
  at issue_32611.Program.Main (System.String[] args) [0x0001b] in <45b1cac287a44c9296f7c1d01cb3db3c>:0

And nslookup obviously fails for the computer name as well:

;; Got SERVFAIL reply from 10.50.50.50, trying next server
Server:         10.50.10.50
Address:        10.50.10.50#53

** server can't find ManickaDebian9: SERVFAIL

So my testing environment is not exactly the same as in the original problem of the issue.

But I was able to get the same problem on WSL, so might continue testing there.

@ManickaP
Copy link
Member

WSL successful repro.
mono:

My hostname is 'MAPICHOV-SB2'
My ip addresses are:
        10.166.147.89
        172.23.240.1
        10.0.75.1

dotnet 3.0.100-rc1-014190

My hostname is 'MAPICHOV-SB2'
My ip addresses are:
        127.0.1.1

So the question now is whether we adopt the mono implementation or not?
@davidsh and @wfurt

@davidsh
Copy link
Contributor

davidsh commented Sep 19, 2019

So the question now is whether we adopt the mono implementation or not?
@davidsh and @wfurt

Are you talking about changing our implemention in .NET Core to do the below?

The Mono implementation of GetHostAddresses() is very different.
If the hostname passed to the function matches the local hostname, then the function ends up calling a specific internal routine which enumerates local ipaddresses through a SIOCGIFCONF ioctl call on a local socket.
I believe that the same approach should be taken here, or at least make use of getifaddr(), although I'm not sure if this call is available on any unix flavor or linux version.

The bottom line is that we should try to get the best and consistent behavior across all platforms (Windows, Linux, Mac). That "best" behavior appears to be to have all the loopback adapter IP addresses (i.e. 127.0.0.1) as well as the other IP addresses assigned to all network interfaces in the system.

@stephentoub @geoffkizer Comments?

@wfurt
Copy link
Member

wfurt commented Sep 19, 2019

I have did quick test on Windows 10 system with 2 interfaces. My second NIC is private network and the address is NOT in DNS. Running example above gives that IP address as well. Interestingly, IPv4 loopback (127.0.0.1) is not in the list. (nor ::1)

If we want consistent behavior, adding special case for own name makes sense to me. (instead of relying on external DNS)

And perhaps updating documentation to be explicit about it.

@davidsh
Copy link
Contributor

davidsh commented Sep 19, 2019

If we want consistent behavior, adding special case for own name makes sense to me. (instead of relying on external DNS)

FYI, Windows doesn't just use DNS to get IP addresses from names. It uses NETBIOS and LLMNR and other protocols.

@ManickaP
Copy link
Member

Please correct me if I'm wrong, but the desired behavior on all OSes is:

That "best" behavior appears to be to have all the loopback adapter IP addresses (i.e. 127.0.0.1) as well as the other IP addresses assigned to all network interfaces in the system.

Unless someone express disagreement here I'll try to look into it.

@davidsh
Copy link
Contributor

davidsh commented Sep 19, 2019

I have did quick test on Windows 10 system with 2 interfaces. My second NIC is private network and the address is NOT in DNS. Running example above gives that IP address as well. Interestingly, IPv4 loopback (127.0.0.1) is not in the list. (nor ::1)
If we want consistent behavior, adding special case for own name makes sense to me. (instead of relying on external DNS)

We should have our APIs be able return all IP addresses which include all of those bound to network interfaces as well as "127.0.0.1" and "::1" (loopback adapter addresses).

Note that on systems supporting IPv6 this will include the link-local addresses as well, i.e: from my "ipconfig /all" on my Windows machine:

fe80::24c4:1dc4:c84e:aa99%5
fe80::cc10:d2a8:c0da:21c0%17

@stephentoub
Copy link
Member

I'm theoretically fine with such a change, especially if it improves our behavior across OSes. One thing to be cautious of is performance; we'll want to pay careful attention to that, as iterating through all such adapters could be non-trivially expensive if we had to do it on every call. We might need to think about some kind of caching strategy.

@wfurt
Copy link
Member

wfurt commented Sep 19, 2019

true. But going to external DNS server is not fast either IMHO. Going through interface list will burn more CPU. We could listen to address updates but that would require background task.

@stephentoub
Copy link
Member

stephentoub commented Sep 19, 2019

But going to external DNS server is not fast either IMHO

Method Mean
GetHostAddresses 179.5 us
Adapters 25,507.2 us
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;

public class Program
{
    static void Main(string[] args) => BenchmarkSwitcher.FromTypes(new[] { typeof(Program) }).Run(args);

    [Benchmark]
    public IPAddress[] GetHostAddresses() => Dns.GetHostAddresses(Dns.GetHostName());

    [Benchmark]
    public IPAddress[] Adapters()
    {
        var addresses = new List<IPAddress>();
        foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
        {
            foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
            {
                addresses.Add(ip.Address);
            }
        }
        return addresses.ToArray();
    }
}

@wfurt
Copy link
Member

wfurt commented Sep 19, 2019

GetAllNetworkInterfaces() (and many other NetworkInfo functions) are bloated ;(
It would be interesting to see getifaddr(). But point taken.

@stephentoub
Copy link
Member

GetAllNetworkInterfaces() (and many other NetworkInfo functions) are bloated

If we can improve those, that'd be excellent.

@wfurt
Copy link
Member

wfurt commented Sep 20, 2019

You got me hooked up @stephentoub. I did run your test, and got ~ 6.5 us for GetHostAddresses. That look too good and I did not see any DNS queries. After while I discovered 127.0.1.1 Ubuntu16.localdomain Ubuntu16 in /etc/hosts. Sure enough, GetHostAddresses() would always return only 127.0.0.1 and never any real address.

I made some proof of concept and I get this on my 4core VM with 4 interfaces and 10 addresses.

Method Mean Error StdDev Median
GetHostAddresses 431.28 us 54.8231 us 158.1772 us 381.35 us
Adapters 1,138.28 us 83.3873 us 237.9086 us 1,039.53 us
GetAllNetworkInterfaces 887.36 us 12.2020 us 11.4137 us 885.88 us
getifaddrs 30.61 us 0.5903 us 0.6316 us 30.55 us

I repeated test on physical machine with 12 interfaces and 21 L3 addresses (2 physical, and bunch of Docker virtual interfaces)

Method Mean Error StdDev
GetHostAddresses 479.80 us 7.6575 us 7.1629 us
Adapters 3,679.03 us 18.5200 us 17.3236 us
GetAllNetworkInterfaces 3,665.06 us 15.8173 us 14.7955 us
getifaddrs 86.07 us 0.2354 us 0.2202 us
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System;
using System.Runtime.InteropServices;

public class Program
{

    [StructLayout (LayoutKind.Sequential)]
    internal unsafe struct sockaddr {
        internal short sa_family;
        internal short sa_port;
        internal fixed byte data[20];
        internal uint scope_id;
    }

    [StructLayout (LayoutKind.Sequential)]
    internal unsafe struct ifaddrs {
        internal readonly void *ifa_next;
        internal readonly char *ifa_name;
        internal readonly uint ifa_flags;
        internal readonly sockaddr  *ifa_addr;
    }

    [DllImport("libc", EntryPoint = "getifaddrs")]
    unsafe static extern int getifaddrs(ref void * addrs);

    [DllImport("libc")]
    unsafe static extern void freeifaddrs(void* ifa);

    static void Main(string[] args) => BenchmarkSwitcher.FromTypes(new[] { typeof(Program) }).Run(args);

    [Benchmark]
    public IPAddress[] GetHostAddresses() => Dns.GetHostAddresses(Dns.GetHostName());

    [Benchmark]
    public IPAddress[] Adapters()
    {
        var addresses = new List<IPAddress>();
        foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
        {
            foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
            {
                addresses.Add(ip.Address);
            }
        }
        return addresses.ToArray();
    }

    [Benchmark]
    public IPAddress[] GetAllNetworkInterfaces()
    {
        var addresses = new IPAddress[0];

        var list = NetworkInterface.GetAllNetworkInterfaces();

        return addresses;
    }
    [Benchmark]
    public unsafe IPAddress[] getifaddrs()
    {
        void * pinput = null;
        int ret = getifaddrs(ref pinput);
        int count = 0;

        ifaddrs * entry = (ifaddrs*)pinput;
        while (entry != null)
        {
             if (entry->ifa_addr->sa_family == 2 || entry->ifa_addr->sa_family == 10)
            {
                count ++;
            }
            entry = (ifaddrs*)entry->ifa_next;
         }

        var addresses = new IPAddress[count];

        entry = (ifaddrs*)pinput;
        count = 0;
        while (entry != null)
        {
            if (entry->ifa_addr->sa_family == 2){
                addresses[count] = new IPAddress(new ReadOnlySpan<byte>(entry->ifa_addr->data, 4));
                count++;
            }
            else if (entry->ifa_addr->sa_family == 10)
            {
                addresses[count] = new IPAddress(new ReadOnlySpan<byte>(entry->ifa_addr->data + 4, 16));
                addresses[count].ScopeId = entry->ifa_addr->scope_id;
                count++;
            }

            entry = (ifaddrs*)entry->ifa_next;
        }

        freeifaddrs(pinput);

        return addresses;
    }
}

I'll take a look if we can improve GetAllNetworkInterfaces() as that really seems to take lot of time.

@wfurt
Copy link
Member

wfurt commented Sep 20, 2019

As far as the LL and lo addresses: This can certainly surprise anybody who is using GetHostAddresses to get addresses system can be reached at. On my physical system I get

127.0.0.1
10.127.72.170
192.168.1.3
172.17.0.1
172.18.0.1
::1
2001:4898:f0:20:6651:6ff:fe5b:6bb6
fe80::6651:6ff:fe5b:6bb6%2
2000::3
fe80::a236:9fff:fe51:b80%3
fc00::1
fe80::42:daff:fed5:e06e%4
fe80::1%4
fe80::ec24:fdff:fe9d:e69b%24
fe80::f811:9ff:febc:2ab1%26
fe80::a09b:2eff:fe93:e7fe%186
fe80::42:9cff:fe2a:1051%193
fe80::f0f8:18ff:fe29:90ee%217
fe80::b88e:45ff:fe79:c330%221
fe80::8835:68ff:fec3:de55%231
fe80::ec9f:bcff:fec0:f05%233

and the link-local addresses are certainly tricky to use.

@stephentoub
Copy link
Member

stephentoub commented Sep 20, 2019

You got me hooked @stephentoub.

😄

@wfurt
Copy link
Member

wfurt commented Sep 26, 2019

we chatted about this offline with @stephentoub. It seems like it is not practical for purpose of the to make massive change to GetAllNetworkInterfaces(). It tries to get way too much in very inefficient way.
I think it make sense to proceed on this with following two caveats.

  • cross-platform consistently.
    It would make sense if this behaves similar as much as possible. There are questions about
    -- adding loopback address on Windows
    -- respecting entries from /etc/host
    -- special-casing this for own hostname. Should we do it also for FQDN? (how does mono and Windows handle this)
    -- handling link-local addresses.

  • performance
    While GetAllNetworkInterfaces() is probably not on critical path, GetHostAddresses() may for cases like HTTP or sockets. Obviously returning more addresses may be slower than returning exactly one. Also this may change based on interface count. But ideally we should be as close to old code as possible. And I think we can based on the benchmarks above.
    To make it fast, I think we will need to have dedicated code to simply get addresses. That can be call to getifaddrs() or some helper function in our native PAL layer.

  • documentation
    I think we should improve documentation and be clear about behavior. For example DNS lookup would not return link-local IPv6 or loopback. But we may in this case.

@ManickaP
Copy link
Member

ManickaP commented Sep 27, 2019

I've tested Dns.GetHostAddresses(Dns.GetHostName()) on my laptop (Windows and WSL) and my PC (Windows, WSL, Linux) on Mono, .NET Framework 4.8 (only Win), and .NET Core 3.0.
I also tested low-level C functions used underneath by .NET Core and Mono (getaddrinfo) and the proposed function (getifaddrs).

  • It consistently returned much more addresses on Windows (including link local ones).
    • It returned the same, extensive list on Mono, .NET Framework and .NET Core.
    • The list contained address of VPN virtual drivers, Hyper-V virtual drivers (Docker, WSL) as well as WiFi/ethernet.
  • On Linux or WSL, what is in '/etc/hosts/' for the local hostname was returned, on Windows the content of 'System32\drivers\etc\hosts' had no effect on the result.
  • Our doc on GetHostAddresses says:

If a host is described in the hosts file, the IP address or addresses there will be returned without querying the DNS server.

  • On Windows is used GetAddrInfoW and on linux getaddrinfo.
  • For FQDN the behavior was consistent across tested platforms/OS/computers.
  • getifaddrs on Linux/WSL returned IPv4, IPv6 and LL IPv6 of the ethernet interface.
    • On laptop, the several virtual adapters were not listed by getifaddrs, though it's most probably due to not being visible within WSL.

@wfurt: I suppose we don't want to break backward compatibility with .NET Framework and prune the list on Windows, but rather extend the list on other OSes?

If so, I'll prepare a fix leveraging getifaddrs for local hostname and benchmark it against the current behavior. Then, we can decide whether to proceed or not.

Also, I can provide the detailed results of the tests I did. I wasn't sure about sharing them here.

@wfurt
Copy link
Member

wfurt commented Sep 30, 2019

yes, I think so @ManickaP e.g. make Windows/Framework/MONO behavior on other platforms. I think flow in documentation is that it does not explain that for host it self the behavior is different and it returns addresses without querying DNS or using host file.
It seems like last prn question is if we should include loopback addresses as @davidsh suggested or not,

@ManickaP
Copy link
Member

ManickaP commented Oct 2, 2019

I have the benchmark results of using ifaddrs along with addrinfo:
Original values:

Method Mean Error StdDev
GetHostName 539.4 ns 8.367 ns 7.417 ns
GetHostAddresses 7,789.7 ns 154.651 ns 144.661 ns
Adapters 453,000.8 ns 5,149.828 ns 4,565.189 ns

New values:

Method Mean Error StdDev
GetHostName 529.9 ns 9.882 ns 9.244 ns
GetHostAddresses 52,723.5 ns 589.729 ns 492.451 ns
Adapters 463,925.0 ns 9,011.857 ns 14,293.760 ns

So originally 7,789.7 ns and after change 52,723.5 ns. I might tweak it to achieve some minor improvements but nothing magical. After all it introduces another system call so it inherently is slower... Unless you want to forgo the getaddrinfo for localhost completely, but that would completely skip data from /etc/hosts wouldn't it?

I will measure it on Windows on my machine as well that might give us some more info.

@wfurt
Copy link
Member

wfurt commented Oct 3, 2019

I did try your code @ManickaP (compared to my work-in-progress on GetAllNetworkInterfaces). I also add one more test to lookup www.mictrosoft.com to get sense how fast GetHostAddresses would be if it does not come from /etc/host. It may be worth for you to try it with and without entry matching local system.

Method Toolchain Mean Error StdDev
GetHostAddressesMS /manickap-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 14,375.87 us 1,011.1393 us 2,750.879 us
GetHostAddressesMS /wfurt-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 115,323.56 us 33,683.0393 us 86,342.507 us
GetHostAddresses /manickap-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 566.90 us 71.0313 us 203.802 us
GetHostAddresses /wfurt-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 324.28 us 29.2352 us 78.539 us
Adapters /manickap-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 931.42 us 11.7161 us 10.386 us
Adapters /wfurt-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 597.41 us 11.7829 us 10.445 us
GetAllNetworkInterfaces /manickap-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 1,075.00 us 46.3276 us 134.405 us
GetAllNetworkInterfaces /wfurt-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 606.68 us 12.1979 us 33.391 us
getifaddrs /manickap-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 32.18 us 0.6407 us 1.393 us
getifaddrs /wfurt-corefx/artifacts/bin/runtime/netcoreapp-Linux-Release-x64/corerun 32.08 us 0.6707 us 1.707 us

@ManickaP
Copy link
Member

ManickaP commented Oct 7, 2019

Another round of benchmarks (microsoft.com is in hosts file, ibm.com isn't):
Windows (see that the time unit is us i.e. micro seconds):

Method hostName Mean Error StdDev Median
GetHostName ? 130.4 us 2.587 us 5.045 us 129.1 us
Adapters ? 172,362.6 us 3,410.236 us 8,170.708 us 171,094.9 us
GetHostAddresses MAPICHOV-SB2 370.7 us 7.151 us 10.025 us 365.9 us
GetHostAddresses ibm.com 367.6 us 7.949 us 21.627 us 363.8 us
GetHostAddresses microsoft.com 427.2 us 17.243 us 49.751 us 412.6 us

Linux (WSL2) before (the time unit is ns i.e. nano seconds):

Method hostName Mean Error StdDev
GetHostName ? 527.4 ns 11.33 ns 22.63 ns
Adapters ? 985,183.5 ns 21,245.91 ns 31,799.88 ns
GetHostAddresses MAPICHOV-SB2 7,720.7 ns 200.58 ns 565.75 ns
GetHostAddresses ibm.com 31,047,740.6 ns 605,455.98 ns 594,638.73 ns
GetHostAddresses microsoft.com 9,890.8 ns 502.90 ns 1,466.98 ns

Linux (WSL2) after (the time unit is ns i.e. nano seconds):

Method hostName Mean Error StdDev Median
GetHostName ? 532.5 ns 10.64 ns 13.84 ns 527.7 ns
Adapters ? 966,544.0 ns 14,129.05 ns 12,525.03 ns 961,682.9 ns
GetHostAddresses MAPICHOV-SB2 43,159.6 ns 844.04 ns 1,263.32 ns 42,670.0 ns
GetHostAddresses ibm.com 30,397,984.9 ns 600,163.73 ns 1,505,693.46 ns 30,777,131.2 ns
GetHostAddresses microsoft.com 8,129.8 ns 324.10 ns 924.67 ns 7,756.9 ns

So my fix slowed the linux version ~6-7 times. However, the original linux version is ~40 times faster than Win and my fix is still ~8-9 times faster than win version. On the other hand, with DNS query, Linux is ~80 times slower since there's no DNS caching on OS level.

Me personally, I would proceed with the change since the numbers are still better than their Win counterparts. There's still some room for improvements (performance-wise) in my version, but nothing which would make it as fast as it originally was.

@stephentoub @wfurt I'm interested in your opinions on the numbers. Should I proceed or not?

@wfurt
Copy link
Member

wfurt commented Oct 7, 2019

I did more work on Friday on tuning GetAllNetworkInterfaces and now on my machine it is faster than GetHostAddresses from your branch @ManickaP. That does not make much sense to me as it needs to do more than just getting all address.

Method Toolchain Mean Error StdDev
GetHostAddresses manickap-corefx/corerun 600.02 us 107.0430 us 310.551 us
GetHostAddresses wfurt-corefx/corerun 394.73 us 58.2416 us 160.414 us
Adapters manickap-corefx/corerun 1,028.19 us 15.7959 us 13.190 us
Adapters wfurt-corefx/corerun 270.05 us 5.3467 us 11.042 us
GetAllNetworkInterfaces manickap-corefx/corerun 1,062.05 us 40.9401 us 112.761 us
GetAllNetworkInterfaces wfurt-corefx/corerun 256.20 us 5.0009 us 5.136 us
getifaddrs manickap-corefx/corerun 33.46 us 1.0050 us 1.535 us
getifaddrs wfurt-corefx/corerun 34.22 us 0.8659 us 2.442 us

It is obvious that some of the numbers depend on particular setup and performance of DNS server. I think it still make sense to proceed unless @stephentoub objects. One thing he pointed out in private chat is that if we call GetHostName() to determine if resolved name is our system or not, we will slow down "normal" lookup path. (assuming most often we try to resolve other systems)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net bug help wanted [up-for-grabs] Good issue for external contributors os-linux Linux OS (any supported distro)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants