Re: On the cost of libraries



Owen Taylor wrote:
> 
> Drazen Kacar <dave arsdigita com> writes:
> 
> > Owen Taylor wrote:
> > 
> > > Let's first get some hard data.
> > 
> > What kind of data would you prefer?
> 
> Timing data. Map data tells me little more than some details about
> how tables are set up in memory; it doesn't give any real indication
> of what the cost is of setting up those tables is.

It tells that some libraries are not touched. The example was run on a
server which didn't have any GTK application running (which can be
deducted from "Shared" column being zero for a lot of text segments).
Truss (that's strace on Linux) output would show that the libraries which
were not mapped are really not touched. So I thought that "not loaded"
equals zero overhead and "loaded" equals some overhead. If you want timing
data, I would have to calculate disk access as well. You might say that
it's atypical for GTK not to be loaded and I'd agree with that. But is it
atypical for Guppi libs?

> > Just the first one, in certain circumstances. Any additional symbol
> > doesn't incur overhead.
> 
> Certainly there is _some_ overhead. The more copy relocs I
> have to do, the more pages of the shared library I have to touch...

Yeah, but hopefully that data would be located at the same page (if it
fits, of course). There are tools which should be able to arrange that.

> ELF on Solaris, in my understanding, is very similar to ELF on
> Linux. 

By default yes. There are linker options which can change the behaviour.

> Because of the way symbol lookups in ELF works, even if there are no
> copy relocations pointing to a library, the first time the linker
> needs to look up a symbol that is in a library after those libraries
> in the link order, it's going to load up those libraries.
> 
> That is, if I have   gcc -o a.out main.c -lfoo -lbar
> 
> then if main.c references bar_a(), then there is no way the runtime
> linker can avoid loading libfoo to see if bar_a() is in
> libfoo. 

Lazy loading breaks that. It is essentialy a contract which says to
run-time linker that the symbol will be in the library that had it at link
time, so it doesn't have to load libfoo. Interposition is bad for other
reasons as well, so making this kind of contract doesn't look like an
important limitation to me. It would be better to have libraries which
were well designed, in which case lazy loading wouldn't give much benefit.
But if they are not well designed, tricks like this can help.

> AFAIK, ELF doesn't record _which_ shared library a reference is in,

That depends on your contract with the linker. ELF is just a file format.

> just that the  reference is in some external shared library. 
> (Which has advantages ... under ELF, you can move symbols around
> between shared libs without breaking bin compat, and you have

The only way to do it is with DT_FILTER entries and lazy loading should
work with that (I haven't tested, though).

> the LD_PRELOAD capability.) 

That's not a problem in any case, because it's explicitely marked as
an interposer.

> Also, I'll point out, your example is quite unusual. It's not
> often you have an executable that links to GTK+ but doesn't
> use in most cases.

Yes, which is why I didn't use lazy loading when linking libgtk in the
first place.

> So, the fact that copy relocs hurt this case isn't a very urgent
> consideration for me. 

Oh, I didn't mean to present it as such. I just needed a small example
(with a small number of libraries, that is) for the demonstration. To
handle this situation with lazy loading, I'd prefer if linker had a flag
which would tell it that GTK data will not be used before one of the GTK
functions was called. Then GTK wouldn't be loaded in this case. I don't
know of a linker system which has this capability, but it could be
implemented. In that case you'd probably be asked to provide a few
functions more, eg. a function which returns GTK version data. But not
much more than that.

> In some cases, exporting variables can result in considerably
> more efficient code ... consider if 'stdin' involved a 
> function call for every use, or the typical implementation
> of ctype().... GNU libc exports about 60 variables, and

libc is different. It predates DSOs, so for some things the API was set in
advance. Another thing is that practically all applications will be linked
against it, so there's nothing to be gained if it was designed for a case 
when it wouldn't be loaded.

> we have a number of important exports in GLib as well.
> 
> 00051f08 D g_ascii_table
> 00051f60 D g_thread_functions_for_glib_use
> 00051f58 D g_threads_got_initialized
> 00059520 D g_utf8_skip

Yeah, but if those were supposed to be accessed by a functional
interface, it doesn't mean there'd be a function call overhead for every
use. You can call the function once, store the returned pointer in a local
or global variable and use that for every other reference.

And there is additional problem with exporting data: the size of your
arrays becomes a part of the API. In case you want to grow these arrays
(if they are arrays; I'm not sure) in the future, you won't be able to do
that without breaking the binary compatibility, because there wouldn't be
enough space in executable's BSS segment for copy relocations. Now,
g_ascii_table looks like something which will always have the same size.
I'm not sure about the others, though. If there is a chance that they
would be extended in the future, export functional interface to access
them.

> And finally, remember a copy reloc doesn't get added to the
> a program unless the program actually accesses the variable.
> Most programs don't have a copy reloc for gdk_display,
> because most programs don't use the GDK_DISPLAY() macro.

Which is why it could have been a function? :-)

-- 
 .-.   .-.    Errors have been made. Others will be blamed.
(_  \ /  _)
     |
     |        dave arsdigita com




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]