Channels ▼
RSS

Tools

Hot-Rodding Windows and Linux App Performance with CUDA-Based Plugins


CUDA as a Cross-Platform Scripting Language

Combining the previous dynamic comple/link and protobuf examples yields the powerful "click-together" framework I mentioned earlier.

The following is the complete source for dynFunc.cc, which combines the streaming of protobuf messages and the dynamic loading of plugins. Windows users will need to precompile the DLLs, while the Linux version dynamically compiles the code at runtime. Production code needs to ensure there are no name conflicts. One possibility is to add the PID (Process ID) to the plugin name.

For flexibility, all methods have the ability to create new messages that can be passed on to other applications in the click-together framework. The plugin method is responsible for creating a char array containing the serialized message. The method returns a pointer to the character array that contains the message. The plugin method must also modify type and size to reflect the new message contents. Returning NULL implies no message is to be written. To simplify memory management, the dynFree() method is used to free the region of memory (for example, C source code would use malloc()/free() and C++ source code would use new/delete).

//Rob Farber
#ifdef _WIN32
#define DYN_INCLUDE "../protobuf_examples/protobuf-2.4.1/vsprojects/include"
#include <cstdint>
extern __declspec(dllimport) char* init(const char*, const char*);
extern __declspec(dllimport) char* func(const char*, const char*,
					uint32_t*, uint32_t*, char*);
extern __declspec(dllimport) char* fini(const char*, const char*);
extern __declspec(dllimport) void dynFree(char*);
#else
#include <cstdlib>
#include <sys/types.h>
#include <dlfcn.h>
#endif
#include <string>
#include <iostream>
#include "packetheader.h"

using namespace std;

#ifdef _WIN32
HINSTANCE lib_handle;
#else
void *lib_handle;
#endif

typedef char* (*initFini_t)(const char*, const char*, uint32_t*, uint32_t*);
typedef char* (*func_t)(const char*, const char*, uint32_t*, uint32_t*, char*);
typedef void (*dynFree_t)(char*);

