#ifdef _MSC_VER
#pragma warning(disable : 4996)
#endif
#include "fft_binary_lookup.h"
#include <iostream>
#include <fstream>
#include <cassert>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#else
#include <unistd.h>
#endif
extern "C"
{
#include "md5sum.h"
}
#define SIZE 256
#define ENABLE_SOURCE_DUMP 0
#define CAPS_DEBUG 0
#include <string.h>
static char * sep()
{
#ifdef _WIN32
return (char*)"\\";
#else
return (char*)"/";
#endif
}
static std::string cache_path;
static bool cache_enabled(false);
static bool request_nomemalloc(false);
void clfftInitRequestLibNoMemAlloc()
{
const char * val = getenv("CLFFT_REQUEST_LIB_NOMEMALLOC");
if (val)
request_nomemalloc = true;
}
bool clfftGetRequestLibNoMemAlloc()
{
return request_nomemalloc;
}
void clfftInitBinaryCache()
{
const char * path = getenv("CLFFT_CACHE_PATH");
if (path)
{
cache_path = std::string(path) + sep();
cache_enabled = true;
}
else
{
cache_path = "";
}
}
FFTBinaryLookup::CacheEntry::CacheEntry(const std::string & filename)
: m_filename(filename), m_successful_creation(false)
{
}
void FFTBinaryLookup::CacheEntry::close()
{
#ifdef _WIN32
CloseHandle(this->m_handle);
#else
::close(*(int*)this->m_handle);
#endif
}
bool FFTBinaryLookup::CacheEntry::successful_creation()
{
return this->m_successful_creation;
}
bool FFTBinaryLookup::CacheEntry::exclusive_create()
{
#ifdef _WIN32
std::wstring tmp;
tmp.assign(this->m_filename.begin(), this->m_filename.end());
HANDLE handle = CreateFile(tmp.c_str(),
GENERIC_WRITE,
0, NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
this->m_handle = handle;
this->m_successful_creation = (handle != INVALID_HANDLE_VALUE);
return this->m_successful_creation;
#else
int * fd = new int;
*fd = open (this->m_filename.c_str(),
O_CREAT | O_EXCL,
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
this->m_handle = fd;
this->m_successful_creation = (*fd != -1);
return *fd >= 0;
#endif
}
FFTBinaryLookup::FFTBinaryLookup(const clfftGenerators gen, const clfftPlanHandle plHandle, cl_context ctxt, cl_device_id device)
: m_context(ctxt), m_device(device), m_program(NULL), m_binary(0), m_signature(0), m_cache_enabled(cache_enabled)
{
this->m_cache_entry_name = getKernelName(gen, plHandle, false);
if (this->m_cache_enabled)
{
cl_int err = this->retrieveDeviceAndDriverInfo();
if (err != CL_SUCCESS)
{
cache_enabled = false;
this->m_cache_enabled = false;
}
}
}
FFTBinaryLookup::~FFTBinaryLookup()
{
if (this->m_binary != NULL)
{
delete[] this->m_binary;
this->m_binary = 0;
}
if (this->m_signature != NULL)
{
delete[] this->m_signature;
this->m_signature = 0;
}
}
FFTBinaryLookup::Variant::Variant()
: m_kind((VariantKind)0), m_size(0), m_data(0)
{
}
FFTBinaryLookup::Variant::Variant(VariantKind kind, char * data, size_t size)
: m_kind(kind), m_size(size)
{
this->m_data = new char[this->m_size];
memcpy(this->m_data, data, size);
}
FFTBinaryLookup::Variant::Variant(const Variant &obj)
: m_kind(obj.m_kind), m_size(obj.m_size)
{
this->m_data = new char[this->m_size];
memcpy(this->m_data, obj.m_data, m_size);
}
FFTBinaryLookup::Variant &FFTBinaryLookup::Variant::operator=(const Variant &obj)
{
if (this->m_data != NULL)
{
delete[] this->m_data;
this->m_data = 0;
}
m_kind = obj.m_kind;
m_size = obj.m_size;
this->m_data = new char[this->m_size];
memcpy(this->m_data, obj.m_data, m_size);
return *this;
}
FFTBinaryLookup::Variant::~Variant()
{
if (this->m_data != NULL)
{
delete[] this->m_data;
this->m_data = 0;
}
}
void FFTBinaryLookup::variantInt(int num)
{
m_variants.push_back(Variant(INT, (char*)&num, sizeof(num)));
}
void FFTBinaryLookup::variantDouble(double num)
{
m_variants.push_back(Variant(DOUBLE, (char*)&num, sizeof(num)));
}
void FFTBinaryLookup::variantCompileOptions(const std::string & opts)
{
m_variants.push_back(Variant(STRING, (char*)opts.c_str(), opts.size()));
}
void FFTBinaryLookup::variantRaw(const void * data, size_t bytes)
{
m_variants.push_back(Variant(DATA, (char*)data, bytes));
}
enum BinaryRepresentation
{
LSB,
MSB,
UNKNOWN
};
static enum BinaryRepresentation getStorageMode(char * data)
{
if (data[0] == 'C' &&
data[1] == 'L' &&
data[2] == 'B' &&
data[3] == '\0')
return LSB;
if (data[0] == 'B' &&
data[1] == 'L' &&
data[2] == 'C' &&
data[3] == '\0')
return MSB;
return UNKNOWN;
}
void FFTBinaryLookup::finalizeVariant()
{
size_t whole_variant_size_in_bytes = 0;
whole_variant_size_in_bytes += this->m_variants.size() * sizeof(int); whole_variant_size_in_bytes += this->m_variants.size() * sizeof(size_t);
for(size_t i=0 ; i<this->m_variants.size() ; ++i)
{
const Variant & v = this->m_variants[i];
whole_variant_size_in_bytes += v.m_size;
}
this->m_header.signature_size = whole_variant_size_in_bytes;
if (this->m_signature != NULL)
{
delete[] this->m_signature;
this->m_signature = 0;
}
this->m_signature = new char[whole_variant_size_in_bytes];
char * current_address = this->m_signature;
for(size_t i=0 ; i<this->m_variants.size() ; ++i)
{
Variant v = this->m_variants[i];
memcpy(current_address, &v.m_kind, sizeof(int));
current_address += sizeof(v.m_kind);
memcpy(current_address, &v.m_size, sizeof(v.m_size));
current_address += sizeof(v.m_size);
memcpy(current_address, v.m_data, v.m_size);
current_address += v.m_size;
}
if (whole_variant_size_in_bytes != 0)
{
char md5_sum[33];
md5sum(this->m_signature, (unsigned long)this->m_header.signature_size, md5_sum);
this->m_cache_entry_name = md5_sum;
}
else
{
this->m_cache_entry_name += ".db";
}
}
bool FFTBinaryLookup::loadHeader(std::ifstream &file, size_t length)
{
file.read ((char*)&this->m_header, sizeof(Header));
assert(getStorageMode(this->m_header.magic_key) == LSB);
if (this->m_header.whole_file_size != (int)length)
{
return false;
}
return true;
}
bool FFTBinaryLookup::loadBinaryAndSignature(std::ifstream &file)
{
{
this->m_binary = new unsigned char [this->m_header.binary_size];
const std::istream& res = file.read((char*)this->m_binary, this->m_header.binary_size);
if (!res.good())
return false;
}
{
if (this->m_signature != NULL)
{
delete[] this->m_signature;
this->m_signature = 0;
}
this->m_signature = new char [this->m_header.signature_size];
const std::istream& res = file.read((char*)this->m_signature, this->m_header.signature_size);
if (!res.good())
return false;
this->m_variants.clear();
char * current = this->m_signature;
for (size_t i=0 ; i<this->m_header.signature_size ; ++i)
{
Variant v;
v.m_kind = *(VariantKind*) current;
i += sizeof(int);
current += sizeof(int);
v.m_size = *(size_t*) current;
i += sizeof(size_t);
current += sizeof(size_t);
v.m_data = new char[v.m_size];
memcpy(v.m_data, current, v.m_size);
i += v.m_size;
current += v.m_size;
this->m_variants.push_back(v);
}
}
return true;
}
bool FFTBinaryLookup::tryLoadCacheFile()
{
const std::string & filename = this->m_path + this->m_cache_entry_name;
std::ifstream file (filename.c_str(), std::ios_base::binary);
if (file.is_open())
{
file.seekg (0, file.end);
size_t length = file.tellg();
file.seekg (0, file.beg);
if (length == 0)
{
return false;
}
bool st;
st = loadHeader(file, length);
if (! st)
return false;
st = loadBinaryAndSignature(file);
if (! st)
return false;
file.close();
return true;
}
else
{
return false;
}
}
bool FFTBinaryLookup::found()
{
if (! this->m_cache_enabled)
{
return false; }
this->finalizeVariant();
if (tryLoadCacheFile())
{
cl_int err = buildFromBinary(this->m_binary,
this->m_header.binary_size);
return err==CL_SUCCESS;
}
return false;
}
static cl_int getSingleBinaryFromProgram(cl_program program,
std::vector<unsigned char*> & binary)
{
size_t size;
cl_int err = clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES,
sizeof(size_t),
&size, NULL);
if (err != CL_SUCCESS)
{
std::cerr << "Error querying for program binary sizes" << std::endl;
return err;
}
binary.resize(size);
binary[0] = new unsigned char[size];
unsigned char * binary_address[1] = { binary[0] };
err = clGetProgramInfo(program, CL_PROGRAM_BINARIES, 1 * sizeof(unsigned char*),
binary_address, NULL);
if (err != CL_SUCCESS)
{
delete[] binary[0];
#if CAPS_DEBUG
std::cerr << "Error querying for program binaries" << std::endl;
#endif
return err;
}
return CL_SUCCESS;
}
cl_int FFTBinaryLookup::writeCacheFile(std::vector<unsigned char*> &data)
{
if (! this->m_cache_enabled)
{
return 0;
}
const std::string & filename = this->m_path + this->m_cache_entry_name;
CacheEntry cache_file(filename);
bool created = cache_file.exclusive_create();
if (created)
{
cache_file.close();
const std::string & filename = this->m_path + this->m_cache_entry_name;
std::ofstream file (filename.c_str(), std::ios_base::binary);
file.write((char*)&this->m_header, sizeof(m_header));
file.write((char*)data[0], this->m_header.binary_size);
file.write((char*)this->m_signature, this->m_header.signature_size);
file.close();
#if ENABLE_SOURCE_DUMP
const std::string & srcFilename = this->m_path + this->m_cache_entry_name + ".cl";
std::ofstream srcFile (srcFilename.c_str());
srcFile << this->m_source;
srcFile.close();
#endif
return CL_SUCCESS;
}
return 1;
}
cl_int FFTBinaryLookup::populateCache()
{
this->m_header.magic_key[0] = 'C';
this->m_header.magic_key[1] = 'L';
this->m_header.magic_key[2] = 'B';
this->m_header.magic_key[3] = '\0';
std::vector<unsigned char*> data;
cl_int err = getSingleBinaryFromProgram(this->m_program, data);
if (err != CL_SUCCESS)
{
return err;
}
this->m_header.header_size = sizeof(Header);
this->m_header.binary_size = data.size();
this->m_header.whole_file_size = this->m_header.header_size + this->m_header.binary_size + this->m_header.signature_size;
writeCacheFile(data); delete [] data[0];
return CL_SUCCESS;
}
cl_int FFTBinaryLookup::buildFromSource(const char * source)
{
cl_int err;
this->m_program = FFTBinaryLookup::buildProgramFromSource(source,
this->m_context,
this->m_device,
err);
if (err != CL_SUCCESS)
{
return err;
}
this->populateCache();
return CL_SUCCESS;
}
cl_int FFTBinaryLookup::buildFromLoadedBinary(const void * data,
size_t len)
{
cl_int err;
this->m_program = FFTBinaryLookup::buildProgramFromBinary((char*) data,
len,
this->m_context,
this->m_device,
err);
return err;
}
cl_int FFTBinaryLookup::buildFromBinary(const void * data,
size_t len)
{
cl_int err = buildFromLoadedBinary(data, len);
if (err != CL_SUCCESS)
return err;
this->populateCache();
return CL_SUCCESS;
}
cl_program FFTBinaryLookup::buildProgramFromSource(const char * source,
cl_context context,
cl_device_id device,
cl_int & err,
const char * options)
{
cl_program program = clCreateProgramWithSource(context, 1, (const char **)&source, NULL, &err);
if (err != CL_SUCCESS)
return NULL;
err = clBuildProgram(program,
1,
&device,
options,
NULL,
NULL);
if (err != CL_SUCCESS)
return NULL;
return program;
}
cl_program FFTBinaryLookup::buildProgramFromBinary(const char * data,
size_t data_size,
cl_context context,
cl_device_id device,
cl_int & err,
const char * options)
{
cl_program program = clCreateProgramWithBinary(context,
1, &device, &data_size, (const unsigned char **)&data,
NULL,
&err);
if (err != CL_SUCCESS)
{
return NULL;
}
err = clBuildProgram(program,
1,
&device,
options,
NULL,
NULL);
if (err != CL_SUCCESS)
{
return NULL;
}
return program;
}
cl_program FFTBinaryLookup::getProgram()
{
return this->m_program;
}
void FFTBinaryLookup::setProgram(cl_program program, const char * source)
{
this->m_program = program;
this->m_source = source;
}
static int make_directory(const std::string &path)
{
#ifdef _WIN32
return _mkdir (path.c_str());
#else
return mkdir (path.c_str(), S_IRWXU);
#endif
}
static void do_mkdir(const std::string &path)
{
int st = make_directory (path.c_str());
if (st != 0)
{
if ( errno != EEXIST )
{
std::string tmp = "Cannot not create directory '" + std::string(path) + "': ";
throw tmp;
}
}
}
cl_int FFTBinaryLookup::retrieveDeviceAndDriverInfo()
{
char m_device_vendor[SIZE];
char m_device_name[SIZE];
char m_driver_version[SIZE];
cl_int err = clGetDeviceInfo(this->m_device, CL_DEVICE_VENDOR, sizeof(m_device_vendor),
&m_device_vendor, NULL);
if (err != CL_SUCCESS)
{
return err;
}
err = clGetDeviceInfo(this->m_device, CL_DEVICE_NAME, sizeof(m_device_name),
&m_device_name, NULL);
if (err != CL_SUCCESS)
{
return err;
}
err = clGetDeviceInfo(this->m_device, CL_DRIVER_VERSION, sizeof(m_driver_version),
&m_driver_version, NULL);
if (err != CL_SUCCESS)
{
return err;
}
#if CAPS_DEBUG
fprintf(stderr, "device vendor = %s\n", this->m_device_vendor);
fprintf(stderr, "device name = %s\n", this->m_device_name);
fprintf(stderr, "driver version = %s\n", this->m_driver_version);
#endif
try
{
const std::string & root = (std::string(cache_path) + m_device_vendor + sep());
do_mkdir(root.c_str());
const std::string & root2 = (root + m_device_name + sep());
do_mkdir(root2.c_str());
const std::string & root3 = (root2 + m_driver_version + sep());
do_mkdir(root3.c_str());
const std::string & root4 = (root3 + this->m_cache_entry_name + sep());
do_mkdir(root4.c_str());
this->m_path = root4;
return CL_SUCCESS;
}
catch (std::string & e)
{
fprintf(stderr, "%s\n", e.c_str());
cache_enabled = false;
this->m_cache_enabled = false;
return CL_INVALID_VALUE;
}
}