ARTICLE: Global variables under QNX RTP - part 1 of 2

Global variables under QNX RTP - part 1 of 2
By Yves Charron, QNX Software Systems Ltd.

INTRODUCTION

Global variables were often used under QNX4 for slightly faster
execution times, but should be sparingly
used under QNX RTP–if at all. Give yourself a moment to relax and
breathe before unearthing your war
hammer and reviewing all the cool code you’ve recently written for the
QNX RTP, literally patched with
global variables. They can be used but not everywhere without one
undesirable side effect–read on to
find out!

DEVELOPMENT

Although global variables are a bane in most programming
environment(including C/C++ under QNX
RTP: our focus for this article), their use doesn’t always affect a
program’s execution time. Unfortunately,
under QNX RTP, using global variables in shared objects(.so)(the
equivalent of .dll’s under WindowsTM)
they suffer a performance hit.

In general, writing code that works is on the mind of most students, but
writing code that works well is on
the mind of most professional/experienced coders. Not all professional
coders are assembler gurus of
old, resurrected into C coders still couting cycles. Most rely on their
greatest boon: knowledge.

Linking an object :

static linking : Statically linking an object(library) has no
performance hit when using global variables.
Because it’s actually linked inside the linking app. shared linking :
Linking to a shared object has a
performance hit when using global variables. By how much? It all depends
on the access rate to the
global variable in question. Currently, three extra instructions are
processed for each access to a global
variable. You make the math. Testing your code is the best way to
witness this performance hit.

HERE’S AN EXAMPLE Thanks for the knowledge bud, but i’m not convinced
and too lazy to test it out!
All right, here’s an example for you to try and convince yourself! Note:
I have included a compiled version
here : example.tar.gz (Please refer to qdn.qnx.com for the download).

[so_example.c] - compile to libso_example.so

int so_global_variable;
void test_so_global_access(void)
{
so_global_variable = 0xFFFFFFFF;
}

[a_example.c] - compile to liba_example.a

int a_global_variable;
void test_a_global_access(void)
{
a_global_variable = 0xFFFFFFFF;
}

[EOF]

[example.c] - compile to example

#include <dlfcn.h> // For dlopen().
#include <stdio.h>
#include <time.h>

int global_variable;

void test_global_access(void)
{
global_variable = 0xFFFFFFFF;
}

void output_string(char *msg, clock_t start, clock_t end)
{
int timing;

timing = (end - start) / CLOCKS_PER_SEC;

printf(msg);
printf("\n\ttest result : %i.%i(in secs)\n\n", timing,
timing%10);
}

int main(void)
{
void (*test_so_global_access)(void)=NULL;
clock_t start, end;
void *so;

int i, j;

// Setup and link shared object.

if ((so = dlopen(“libso_example.so”, 0755)) == NULL)
{
perror(“dlopen”);
printf(“dlerror: %s\n”, dlerror());
exit(1);
}

if ((test_so_global_access = dlsym(so,
“test_so_global_access”)) == NULL)
{
printf(“Failed to get test func from shared object.”);
exit(1);
}

// Run tests…

// Inside program(.o): no performance hit.

start = clock();
for (i=0; i < 500; i++)
for (j=0; j < 1000000; j++)
test_global_access();
end = clock();

output_timing(“Inside program :”, start, end);

// Inside statically linked library(.a): no performance hit.

start = clock();

for (i=0; i < 500; i++)
for (j=0; j < 1000000; j++)
test_a_global_access();
end = clock();

output_timing(“Inside statically linked library :”, start,
end);

// Inside shared object(.so): no performance hit.

start = clock();
for (i=0; i < 500; i++)
for (j=0; j < 1000000; j++)
test_so_global_access();
end = clock();

output_timing(“Inside shared object :”, start, end);

return 0;
}

[EOF]


}

Result for a test run on my PC :
Test machine :
Intel Pentium III 550

Banshee Voodoo 3 video adapter

128 Megs RAM


Output :

Inside program : test result : 7.7(in secs)

Inside statically linked library :

test result : 7.7(in secs)

Inside shared object :

test result : 17.7(in secs)

As you can see from the results, the theory is proven. Using global
variables in a shared object is slower.
Use them with care. When analyzing the results, do not forget the
overhead of the function call, but a
substantial gain in speed is present on a high access rate(500 *
1000000) in this case. Also, if you take
a look at the assasembly file using ‘objdump -d libso_example.so’ and
compare to ‘objdump -d example’
you can clearly see the 3 extra instructions for the shared object file.

CONCLUSION

Under QNX RTP, when using shared objects avoid global variables for
critical code, that have a high
access rate. Use them sparingly elsewhere–but that’s up to you: for a
quick patch, flags, etc. I’m sure
you’ll have lots of ideas on how to do this.

FOLLOW UP

Still to come on global variables :

Global variables under QNX RTP: Avoiding them under Photon and standard
applications.