int main(int argc, char **argv) 
{
  if(argc < 2) {
     cerr << "Use: sourcefilename" << endl;
     return -1;
  }
  string base_filename(argv[1]);
  base_filename = base_filename.substr(0,base_filename.find_last_of("."));
 
 // build the shared object or dll
#ifdef _WIN32
  string buildCommand("nvcc ");
  buildCommand += string(" -Xcompiler \"/MD\" -I ") 
+ string(DYN_INCLUDE) + string(" ") 
	+ string(argv[1]) + string(" tutorial.pb.cc ") 
	+ string(" ") 
	+ string(" -Xlinker \"/DLL /OUT:") + base_filename + string(".dll \"");
 // See article: Windows only links to pre-made DLLs
#else
  string buildCommand("nvcc -Xcompiler \"-fPIC -shared\" ");
  buildCommand += string(argv[1]) 
               + string(" -o ") + base_filename + string(".so ");

  cerr << "Compiling with \"" << buildCommand << "\"" << endl;
  if(system(buildCommand.c_str())) {
     cerr << "compile command failed!" << endl;
     cerr << "Build command " << buildCommand << endl;
     return -1;
  }
#endif
 
 
 // load the library ------------ 
  string nameOfLibToLoad("./");
  nameOfLibToLoad += base_filename;
 
#ifdef _WIN32
  nameOfLibToLoad += ".dll";
  lib_handle = LoadLibrary(TEXT(nameOfLibToLoad.c_str()));
  if (!lib_handle) {
     cerr << "Cannot load library: " << TEXT(nameOfLibToLoad.c_str()) << endl;
     return -1;
  }
#else
  nameOfLibToLoad += ".so";
  lib_handle = dlopen(nameOfLibToLoad.c_str(), RTLD_LAZY);
  if (!lib_handle) {
     cerr << "Cannot load library: " << dlerror() << endl;
     return -1;
  }
#endif
 
  // load the symbols ------------
  initFini_t dynamicInit= NULL;
  func_t dynamicFunc= NULL;
  initFini_t dynamicFini= NULL;
  dynFree_t dynamicFree= NULL;

#ifdef _WIN32
  dynamicInit = (initFini_t) GetProcAddress(lib_handle, "init");
  if (!dynamicInit) {cerr << "sym load error on init" << endl; return -1;}
  dynamicFunc = (func_t) GetProcAddress(lib_handle, "func");
  if (!dynamicFunc) {cerr << "sym load error on func" << endl; return -1;}
  dynamicFini = (initFini_t) GetProcAddress(lib_handle, "fini");
  if (!dynamicInit) {cerr << "sym load error on fini" << endl; return -1;}
  dynamicFree = (dynFree_t) GetProcAddress(lib_handle, "dynFree");
  if (!dynamicFree) {cerr << "sym load error on dynFree" << endl; return -1;}
#else
  // reset errors
  dlerror();
 
  // load the function pointers
  dynamicFunc= (func_t) dlsym(lib_handle, "func");
  const char* dlsym_error = dlerror();
  if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
  dynamicInit= (initFini_t) dlsym(lib_handle, "init");
  dlsym_error = dlerror();
  if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
  dynamicFini= (initFini_t) dlsym(lib_handle, "fini");
  dlsym_error = dlerror();
  if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
  dlsym_error = dlerror();
  if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
  dynamicFree= (dynFree_t) dlsym(lib_handle, "dynFree");
#endif

  // 
  // work with protobufs
  // 

  //enable C++ binary cin and cout
  if (!setPacket_binaryIO()) {
    cerr << "Cannot set binary mode for cin and cout!" << endl;
    return -1;
  }

  uint32_t size, type;
  char *retBlob;

  // handle initialization and put information on output stream when told
  if( (retBlob=(*dynamicInit)(argv[0], base_filename.c_str(),&size, &type)) ) {
    writePacketHdr(size, type, &std::cout);
    cout.write(retBlob, size);
    (dynamicFree)(retBlob);
  }

  // read stream from cin and put information on output stream when told
  while(readPacketHdr(&size, &type, &std::cin)) {
    char *blob = new char[size];
    cin.read(blob, size);
    retBlob =(*dynamicFunc)(argv[0], base_filename.c_str(), &size, &type, blob);
    if(retBlob) {
      writePacketHdr(size, type, &std::cout);
      cout.write(retBlob, size);
      // optimization: if retBlob == blob then allocated was by this program
      if(retBlob != blob) (dynamicFree)(retBlob);
    }
    delete [] blob;
  }

 // handle finalization (fini) and put information on output stream when told
   if( retBlob = (*dynamicFini)(argv[0], base_filename.c_str(),&size, &type) ) {
     writePacketHdr(size, type, &std::cout);
     cout.write(retBlob, size);
     (dynamicFree)(retBlob);
   }
 
 // unload the library -----------------------------------------------
#ifdef _WIN32
 FreeLibrary(lib_handle);
#else
  dlclose(lib_handle);
#endif
  return 0;
}

The source for passthrough.cc is included below. This plugin simply passes all messages along to the next application in the pipeline. It is important that Windows users note the use of #pragma comment() to inform the linker about needed libraries. In this way, the protobuf method can be linked with the plugin. Linux users will note the nvcc command includes use of the –rdynamic option, which tells the linker to check the executable for any unresolved symbols.

//passthrough.cc (Rob Farber)
#include <stdlib.h>
#include <stdint.h>
#include <iostream>
#include "tutorial.pb.h"
using namespace std;

#ifdef _WIN32
// tell the linker about needed libraries
#pragma comment(lib,"libprotobuf")
#endif

extern "C" 
#ifdef _WIN32
__declspec(dllexport)
#endif
char* init(const char* progname, const char* sourcename,
	 uint32_t *size, uint32_t *type) {
 return(NULL); 
}

extern "C" 
#ifdef _WIN32
__declspec(dllexport)
#endif
char* func(const char* progname, const char* sourcename, 
	 uint32_t *size, uint32_t *type, char *blob)
{
 return(blob); //Note: this is a special case that will not invoke dynFree
}

extern "C" 
#ifdef _WIN32
__declspec(dllexport)
#endif
char* fini(const char* progname, const char* sourcename,
	 uint32_t *size, uint32_t *type) {
 return(NULL); 
}

extern "C" 
#ifdef _WIN32
__declspec(dllexport)
#endif
void dynFree(char* pt) {
  cerr << "dynFree" << endl;
  if(pt) delete [] pt;
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video