ARTICLE REPOST: 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 QNX 4
for slightly faster execution times, but you should
use them sparingly 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 QNX RTP, code that’s
literally patched with global variables. You can still
use global variables, but not everywhere, without
one undesirable side effect – read on to find out how!

Development

Although global variables are a bane in most
programming environments (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 files, the equivalent of .dlls under Windows) will
mean a performance hit.

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

Linking an object

How you link an object can affect performance:
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 do the math. Testing your code is the best
way to witness this performance hit.


Here’s an example

You might be thinking, “Thanks for the knowledge,
bud, but I’m not convinced and I’m too lazy to test
it out!” All right, here’s an example for you to try
and convince yourself!

NOTE: I’ve included a compiled version on
http://qdn.qnx.com/articles/dec2100/global.html :
example.tar.gz

[so_example.c] - compile to libso_example.so

int so_global_variable;

void test_so_global_access(void)
{
so_global_variable = 0xFFFFFFFF;
}
[EOF]

[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”, RTLD_NOW)) == 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]



Test results

Here are the results from a test run on my PC:

Test machine

Intel Pentium III 550
Banshee Voodoo 3 video adapter
128 Megs RAM

Output

Location of global variable: Test result:
Inside program 7.7 secs
Inside statically linked library 7.7 secs
Inside shared object 17.7 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, don’t 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 assembly file
using objdump -d libso_example.so and compare that to
objdump -d example, you can clearly see the three extra
instructions for the shared object file.

Conclusion

As a rule, when using shared objects under QNX RTP,
avoid global variables for critical code, especially if they
have a high access rate. Use them sparingly elsewhere
– but that’s up to you (e.g. 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 in Photon and standard apps.