Subject: Re: Time and motion studies of gcc and egcs and LCC
From: [email-masked]

4 Feb 1998 16:09:36 -0800
Message-ID: <[email-masked]>
To: [email-masked], [email-masked]

I said:
>I don't think that the overhead of getting into a DLL is all that great, so
>I think that the performance hit is somewhere else, like inside cygwin.dll
>and NT itself.  The cost to enter a DLL is an additional jump instruction
>(indirect through the linkage pointer).

[email-masked] said:
<I guarantee you that your estimates are extremely optimistic.  Thunking
<is an operating system call followed by filling in a jump location,
<followed by the jump. Every time the call is made. Using a DLL library
<under Windows is _not_ like using a share library under Linux, because
<the Linux linker takes the time to make all calls to the shared jumptable
<library behave like statically-linked calls.

<Rather than wage a war of opinions in which I am sure that mine is the
<better,  someone should do a profile of where the compiler and bash.exe
<spend their time.

While I agree that profiling will likely tell the story (if you profile the
DLL entry/exit code), I don't agree that there is any significant code
executed to get into a DLL.

First off, there are two different ways that a DLL can be called by a program.
The DLL can be linked and loaded when the .exe file is loaded, which is the
normal way that cygwin32 attempts to build things (i.e. with dlltool, etc.).
The other way is to be loaded at run time, where the calling routine first
gets into code to load the DLL and resolve that one function, then all
subsequent calls to that function require very little overhead (for a
reasonable implementation of a run-time loader, I don't know if there is
a standard, though).  Since load-time DLL loading is most common, that is
what I am talking about.

I have not profiled the code execution, but I have taken apart the peices
of code generated to build the import table to try to solve some link
problems I was having.  This is the structure of the import table:

 section .text:
func@0:		jmp *XXXX

 section .idata$5:
XXXX:		YYYY
	....

 section .idata$6:
YYYY:	00 01		// "hint"
	"func"		// ascii string, name of function in DLL

That is, the symbol that the calling program links to is defined in the
..text section and contains an indirect jump.  When linked, the address
stored at XXXX is actually a pointer to a linkage table.  All of the
functions called from the DLL are allocated pointers in .idata$5 consecutively
with a 0 (NULL) terminator.  A pointer to this list is stored in the
import table header.  When the .exe file is loaded, the loader scans
this import table header list.  It will find this DLL and load the DLL.
Then, it will walk down this table in .idata$5 and try to resolve each
function called in the DLL.  In this case, it will follow the pointer to
address YYYY and find the name of the function "func".  If "func" is
found in the DLL, the address of "func" in the DLL is written into memory
location XXXX.  If it isn't found, an error is reported.

So, after loading the .exe and all DLLs referenced by it, memory looks like:

 section .text:
func@0:		jmp *XXXX

 section .idata$5:
XXXX:		func	// Address of function in already loaded DLL

Thus, when the calling program calls "func@0", it will execute the jmp
instruction which will pick up the target address from XXXX and this
results in the address of the function in the DLL.  The function is already
loaded, so control transfers directly to the called function.  (This is
neglecting the possible interruptions for page faults, of course.)

So, I stand by my statement that DLL linkage incurs very little run-time
overhead.  There is some overhead at load-time, so for a trivial program
I guess that it could be a significant portion, but for a long running
program such as bash or gcc, the load-time overhead is proportionally small.

marcus hall

-
For help on using this list (especially unsubscribing), send a message to
"[email-masked]" with one line of text: "help".