[Python-bugs-list] [ python-Bugs-774751 ] slow socket binding & netinfo lookups

SourceForge.net noreply@sourceforge.net
Tue, 22 Jul 2003 07:09:02 -0700


Bugs item #774751, was opened at 2003-07-20 18:57
Message generated for change (Settings changed) made by montanaro
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=774751&group_id=5470

Category: Macintosh
Group: Python 2.3
>Status: Pending
>Resolution: Postponed
Priority: 5
Submitted By: Stuart Bishop (zenzen)
>Assigned to: Just van Rossum (jvr)
Summary: slow socket binding & netinfo lookups

Initial Comment:
The following code takes < 1 second to run under Python 
2.1, but 20 seconds to run under Python 2.2 or 2.3 on a top-
end PowerBook running OSX 10.2. It is part of the Zope 
startup routine.

def max_server_sockets():
    sl = []
    while 1:
        try:
            s = socket.socket (socket.AF_INET, 
socket.SOCK_STREAM)
            s.bind (('',0))
            s.listen(5)
            sl.append (s)
        except:
            break
    num = len(sl)
    for s in sl:
        s.close()
    del sl
    return num



----------------------------------------------------------------------

>Comment By: Skip Montanaro (montanaro)
Date: 2003-07-22 09:08

Message:
Logged In: YES 
user_id=44345

Putting this on the back burner for now and assigning to Just as a
present for when he returns ;-).  I haven't been able to resolve
the problem and Apple tells me that getaddrinfo was completely
rewritten for 10.3 (Panther).

----------------------------------------------------------------------

Comment By: Skip Montanaro (montanaro)
Date: 2003-07-21 21:48

Message:
Logged In: YES 
user_id=44345

Another little observation.  I ran the getaddr program (with the
loop cranked up to 10 million iterations) while the top command
was running.  The lookupd process didn't show on the radar at
all.  While running the Python script however, lookupd
consumed about 50% of the cpu.


----------------------------------------------------------------------

Comment By: Skip Montanaro (montanaro)
Date: 2003-07-21 21:42

Message:
Logged In: YES 
user_id=44345

I think we've concluded that it's okay to ship 2.3 with this problem
unresolved, however I'm completely befuddled at the moment.  I 
wrote this
simple program:

    #include <sys/time.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <stdio.h>

    int
    main(int argc, char **argv) {
	    int i;
	    struct addrinfo hints, *res;
	    int error;
	    struct timeval t, u;

	    hints.ai_family = AF_INET;
	    hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
	    hints.ai_flags = AI_PASSIVE;

	    printf("start\n");
	    for (i=0; i<100; i++) {
		    gettimeofday(&t, NULL);
		    error = getaddrinfo(0, "0", &hints, &res);
		    gettimeofday(&u, NULL);
		    fprintf(stderr, "gtod: %.6f\n",
			    u.tv_sec-t.tv_sec+(u.tv_usec-t.tv_usec)*1e-6);
		    freeaddrinfo(res);
	    }
	    printf("finish\n");
    }

When run on my Mac, it takes between 2 and 7 microseconds per 
getaddrinfo
call.  The exact same instrumented call inside 
socketmodule.c:setipaddr at
line 700 takes about 150 *milli*seconds per call.  I tried 
eliminating the
Py_{BEGIN,END}_ALLOW_THREADS calls as well as the ACQUIRE 
and RELEASE of the
getaddrinfo lock.  That had no effect on the call.

I also tweaked the compile like to run just the C preprocessor (gcc 
-E
instead of gcc -g) and checked the output:

	        ...
                hints.ai_family = af;
                hints.ai_socktype = 2;
                hints.ai_flags = 0x00000001;
                { PyThreadState *_save; _save = 
PyEval_SaveThread();
                PyThread_acquire_lock(netdb_lock, 1);
                gettimeofday(&t, 0);
                error = getaddrinfo(0, "0", &hints, &res);
                gettimeofday(&u, 0);
                fprintf((&__sF[2]), "gtod: %.6f\n",
                        u.tv_sec-t.tv_sec+(u.tv_usec-t.tv_usec)*1e-6);
                PyEval_RestoreThread(_save); }
		...

otool -L run against both the test program and _socket.so indicate 
that both
refer to just one shared library (/usr/lib/libSystem.B.dylib), so 
both
should be calling the same routine.


----------------------------------------------------------------------

Comment By: Jack Jansen (jackjansen)
Date: 2003-07-21 15:52

Message:
Logged In: YES 
user_id=45365

I have absolutely nothing to contribute wrt. this bug. As Just is 
away I guess Skip is the best target for the hot patato. 

----------------------------------------------------------------------

Comment By: Skip Montanaro (montanaro)
Date: 2003-07-21 11:33

Message:
Logged In: YES 
user_id=44345

Digging a bit deeper, it appears on Mac OS X that
fake_getaddrinfo is used.  That's deemed not to be thread-safe.
I see three static or global variables acessed by this function:

    firsttime - simple guard
    translate - set inside the guard - readonly after that
    faith_prefix - same

Why not push the guard and initialization code into init_socket
and protect it with the getaddrinfo lock?  Fake_getaddrinfo
would thus be thread-safe and could be accessed without
acquiring a lock.


----------------------------------------------------------------------

Comment By: Skip Montanaro (montanaro)
Date: 2003-07-21 11:14

Message:
Logged In: YES 
user_id=44345

whoops...  my apologies for not posting here.


----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2003-07-21 10:50

Message:
Logged In: YES 
user_id=31435

Attaching copious info from Skip Montanaro (skip.txt).

----------------------------------------------------------------------

Comment By: Anthony Baxter (anthonybaxter)
Date: 2003-07-21 02:05

Message:
Logged In: YES 
user_id=29957

Is this something that should be looked at for 2.3rc2?


----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=774751&group_id=5470