syntax-highlighter

Sunday, January 31, 2010

Cross-platform Code Profiling

In order to guide optimization efforts, it is often necessary to determine how long individual blocks of code take to execute. Although there are a number of packages available that do this, I have found them to generally i) be quite expensive, ii) be inconvenient to use and iii) generate huge output files. This is not to say these packages don't have a place, but simply that sometimes you don't need all their features and so a more compact approach would be useful.

The classes presented here are simply included in you project and compiled in. Blocks are instrumented manually with pre-processor macros that initialize and register timers. Timing is performed with QueryPerformanceCounter on Windows and gettimeofday on Linux/Mac, giving an approximate lower bound on the intervals that can be resolved of about 1 microsecond. If you need more resolution than this, you'll have to use something else. Statistics can then be dumped to any std::ostream, allowing you to print profiling statistics to a file, the console, log window or whatever. The only statistics collected are the call count for each block, average time per call and total elapse time in the block.

Here is an example of how to use them:

#include<iostream>

// comment this out to turn off profiling
// (and avoid its associated cost)
#define USE_PROFILING

#include"Profiler.h"

int main( int argc, char **argv ){
 PROFILER_START( main_timer );

 // do stuff here

 PROFILER_END( main_timer );
 ProfileManager::Get()->PrintStatistics( std::cout );
 return 0;
}


Here are the source files. You can use them under a BSD license. If you find them useful I'd appreciate if you let me know.

Profiler.h
Profiler.cpp

Portable Dynamic Libraries

It's often useful to encapsulate components of a program in dynamic libraries. These allow you you make minor changes to a module without rebuilding the whole program and swap out components at runtime. This post shows how to do this from the command-line on Windows, Linux and Mac platforms for a simple example. I am assuming that explicit linking is being used; in my opinion the point of using dynamic libraries is to not need things like import libraries and the like...

To start with, there needs to be a module to make a dynamic library from. I'm going to work from the following, trivial function (assumed to be saved as module.cpp)

// module.cpp
int ModuleFunction( void ){ 
    return 5; 
}


In order to build this as a dynamic library some boiler-plate needs to be added. First of all the whole function needs be wrapped in an extern "C" block. This prevents C++ name mangling from changing the name of the function in the module. Secondly, under Windows the function has to be declared with __declspec(dllexport) in order to be accessible from the library. This is unnecessary on Linux and Mac machines, so this declaration is only applied if building on Windows machines. With these changes, tmp.cpp becomes:

// module.cpp
#ifdef WIN32
    #define EXPORT __declspec(dllexport)
#else
    #define EXPORT
#endif
extern "C" {
    EXPORT int ModuleFunction( void ){ 
        return 5; 
    }
}


With the module source handled, the library has to be built. The command-lines to do this differ depending on what platform and compiler you're using:

Windows, Visual C++
vcvarsall.bat (or vcvars32.bat)
cl.exe -o module.dll module.cpp /link /DLL

Windows, MinGW/ Linux, GCC
g++ -fPIC -shared module.cpp -o module.dll (or module.so)

Mac, GCC
g++ -dynamiclib tmp.cpp -o module.so

Note that for non-trivial examples you would have to specify additional source files and include/library paths.

At this point, there should be a dynamic library named module.dll (Windows) or module.so (Linux/Mac). To actually use this library there are minor differences between Windows and Linux/Mac. The following header (DLL.h) handles these variations allowing a common interface under all three platforms:

// DLL.h
#ifndef DLL_H
#define DLL_H

#ifdef _WIN32
#include<windows.h>
#define DLLPtr    HMODULE
#define LoadDLL(a) LoadLibraryA( a )
#define FreeDLL(a) FreeLibrary( a )
#else
#include<dlfcn.h>
#define DLLPtr    void*
#define LoadDLL(a) dlopen( a, RTLD_LAZY )
#define FreeDLL(a) dlclose( a )
#endif

template<typename Func>
__inline Func GetFunc( DLLPtr dll, const char *symbol ){
 #ifdef _WIN32
  return (Func)GetProcAddress( dll, symbol );
 #else
  return (Func)dlsym( dll, symbol );
 #endif
}

#endif


This provides functions to load and free dynamic libraries as well as retrieve function pointers from dynamic libraries. An example of its usage for the simple library defined above is:

// main.cpp
int main( int argc, char **argv ){
 DLLPtr lib = LoadDLL( "blah_0.dll" );
 if( !lib ){
  // handle error;
  return 0;
 }

 int (*F)(void) = GetFunc<int(*)(void)>( lib, "blah" );
 if( !F ){
  // handle error
  return 0;
 }
 std::cout << "Result: " << F() << std::endl;
 FreeDLL(lib);
 return 0;
}


Windows, Visual C++
vcvarsall.bat (or vcvars32.bat)
cl.exe -o test.exe main.cpp /link

Windows, MinGW /Mac GCC
g++ -o test main.cpp (test.exe on Windows)

Linux, GCC
g++ -o test -ldl main.cpp

Running the compiled executable should print the following string "Result: 5".

Some Notes:
1. I have found that repeatedly loading and unloading DLLs that were compiled with MinGW under Windows causes a crash. This appears to be isolated to MinGW, since the same programs run fine when compiled with Visual C++, or on Linux and Mac systems with GCC.

2. It is possible to build a dynamic library with one compiler for use with an application built by another compiler. This could be useful for allowing users to write their own plugins for a proprietary system. However for this to work properly, it seems that the runtime libraries used by both library and executable must be the same (if platform specific code is called) and any objects passed between the library and executable must be binary compatible. This frequently means that things like stl containers cannot be passed, since different compilers will likely use different implementations of stl.