When you use dynamic library loading you probably open library each time you want to load routine. As for loading/unloading routines on demand you should open library every time to ensure that library is loaded and you can access routine you are interested. This will heavily reduce performance and memory usage. Probably you can control whether library was opened or not but this requires more code from you. In glibc from 2.2 had appeared 2 very useful flags to cover this situation: dlopen now may get RTLD_NOLOAD and RTLD_NODELETE flags. With RTLD_NOLOAD you can check if library was loaded and with RTLD_NODELETE you say dlclose not to unload library. I've made some tests. W/o RTLD_NOLOAD and RTLD_NODELETE flags:
void *handle; int i; for (i=0;i<1000000;++i) { handle = dlopen("/usr/lib/libdl.so", RTLD_LAZY); if (handle == NULL) break; dlclose(handle); }This gave me
for i in 1 2 3; do time ./test; done real 0m0.567s user 0m0.568s sys 0m0.000s real 0m0.566s user 0m0.556s sys 0m0.000s real 0m0.562s user 0m0.556s sys 0m0.000sPretty slow, I should say. And w/ RTLD_NOLOAD and RTLD_NODELETE flags:
void *handle; int i; for (i=0;i<1000000;++i) { handle = dlopen("/usr/lib/libdl.so", RTLD_LAZY|RTLD_NOLOAD|RTLD_NODELETE); if (handle == NULL) handle = dlopen("/usr/lib/libdl.so", RTLD_LAZY|RTLD_NODELETE); if (handle == NULL) break; dlclose(handle); }This one gave me
for i in 1 2 3; do time ./test; done real 0m0.542s user 0m0.536s sys 0m0.000s real 0m0.570s user 0m0.572s sys 0m0.000s real 0m0.554s user 0m0.528s sys 0m0.004sThe same. libdl is pretty small. Let's try something bigger. Tests results for /lib/libc-2.7.so: w/o RTLD_NOLOAD and RTLD_NODELETE flags:
for i in 1 2 3; do time ./test; done real 0m0.656s user 0m0.656s sys 0m0.000s real 0m0.649s user 0m0.644s sys 0m0.000s real 0m0.647s user 0m0.644s sys 0m0.000sw/ RTLD_NOLOAD and RTLD_NODELETE flags:
real 0m0.611s user 0m0.612s sys 0m0.000s real 0m0.610s user 0m0.600s sys 0m0.000s real 0m0.608s user 0m0.604s sys 0m0.000sWe have won ~0.040s. Not bad. And another one for /usr/lib/libdb_cxx-4.5.so[note, I have changed circumstances: loop ran only 1000 times]: w/o RTLD_NOLOAD and RTLD_NODELETE flags:
for i in 1 2 3; do time ./test; done real 0m1.270s user 0m1.016s sys 0m0.244s real 0m1.290s user 0m1.084s sys 0m0.188s real 0m1.269s user 0m1.036s sys 0m0.236sw/ RTLD_NOLOAD and RTLD_NODELETE flags:
for i in 1 2 3; do time ./test; done real 0m0.003s user 0m0.004s sys 0m0.000s real 0m0.003s user 0m0.004s sys 0m0.000s real 0m0.003s user 0m0.000s sys 0m0.000sYay! They differ for more than 1.2 seconds. That's because /usr/lib/libdb_cxx-4.5.so had to load some extra libraries.
ldd /usr/lib/libdl.so linux-gate.so.1 => (0xb7fa2000) libc.so.6 => /lib/libc.so.6 (0xb7e53000) /lib/ld-linux.so.2 (0xb7fa3000)
ldd /lib/libc-2.7.so /lib/ld-linux.so.2 (0xb7f7f000) linux-gate.so.1 => (0xb7f7e000)
ldd /usr/lib/libdb_cxx-4.5.so linux-gate.so.1 => (0xb7f3b000) libpthread.so.0 => /lib/libpthread.so.0 (0xb7dcb000) libstdc++.so.6 => /usr/lib/gcc/i686-pc-linux-gnu/4.2.3/libstdc++.so.6 (0xb7ce0000) libm.so.6 => /lib/libm.so.6 (0xb7cbb000) libc.so.6 => /lib/libc.so.6 (0xb7b86000) libgcc_s.so.1 => /usr/lib/gcc/i686-pc-linux-gnu/4.2.3/libgcc_s.so.1 (0xb7b79000) /lib/ld-linux.so.2 (0x80000000)
3 comments:
By performance basic your blog is providing outstanding performance. This is one of the superior post.
Android app developers
Why would someone close a handle if he intends to use it later? How it is beneficial to close a handle?
Hi Santosh,
it makes sense if you are bothering about the memory footprint and external function is called rarely.
Post a Comment