opencl: better logging of devices and device selection
changes:
- Clew misses a lot of things, added defines needed for gathering
platform and device info.
- Refactored profile saving and loading to use libxml2 instead
the weird type of saving the profile data.
- Added an additional "log" file which is similar to the OpenGL
but it writes the OpenCL relevant information like which devices
and platforms are available (+ all the extra useful version
information) and which device was selected (if any at all).
Change-Id: I0fe793c756f8f4f1761fe120fc361df36e581903
Reviewed-on: https://gerrit.libreoffice.org/24270
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/external/clew/source/include/clew/clew.h b/external/clew/source/include/clew/clew.h
index f932b34..b23dc60 100644
--- a/external/clew/source/include/clew/clew.h
+++ b/external/clew/source/include/clew/clew.h
@@ -412,19 +412,86 @@ typedef struct {
#define CL_DEVICE_TYPE_CPU (1 << 1)
#define CL_DEVICE_TYPE_GPU (1 << 2)
#define CL_DEVICE_TYPE_ACCELERATOR (1 << 3)
#define CL_DEVICE_TYPE_CUSTOM (1 << 4)
#define CL_DEVICE_TYPE_ALL 0xFFFFFFFF
// cl_device_info
#define CL_DEVICE_TYPE 0x1000
#define CL_DEVICE_VENDOR_ID 0x1001
#define CL_DEVICE_MAX_COMPUTE_UNITS 0x1002
#define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 0x1003
#define CL_DEVICE_MAX_WORK_GROUP_SIZE 0x1004
#define CL_DEVICE_MAX_WORK_ITEM_SIZES 0x1005
#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR 0x1006
#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT 0x1007
#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT 0x1008
#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG 0x1009
#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT 0x100A
#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE 0x100B
#define CL_DEVICE_MAX_CLOCK_FREQUENCY 0x100C
#define CL_DEVICE_ADDRESS_BITS 0x100D
#define CL_DEVICE_MAX_READ_IMAGE_ARGS 0x100E
#define CL_DEVICE_MAX_WRITE_IMAGE_ARGS 0x100F
#define CL_DEVICE_MAX_MEM_ALLOC_SIZE 0x1010
#define CL_DEVICE_IMAGE2D_MAX_WIDTH 0x1011
#define CL_DEVICE_IMAGE2D_MAX_HEIGHT 0x1012
#define CL_DEVICE_IMAGE3D_MAX_WIDTH 0x1013
#define CL_DEVICE_IMAGE3D_MAX_HEIGHT 0x1014
#define CL_DEVICE_IMAGE3D_MAX_DEPTH 0x1015
#define CL_DEVICE_IMAGE_SUPPORT 0x1016
#define CL_DEVICE_MAX_PARAMETER_SIZE 0x1017
#define CL_DEVICE_MAX_SAMPLERS 0x1018
#define CL_DEVICE_MEM_BASE_ADDR_ALIGN 0x1019
#define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE 0x101A
#define CL_DEVICE_SINGLE_FP_CONFIG 0x101B
#define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE 0x101C
#define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE 0x101D
#define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE 0x101E
#define CL_DEVICE_GLOBAL_MEM_SIZE 0x101F
#define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE 0x1020
#define CL_DEVICE_MAX_CONSTANT_ARGS 0x1021
#define CL_DEVICE_LOCAL_MEM_TYPE 0x1022
#define CL_DEVICE_LOCAL_MEM_SIZE 0x1023
#define CL_DEVICE_ERROR_CORRECTION_SUPPORT 0x1024
#define CL_DEVICE_PROFILING_TIMER_RESOLUTION 0x1025
#define CL_DEVICE_ENDIAN_LITTLE 0x1026
#define CL_DEVICE_AVAILABLE 0x1027
#define CL_DEVICE_COMPILER_AVAILABLE 0x1028
#define CL_DEVICE_EXECUTION_CAPABILITIES 0x1029
#define CL_DEVICE_QUEUE_PROPERTIES 0x102A
#define CL_DEVICE_NAME 0x102B
#define CL_DEVICE_VENDOR 0x102C
#define CL_DRIVER_VERSION 0x102D
#define CL_DEVICE_PROFILE 0x102E
#define CL_DEVICE_VERSION 0x102F
#define CL_DEVICE_EXTENSIONS 0x1030
#define CL_DEVICE_PLATFORM 0x1031
#define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032
/* 0x1033 reserved for CL_DEVICE_HALF_FP_CONFIG */
#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF 0x1034
#define CL_DEVICE_HOST_UNIFIED_MEMORY 0x1035
#define CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR 0x1036
#define CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT 0x1037
#define CL_DEVICE_NATIVE_VECTOR_WIDTH_INT 0x1038
#define CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG 0x1039
#define CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT 0x103A
#define CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE 0x103B
#define CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF 0x103C
#define CL_DEVICE_OPENCL_C_VERSION 0x103D
#define CL_DEVICE_LINKER_AVAILABLE 0x103E
#define CL_DEVICE_BUILT_IN_KERNELS 0x103F
#define CL_DEVICE_IMAGE_MAX_BUFFER_SIZE 0x1040
#define CL_DEVICE_IMAGE_MAX_ARRAY_SIZE 0x1041
#define CL_DEVICE_PARENT_DEVICE 0x1042
#define CL_DEVICE_PARTITION_MAX_SUB_DEVICES 0x1043
#define CL_DEVICE_PARTITION_PROPERTIES 0x1044
#define CL_DEVICE_PARTITION_AFFINITY_DOMAIN 0x1045
#define CL_DEVICE_PARTITION_TYPE 0x1046
#define CL_DEVICE_REFERENCE_COUNT 0x1047
#define CL_DEVICE_PREFERRED_INTEROP_USER_SYNC 0x1048
#define CL_DEVICE_PRINTF_BUFFER_SIZE 0x1049
#define CL_DEVICE_IMAGE_PITCH_ALIGNMENT 0x104A
#define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT 0x104B
// cl_device_fp_config - bitfield
#define CL_FP_DENORM (1 << 0)
diff --git a/opencl/Library_opencl.mk b/opencl/Library_opencl.mk
index eb83220..501be30 100644
--- a/opencl/Library_opencl.mk
+++ b/opencl/Library_opencl.mk
@@ -24,6 +24,7 @@ $(eval $(call gb_Library_use_externals,opencl,\
icu_headers \
icui18n \
icuuc \
libxml2 \
))
$(eval $(call gb_Library_use_custom_headers,opencl,\
@@ -37,6 +38,7 @@ $(eval $(call gb_Library_use_libraries,opencl,\
comphelper \
cppu \
sal \
tl \
))
$(eval $(call gb_Library_add_exception_objects,opencl,\
diff --git a/opencl/inc/opencl_device.hxx b/opencl/inc/opencl_device.hxx
index 3e9bdef..7b5b79ae 100644
--- a/opencl/inc/opencl_device.hxx
+++ b/opencl/inc/opencl_device.hxx
@@ -14,7 +14,7 @@
namespace opencl {
ds_device getDeviceSelection(const char* pFileName, bool bForceSelection = false);
ds_device getDeviceSelection(OUString pFileName, bool bForceSelection = false);
}
diff --git a/opencl/inc/opencl_device_selection.h b/opencl/inc/opencl_device_selection.h
index b6a30fd..cf57b31 100644
--- a/opencl/inc/opencl_device_selection.h
+++ b/opencl/inc/opencl_device_selection.h
@@ -19,8 +19,12 @@
#include <string.h>
#include <clew/clew.h>
#include <libxml/xmlwriter.h>
#include <libxml/xmlstring.h>
#include <tools/stream.hxx>
#include <rtl/math.hxx>
#define DS_DEVICE_NAME_LENGTH 256
#include <vector>
enum ds_status
{
@@ -38,84 +42,106 @@ enum ds_status
};
// device type
enum ds_device_type
enum class DeviceType
{
DS_DEVICE_NATIVE_CPU = 0
,DS_DEVICE_OPENCL_DEVICE
None,
NativeCPU,
OpenCLDevice
};
struct ds_device
{
ds_device_type type;
cl_device_id oclDeviceID;
char* oclPlatformVendor;
char* oclDeviceName;
char* oclDriverVersion;
void* score; // a pointer to the score data, the content/format is application defined
DeviceType eType;
cl_device_id aDeviceID;
OString sPlatformName;
OString sPlatformVendor;
OString sPlatformVersion;
OString sPlatformProfile;
OString sPlatformExtensions;
OString sDeviceName;
OString sDeviceVendor;
OString sDeviceVersion;
OString sDriverVersion;
OString sDeviceType;
OString sDeviceExtensions;
OString sDeviceOpenCLVersion;
bool bDeviceAvailable;
bool bDeviceCompilerAvailable;
bool bDeviceLinkerAvailable;
double fTime; // small time means faster device
bool bErrors; // were there any opencl errors
};
struct ds_profile
{
unsigned int numDevices;
ds_device* devices;
const char* version;
std::vector<ds_device> devices;
OString version;
ds_profile(OString& inVersion)
: version(inVersion)
{}
};
// deallocate memory used by score
typedef ds_status(* ds_score_release)(void* score);
inline ds_status releaseDSProfile(ds_profile* profile, ds_score_release sr)
inline OString getPlatformInfoString(cl_platform_id aPlatformId, cl_platform_info aPlatformInfo)
{
ds_status status = DS_SUCCESS;
if (profile != NULL)
{
if (profile->devices != NULL && sr != NULL)
{
unsigned int i;
for (i = 0; i < profile->numDevices; i++)
{
free(profile->devices[i].oclPlatformVendor);
free(profile->devices[i].oclDeviceName);
free(profile->devices[i].oclDriverVersion);
status = sr(profile->devices[i].score);
if (status != DS_SUCCESS) break;
}
free(profile->devices);
}
free(profile);
}
return status;
std::vector<char> temporary(2048, 0);
clGetPlatformInfo(aPlatformId, aPlatformInfo, temporary.size(), temporary.data(), nullptr);
return OString(temporary.data());
}
inline OString getDeviceInfoString(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
{
std::vector<char> temporary(2048, 0);
clGetDeviceInfo(aDeviceId, aDeviceInfo, temporary.size(), temporary.data(), nullptr);
return OString(temporary.data());
}
inline ds_status initDSProfile(ds_profile** p, const char* version)
inline OString getDeviceType(cl_device_id aDeviceId)
{
OString sType = "";
cl_device_type aDeviceType;
clGetDeviceInfo(aDeviceId, CL_DEVICE_TYPE, sizeof(aDeviceType), &aDeviceType, nullptr);
if (aDeviceType & CL_DEVICE_TYPE_CPU)
sType += "cpu ";
if (aDeviceType & CL_DEVICE_TYPE_GPU)
sType += "gpu ";
if (aDeviceType & CL_DEVICE_TYPE_ACCELERATOR)
sType += "accelerator ";
if (aDeviceType & CL_DEVICE_TYPE_CUSTOM)
sType += "custom ";
if (aDeviceType & CL_DEVICE_TYPE_DEFAULT)
sType += "default ";
return sType;
}
inline bool getDeviceInfoBool(cl_device_id aDeviceId, cl_device_info aDeviceInfo)
{
cl_bool bCLBool;
clGetDeviceInfo(aDeviceId, aDeviceInfo, sizeof(bCLBool), &bCLBool, nullptr);
return bCLBool == CL_TRUE;
}
inline ds_status initDSProfile(std::unique_ptr<ds_profile>& rProfile, OString rVersion)
{
int numDevices;
cl_uint numPlatforms;
cl_platform_id* platforms = NULL;
cl_device_id* devices = NULL;
ds_status status = DS_SUCCESS;
ds_profile* profile = NULL;
std::vector<cl_platform_id> platforms;
std::vector<cl_device_id> devices;
unsigned int next;
unsigned int i;
if (p == NULL) return DS_INVALID_PROFILE;
profile = static_cast<ds_profile*>(malloc(sizeof(ds_profile)));
if (profile == NULL) return DS_MEMORY_ERROR;
memset(profile, 0, sizeof(ds_profile));
rProfile = std::unique_ptr<ds_profile>(new ds_profile(rVersion));
clGetPlatformIDs(0, NULL, &numPlatforms);
if (numPlatforms != 0)
{
platforms = static_cast<cl_platform_id*>(malloc(numPlatforms * sizeof(cl_platform_id)));
if (platforms == NULL)
{
status = DS_MEMORY_ERROR;
goto cleanup;
}
clGetPlatformIDs(numPlatforms, platforms, NULL);
platforms.resize(numPlatforms);
clGetPlatformIDs(numPlatforms, platforms.data(), NULL);
}
numDevices = 0;
@@ -134,35 +160,27 @@ inline ds_status initDSProfile(ds_profile** p, const char* version)
}
numDevices += num;
}
if (numDevices != 0)
{
devices = static_cast<cl_device_id*>(malloc(numDevices * sizeof(cl_device_id)));
if (devices == NULL)
{
status = DS_MEMORY_ERROR;
goto cleanup;
}
devices.resize(numDevices);
}
profile->numDevices = numDevices + 1; // +1 to numDevices to include the native CPU
profile->devices = static_cast<ds_device*>(malloc(profile->numDevices * sizeof(ds_device)));
if (profile->devices == NULL)
{
profile->numDevices = 0;
status = DS_MEMORY_ERROR;
goto cleanup;
}
memset(profile->devices, 0, profile->numDevices * sizeof(ds_device));
rProfile->devices.resize(numDevices + 1); // +1 to numDevices to include the native CPU
next = 0;
for (i = 0; i < (unsigned int)numPlatforms; i++)
{
cl_uint num = 0;
unsigned j;
char vendor[256];
if (clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(vendor), vendor, NULL) != CL_SUCCESS)
vendor[0] = '\0';
cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices, &num);
OString sPlatformProfile = getPlatformInfoString(platforms[i], CL_PLATFORM_PROFILE);
OString sPlatformVersion = getPlatformInfoString(platforms[i], CL_PLATFORM_VERSION);
OString sPlatformName = getPlatformInfoString(platforms[i], CL_PLATFORM_NAME);
OString sPlatformVendor = getPlatformInfoString(platforms[i], CL_PLATFORM_VENDOR);
OString sPlatformExts = getPlatformInfoString(platforms[i], CL_PLATFORM_EXTENSIONS);
cl_int err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, numDevices, devices.data(), &num);
if (err != CL_SUCCESS)
{
/* we want to catch at least the case when the call returns
@@ -174,470 +192,369 @@ inline ds_status initDSProfile(ds_profile** p, const char* version)
}
for (j = 0; j < num; j++, next++)
{
char buffer[DS_DEVICE_NAME_LENGTH];
size_t length;
cl_device_id aDeviceID = devices[j];
profile->devices[next].type = DS_DEVICE_OPENCL_DEVICE;
profile->devices[next].oclDeviceID = devices[j];
ds_device& rDevice = rProfile->devices[next];
rDevice.eType = DeviceType::OpenCLDevice;
rDevice.aDeviceID = aDeviceID;
profile->devices[next].oclPlatformVendor = strdup(vendor);
rDevice.sPlatformName = sPlatformName;
rDevice.sPlatformVendor = sPlatformVendor;
rDevice.sPlatformVersion = sPlatformVersion;
rDevice.sPlatformProfile = sPlatformProfile;
rDevice.sPlatformExtensions = sPlatformExts;
clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DEVICE_NAME
, DS_DEVICE_NAME_LENGTH, &buffer, NULL);
length = strlen(buffer);
profile->devices[next].oclDeviceName = static_cast<char*>(malloc(length + 1));
memcpy(profile->devices[next].oclDeviceName, buffer, length + 1);
rDevice.sDeviceName = getDeviceInfoString(aDeviceID, CL_DEVICE_NAME);
rDevice.sDeviceVendor = getDeviceInfoString(aDeviceID, CL_DEVICE_VENDOR);
rDevice.sDeviceVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_VERSION);
rDevice.sDriverVersion = getDeviceInfoString(aDeviceID, CL_DRIVER_VERSION);
rDevice.sDeviceType = getDeviceType(aDeviceID);
rDevice.sDeviceExtensions = getDeviceInfoString(aDeviceID, CL_DEVICE_EXTENSIONS);
rDevice.sDeviceOpenCLVersion = getDeviceInfoString(aDeviceID, CL_DEVICE_OPENCL_C_VERSION);
clGetDeviceInfo(profile->devices[next].oclDeviceID, CL_DRIVER_VERSION
, DS_DEVICE_NAME_LENGTH, &buffer, NULL);
length = strlen(buffer);
profile->devices[next].oclDriverVersion = static_cast<char*>(malloc(length + 1));
memcpy(profile->devices[next].oclDriverVersion, buffer, length + 1);
rDevice.bDeviceAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_AVAILABLE);
rDevice.bDeviceCompilerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_COMPILER_AVAILABLE);
rDevice.bDeviceLinkerAvailable = getDeviceInfoBool(aDeviceID, CL_DEVICE_LINKER_AVAILABLE);
}
}
profile->devices[next].type = DS_DEVICE_NATIVE_CPU;
profile->version = version;
rProfile->devices[next].eType = DeviceType::NativeCPU;
cleanup:
if (platforms) free(platforms);
if (devices) free(devices);
if (status == DS_SUCCESS)
{
*p = profile;
}
else
{
if (profile)
{
if (profile->devices) free(profile->devices);
free(profile);
}
}
return status;
return DS_SUCCESS;
}
// Pointer to a function that calculates the score of a device (ex: device->score)
// update the data size of score. The encoding and the format of the score data
// is implementation defined. The function should return DS_SUCCESS if there's no error to be reported.
typedef ds_status(* ds_perf_evaluator)(ds_device* device, void* data);
typedef enum {
DS_EVALUATE_ALL
, DS_EVALUATE_NEW_ONLY
} ds_evaluation_type;
inline ds_status profileDevices(ds_profile* profile, const ds_evaluation_type type,
ds_perf_evaluator evaluator, void* evaluatorData, unsigned int* numUpdates)
namespace
{
ds_status status = DS_SUCCESS;
unsigned int i;
unsigned int updates = 0;
if (profile == NULL)
/**
* XmlWriter writes a XML to a SvStream. It uses libxml2 for writing but hides
* all the internal libxml2 workings and uses types that are native for LO
* development.
*
* The codepage used for XML is always "utf-8" and the output is indented so it
* is easier to read.
*
* TODO: move to common code
*/
class XmlWriter
{
private:
SvStream* mpStream;
xmlTextWriterPtr mpWriter;
static int funcWriteCallback(void* pContext, const char* sBuffer, int nLen)
{
return DS_INVALID_PROFILE;
}
if (evaluator == NULL)
{
return DS_INVALID_PERF_EVALUATOR;
SvStream* pStream = static_cast<SvStream*>(pContext);
return static_cast<int>(pStream->Write(sBuffer, nLen));
}
for (i = 0; i < profile->numDevices; i++)
static int funcCloseCallback(void* pContext)
{
ds_status evaluatorStatus;
SvStream* pStream = static_cast<SvStream*>(pContext);
pStream->Flush();
return 0; // 0 or -1 in case of error
}
switch (type)
public:
XmlWriter(SvStream* pStream)
: mpStream(pStream)
, mpWriter(nullptr)
{
}
~XmlWriter()
{
if (mpWriter != nullptr)
endDocument();
}
bool startDocument()
{
xmlOutputBufferPtr xmlOutBuffer = xmlOutputBufferCreateIO(funcWriteCallback, funcCloseCallback, mpStream, nullptr);
mpWriter = xmlNewTextWriter(xmlOutBuffer);
if (mpWriter == nullptr)
return false;
xmlTextWriterSetIndent(mpWriter, 1);
xmlTextWriterStartDocument(mpWriter, nullptr, "UTF-8", nullptr);
return true;
}
void endDocument()
{
xmlTextWriterEndDocument(mpWriter);
xmlFreeTextWriter(mpWriter);
mpWriter = nullptr;
}
void startElement(const OString& sName)
{
xmlChar* xmlName = xmlCharStrdup(sName.getStr());
xmlTextWriterStartElement(mpWriter, xmlName);
xmlFree(xmlName);
}
void endElement()
{
xmlTextWriterEndElement(mpWriter);
}
void content(const OString& sValue)
{
xmlChar* xmlValue = xmlCharStrdup(sValue.getStr());
xmlTextWriterWriteString(mpWriter, xmlValue);
xmlFree(xmlValue);
}
};
/**
* XmlWalker main purpose is to make it easier for walking the
* parsed XML DOM tree.
*
* It hides all the libxml2 and C -isms and makes the useage more
* confortable from LO developer point of view.
*
* TODO: move to common code
*/
class XmlWalker
{
private:
xmlDocPtr mpDocPtr;
xmlNodePtr mpRoot;
xmlNodePtr mpCurrent;
std::vector<xmlNodePtr> mpStack;
public:
XmlWalker()
{}
~XmlWalker()
{
xmlFreeDoc(mpDocPtr);
}
bool open(SvStream* pStream)
{
sal_Size nSize = pStream->remainingSize();
std::vector<sal_uInt8> aBuffer(nSize + 1);
pStream->Read(aBuffer.data(), nSize);
aBuffer[nSize] = 0;
mpDocPtr = xmlParseDoc(reinterpret_cast<xmlChar*>(aBuffer.data()));
if (mpDocPtr == nullptr)
return false;
mpRoot = xmlDocGetRootElement(mpDocPtr);
mpCurrent = mpRoot;
mpStack.push_back(mpCurrent);
return true;
}
OString name()
{
return OString(reinterpret_cast<const char*>(mpCurrent->name));
}
OString content()
{
OString aContent;
if (mpCurrent->xmlChildrenNode != nullptr)
{
case DS_EVALUATE_NEW_ONLY:
if (profile->devices[i].score != NULL) break;
// else fall through
case DS_EVALUATE_ALL:
evaluatorStatus = evaluator(profile->devices + i, evaluatorData);
if (evaluatorStatus != DS_SUCCESS)
{
status = evaluatorStatus;
return status;
}
updates++;
xmlChar* pContent = xmlNodeListGetString(mpDocPtr, mpCurrent->xmlChildrenNode, 1);
aContent = OString(reinterpret_cast<const char*>(pContent));
xmlFree(pContent);
}
return aContent;
}
void children()
{
mpStack.push_back(mpCurrent);
mpCurrent = mpCurrent->xmlChildrenNode;
}
void parent()
{
mpCurrent = mpStack.back();
mpStack.pop_back();
}
void next()
{
mpCurrent = mpCurrent->next;
}
bool isValid()
{
return mpCurrent != nullptr;
}
};
} // end anonymous namespace
inline ds_status writeProfile(const OUString& rStreamName, std::unique_ptr<ds_profile>& pProfile)
{
if (pProfile == nullptr)
return DS_INVALID_PROFILE;
if (rStreamName.isEmpty())
return DS_INVALID_PROFILE;
std::unique_ptr<SvStream> pStream;
pStream.reset(new SvFileStream(rStreamName, STREAM_STD_READWRITE | StreamMode::TRUNC));
XmlWriter aXmlWriter(pStream.get());
if (aXmlWriter.startDocument() == false)
return DS_FILE_ERROR;
aXmlWriter.startElement("profile");
aXmlWriter.startElement("version");
aXmlWriter.content(OString(pProfile->version));
aXmlWriter.endElement();
for (ds_device& rDevice : pProfile->devices)
{
aXmlWriter.startElement("device");
switch(rDevice.eType)
{
case DeviceType::NativeCPU:
aXmlWriter.startElement("type");
aXmlWriter.content("native");
aXmlWriter.endElement();
break;
case DeviceType::OpenCLDevice:
aXmlWriter.startElement("type");
aXmlWriter.content("opencl");
aXmlWriter.endElement();
aXmlWriter.startElement("name");
aXmlWriter.content(OString(rDevice.sDeviceName));
aXmlWriter.endElement();
aXmlWriter.startElement("driver");
aXmlWriter.content(OString(rDevice.sDriverVersion));
aXmlWriter.endElement();
break;
default:
return DS_INVALID_PERF_EVALUATOR_TYPE;
break;
};
}
if (numUpdates) *numUpdates = updates;
return status;
}
#define DS_TAG_VERSION "<version>"
#define DS_TAG_VERSION_END "</version>"
#define DS_TAG_DEVICE "<device>"
#define DS_TAG_DEVICE_END "</device>"
#define DS_TAG_SCORE "<score>"
#define DS_TAG_SCORE_END "</score>"
#define DS_TAG_DEVICE_TYPE "<type>"
#define DS_TAG_DEVICE_TYPE_END "</type>"
#define DS_TAG_DEVICE_NAME "<name>"
#define DS_TAG_DEVICE_NAME_END "</name>"
#define DS_TAG_DEVICE_DRIVER_VERSION "<driver>"
#define DS_TAG_DEVICE_DRIVER_VERSION_END "</driver>"
#define DS_DEVICE_NATIVE_CPU_STRING "native_cpu"
typedef ds_status(* ds_score_serializer)(ds_device* device, void** serializedScore, unsigned int* serializedScoreSize);
inline ds_status writeProfileToFile(ds_profile* profile, ds_score_serializer serializer, const char* file)
{
ds_status status = DS_SUCCESS;
FILE* profileFile = NULL;
if (profile == NULL) return DS_INVALID_PROFILE;
profileFile = fopen(file, "wb");
if (profileFile == NULL)
{
status = DS_FILE_ERROR;
}
else
{
unsigned int i;
// write version string
fwrite(DS_TAG_VERSION, sizeof(char), strlen(DS_TAG_VERSION), profileFile);
fwrite(profile->version, sizeof(char), strlen(profile->version), profileFile);
fwrite(DS_TAG_VERSION_END, sizeof(char), strlen(DS_TAG_VERSION_END), profileFile);
fwrite("\n", sizeof(char), 1, profileFile);
for (i = 0; i < profile->numDevices && status == DS_SUCCESS; i++)
{
void* serializedScore;
unsigned int serializedScoreSize;
fwrite(DS_TAG_DEVICE, sizeof(char), strlen(DS_TAG_DEVICE), profileFile);
fwrite(DS_TAG_DEVICE_TYPE, sizeof(char), strlen(DS_TAG_DEVICE_TYPE), profileFile);
fwrite(&profile->devices[i].type, sizeof(ds_device_type), 1, profileFile);
fwrite(DS_TAG_DEVICE_TYPE_END, sizeof(char), strlen(DS_TAG_DEVICE_TYPE_END), profileFile);
switch (profile->devices[i].type)
{
case DS_DEVICE_NATIVE_CPU:
{
// There's no need to emit a device name for the native CPU device.
/*
fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
fwrite(DS_DEVICE_NATIVE_CPU_STRING,sizeof(char),strlen(DS_DEVICE_NATIVE_CPU_STRING), profileFile);
fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
*/
}
break;
case DS_DEVICE_OPENCL_DEVICE:
{
fwrite(DS_TAG_DEVICE_NAME, sizeof(char), strlen(DS_TAG_DEVICE_NAME), profileFile);
fwrite(profile->devices[i].oclDeviceName, sizeof(char), strlen(profile->devices[i].oclDeviceName), profileFile);
fwrite(DS_TAG_DEVICE_NAME_END, sizeof(char), strlen(DS_TAG_DEVICE_NAME_END), profileFile);
fwrite(DS_TAG_DEVICE_DRIVER_VERSION, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION), profileFile);
fwrite(profile->devices[i].oclDriverVersion, sizeof(char), strlen(profile->devices[i].oclDriverVersion), profileFile);
fwrite(DS_TAG_DEVICE_DRIVER_VERSION_END, sizeof(char), strlen(DS_TAG_DEVICE_DRIVER_VERSION_END), profileFile);
}
break;
default:
break;
};
fwrite(DS_TAG_SCORE, sizeof(char), strlen(DS_TAG_SCORE), profileFile);
status = serializer(profile->devices + i, &serializedScore, &serializedScoreSize);
if (status == DS_SUCCESS && serializedScore != NULL && serializedScoreSize > 0)
{
fwrite(serializedScore, sizeof(char), serializedScoreSize, profileFile);
free(serializedScore);
}
fwrite(DS_TAG_SCORE_END, sizeof(char), strlen(DS_TAG_SCORE_END), profileFile);
fwrite(DS_TAG_DEVICE_END, sizeof(char), strlen(DS_TAG_DEVICE_END), profileFile);
fwrite("\n", sizeof(char), 1, profileFile);
}
fclose(profileFile);
}
return status;
}
aXmlWriter.startElement("time");
if (rDevice.fTime == DBL_MAX)
aXmlWriter.content("max");
else
aXmlWriter.content(OString::number(rDevice.fTime));
aXmlWriter.endElement();
inline ds_status readProFile(const char* fileName, char** content, size_t* contentSize)
{
FILE* input = NULL;
size_t size = 0;
char* binary = NULL;
long pos = -1;
aXmlWriter.startElement("errors");
aXmlWriter.content(rDevice.bErrors ? "true" : "false");
aXmlWriter.endElement();
*contentSize = 0;
*content = NULL;
input = fopen(fileName, "rb");
if (input == NULL)
{
return DS_FILE_ERROR;
aXmlWriter.endElement();
}
fseek(input, 0L, SEEK_END);
pos = ftell(input);
if (pos < 0)
{
fclose(input);
return DS_FILE_ERROR;
}
aXmlWriter.endElement();
aXmlWriter.endDocument();
size = pos;
rewind(input);
binary = static_cast<char*>(malloc(size));
if (binary == NULL)
{
fclose(input);
return DS_FILE_ERROR;
}
size_t bytesRead = fread(binary, sizeof(char), size, input);
(void) bytesRead; // avoid warning
fclose(input);
*contentSize = size;
*content = binary;
return DS_SUCCESS;
}
inline const char* findString(const char* contentStart, const char* contentEnd, const char* string)
inline ds_status readProfile(const OUString& rStreamName, std::unique_ptr<ds_profile>& pProfile)
{
size_t stringLength;
const char* currentPosition;
const char* found;
found = NULL;
stringLength = strlen(string);
currentPosition = contentStart;
for (currentPosition = contentStart; currentPosition < contentEnd; currentPosition++)
ds_status eStatus = DS_SUCCESS;
if (rStreamName.isEmpty())
return DS_INVALID_PROFILE;
std::unique_ptr<SvStream> pStream;
pStream.reset(new SvFileStream(rStreamName, StreamMode::READ));
XmlWalker aWalker;
if (!aWalker.open(pStream.get()))
return DS_FILE_ERROR;
if (aWalker.name() == "profile")
{
if (*currentPosition == string[0])
aWalker.children();
while (aWalker.isValid())
{
if (currentPosition + stringLength < contentEnd)
if (aWalker.name() == "version")
{
if (strncmp(currentPosition, string, stringLength) == 0)
{
found = currentPosition;
break;
}
if (aWalker.content() != pProfile->version)
return DS_PROFILE_FILE_ERROR;
}
}
}
return found;
}
typedef ds_status(* ds_score_deserializer)(ds_device* device, const unsigned char* serializedScore, unsigned int serializedScoreSize);
inline ds_status readProfileFromFile(ds_profile* profile, ds_score_deserializer deserializer, const char* file)
{
ds_status status = DS_SUCCESS;
char* contentStart = NULL;
const char* contentEnd = NULL;
size_t contentSize;
if (profile == NULL) return DS_INVALID_PROFILE;
status = readProFile(file, &contentStart, &contentSize);
if (status == DS_SUCCESS)
{
const char* currentPosition;
const char* dataStart;
const char* dataEnd;
size_t versionStringLength;
contentEnd = contentStart + contentSize;
currentPosition = contentStart;
// parse the version string
dataStart = findString(currentPosition, contentEnd, DS_TAG_VERSION);
if (dataStart == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
dataStart += strlen(DS_TAG_VERSION);
dataEnd = findString(dataStart, contentEnd, DS_TAG_VERSION_END);
if (dataEnd == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
versionStringLength = strlen(profile->version);
if (versionStringLength != static_cast<size_t>(dataEnd - dataStart)
|| strncmp(profile->version, dataStart, versionStringLength) != 0)
{
// version mismatch
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
currentPosition = dataEnd + strlen(DS_TAG_VERSION_END);
// parse the device information
while (true)
{
unsigned int i;
const char* deviceTypeStart;
const char* deviceTypeEnd;
ds_device_type deviceType;
const char* deviceNameStart;
const char* deviceNameEnd;
const char* deviceScoreStart;
const char* deviceScoreEnd;
const char* deviceDriverStart;
const char* deviceDriverEnd;
dataStart = findString(currentPosition, contentEnd, DS_TAG_DEVICE);
if (dataStart == NULL)
else if (aWalker.name() == "device")
{
// nothing useful remain, quit...
break;
}
dataStart += strlen(DS_TAG_DEVICE);
dataEnd = findString(dataStart, contentEnd, DS_TAG_DEVICE_END);
if (dataEnd == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
aWalker.children();
// parse the device type
deviceTypeStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_TYPE);
if (deviceTypeStart == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
deviceTypeStart += strlen(DS_TAG_DEVICE_TYPE);
deviceTypeEnd = findString(deviceTypeStart, contentEnd, DS_TAG_DEVICE_TYPE_END);
if (deviceTypeEnd == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
memcpy(&deviceType, deviceTypeStart, sizeof(ds_device_type));
DeviceType eDeviceType = DeviceType::None;
OString sName;
OString sVersion;
double fTime = -1.0;
bool bErrors = true;
// parse the device name
if (deviceType == DS_DEVICE_OPENCL_DEVICE)
{
deviceNameStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_NAME);
if (deviceNameStart == NULL)
while (aWalker.isValid())
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
deviceNameStart += strlen(DS_TAG_DEVICE_NAME);
deviceNameEnd = findString(deviceNameStart, contentEnd, DS_TAG_DEVICE_NAME_END);
if (deviceNameEnd == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
deviceDriverStart = findString(dataStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION);
if (deviceDriverStart == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
deviceDriverStart += strlen(DS_TAG_DEVICE_DRIVER_VERSION);
deviceDriverEnd = findString(deviceDriverStart, contentEnd, DS_TAG_DEVICE_DRIVER_VERSION_END);
if (deviceDriverEnd == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
// check if this device is on the system
for (i = 0; i < profile->numDevices; i++)
{
if (profile->devices[i].type == DS_DEVICE_OPENCL_DEVICE)
if (aWalker.name() == "type")
{
size_t actualDeviceNameLength;
size_t driverVersionLength;
OString sContent = aWalker.content();
if (sContent == "native")
eDeviceType = DeviceType::NativeCPU;
else if (sContent == "opencl")
eDeviceType = DeviceType::OpenCLDevice;
else
return DS_PROFILE_FILE_ERROR;
}
else if (aWalker.name() == "name")
{
sName = aWalker.content();
}
else if (aWalker.name() == "driver")
{
sVersion = aWalker.content();
}
else if (aWalker.name() == "time")
{
if (aWalker.content() == "max")
fTime = DBL_MAX;
else
fTime = aWalker.content().toDouble();
}
else if (aWalker.name() == "errors")
{
bErrors = (aWalker.content() == "true");
}
actualDeviceNameLength = strlen(profile->devices[i].oclDeviceName);
driverVersionLength = strlen(profile->devices[i].oclDriverVersion);
if (actualDeviceNameLength == static_cast<size_t>(deviceNameEnd - deviceNameStart)
&& driverVersionLength == static_cast<size_t>(deviceDriverEnd - deviceDriverStart)
&& strncmp(profile->devices[i].oclDeviceName, deviceNameStart, actualDeviceNameLength) == 0
&& strncmp(profile->devices[i].oclDriverVersion, deviceDriverStart, driverVersionLength) == 0)
aWalker.next();
}
if (fTime < 0.0)
return DS_PROFILE_FILE_ERROR;
for (ds_device& rDevice : pProfile->devices)
{
// type matches? either both are DS_DEVICE_OPENCL_DEVICE or DS_DEVICE_NATIVE_CPU
if (rDevice.eType == eDeviceType)
{
// is DS_DEVICE_NATIVE_CPU or name + version matches?
if (eDeviceType == DeviceType::NativeCPU ||
(sName == OString(rDevice.sDeviceName) &&
sVersion == OString(rDevice.sDriverVersion)))
{
deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
if (deviceScoreStart == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
deviceScoreStart += strlen(DS_TAG_SCORE);
deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
status = deserializer(profile->devices + i, reinterpret_cast<const unsigned char*>(deviceScoreStart), deviceScoreEnd - deviceScoreStart);
if (status != DS_SUCCESS)
{
goto cleanup;
}
rDevice.fTime = fTime;
rDevice.bErrors = bErrors;
}
}
}
aWalker.parent();
}
else if (deviceType == DS_DEVICE_NATIVE_CPU)
{
for (i = 0; i < profile->numDevices; i++)
{
if (profile->devices[i].type == DS_DEVICE_NATIVE_CPU)
{
deviceScoreStart = findString(dataStart, contentEnd, DS_TAG_SCORE);
if (deviceScoreStart == NULL)
{
status = DS_PROFILE_FILE_ERROR;
goto cleanup;
}
deviceScoreStart += strlen(DS_TAG_SCORE);
deviceScoreEnd = findString(deviceScoreStart, contentEnd, DS_TAG_SCORE_END);
status = deserializer(profile->devices + i, reinterpret_cast<const unsigned char*>(deviceScoreStart), deviceScoreEnd - deviceScoreStart);
if (status != DS_SUCCESS)
{
goto cleanup;
}
}
}
}
// skip over the current one to find the next device
currentPosition = dataEnd + strlen(DS_TAG_DEVICE_END);
aWalker.next();
}
aWalker.parent();
}
cleanup:
if (contentStart != NULL) free(contentStart);
if (status != DS_SUCCESS)
return status;
// Check that all the devices present had valid cached scores. If
// not, return DS_INVALID_PROFILE and let the caller re-evaluate
// scores for present devices, and write a new profile file.
for (unsigned int i = 0; i < profile->numDevices; i++)
if (profile->devices[i].score == NULL)
return DS_INVALID_PROFILE;
return DS_SUCCESS;
return eStatus;
}
#endif
diff --git a/opencl/source/opencl_device.cxx b/opencl/source/opencl_device.cxx
index 32a94df..af3bc18 100644
--- a/opencl/source/opencl_device.cxx
+++ b/opencl/source/opencl_device.cxx
@@ -46,15 +46,11 @@
namespace opencl {
namespace {
bool bIsDeviceSelected = false;
ds_device selectedDevice;
struct LibreOfficeDeviceScore
{
double fTime; // small time means faster device
bool bNoCLErrors; // were there any opencl errors
};
struct LibreOfficeDeviceEvaluationIO
{
std::vector<double> input0;
@@ -182,7 +178,7 @@ double random(double min, double max)
}
/* Populate input */
void populateInput(LibreOfficeDeviceEvaluationIO* testData)
void populateInput(std::unique_ptr<LibreOfficeDeviceEvaluationIO>& testData)
{
double* input0 = &testData->input0[0];
double* input1 = &testData->input1[0];
@@ -196,49 +192,22 @@ void populateInput(LibreOfficeDeviceEvaluationIO* testData)
input3[i] = random(0, i);
}
}
/* Encode score object as byte string */
ds_status serializeScore(ds_device* device, void** serializedScore, unsigned int* serializedScoreSize)
{
*serializedScoreSize = sizeof(LibreOfficeDeviceScore);
*serializedScore = static_cast<void*>(new unsigned char[*serializedScoreSize]);
memcpy(*serializedScore, device->score, *serializedScoreSize);
return DS_SUCCESS;
}
/* Parses byte string and stores in score object */
ds_status deserializeScore(ds_device* device, const unsigned char* serializedScore, unsigned int serializedScoreSize)
{
// check that serializedScoreSize == sizeof(LibreOfficeDeviceScore);
device->score = new LibreOfficeDeviceScore;
memcpy(device->score, serializedScore, serializedScoreSize);
return DS_SUCCESS;
}
/* Releases memory held by score */
ds_status releaseScore(void* score)
{
if (nullptr != score)
{
delete static_cast<LibreOfficeDeviceScore*>(score);
}
return DS_SUCCESS;
}
/* Evaluate devices */
ds_status evaluateScoreForDevice(ds_device* device, void* evalData)
ds_status evaluateScoreForDevice(ds_device& rDevice, std::unique_ptr<LibreOfficeDeviceEvaluationIO>& testData)
{
if (DS_DEVICE_OPENCL_DEVICE == device->type)
if (rDevice.eType == DeviceType::OpenCLDevice)
{
/* Evaluating an OpenCL device */
SAL_INFO("opencl.device", "Device: \"" << device->oclDeviceName << "\" (OpenCL) evaluation...");
SAL_INFO("opencl.device", "Device: \"" << rDevice.sDeviceName << "\" (OpenCL) evaluation...");
cl_int clStatus;
/* Check for 64-bit float extensions */
size_t aDevExtInfoSize = 0;
clStatus = clGetDeviceInfo(device->oclDeviceID, CL_DEVICE_EXTENSIONS, 0, nullptr, &aDevExtInfoSize);
clStatus = clGetDeviceInfo(rDevice.aDeviceID, CL_DEVICE_EXTENSIONS, 0, nullptr, &aDevExtInfoSize);
DS_CHECK_STATUS(clStatus, "evaluateScoreForDevice::clGetDeviceInfo");
std::unique_ptr<char[]> aExtInfo(new char[aDevExtInfoSize]);
clStatus = clGetDeviceInfo(device->oclDeviceID, CL_DEVICE_EXTENSIONS, sizeof(char) * aDevExtInfoSize, aExtInfo.get(), nullptr);
clStatus = clGetDeviceInfo(rDevice.aDeviceID, CL_DEVICE_EXTENSIONS, sizeof(char) * aDevExtInfoSize, aExtInfo.get(), nullptr);
DS_CHECK_STATUS(clStatus, "evaluateScoreForDevice::clGetDeviceInfo");
bool bKhrFp64Flag = false;
bool bAmdFp64Flag = false;
@@ -268,9 +237,8 @@ ds_status evaluateScoreForDevice(ds_device* device, void* evalData)
if (!bKhrFp64Flag && !bAmdFp64Flag)
{
/* No 64-bit float support */
device->score = static_cast<void*>(new LibreOfficeDeviceScore);
static_cast<LibreOfficeDeviceScore*>(device->score)->fTime = DBL_MAX;
static_cast<LibreOfficeDeviceScore*>(device->score)->bNoCLErrors = true;
rDevice.fTime = DBL_MAX;
rDevice.bErrors = false;
SAL_INFO("opencl.device", "... no fp64 support");
}
else
@@ -278,30 +246,29 @@ ds_status evaluateScoreForDevice(ds_device* device, void* evalData)
/* 64-bit float support present */
/* Create context and command queue */
cl_context clContext = clCreateContext(nullptr, 1, &device->oclDeviceID, nullptr, nullptr, &clStatus);
cl_context clContext = clCreateContext(nullptr, 1, &rDevice.aDeviceID, nullptr, nullptr, &clStatus);
DS_CHECK_STATUS(clStatus, "evaluateScoreForDevice::clCreateContext");
cl_command_queue clQueue = clCreateCommandQueue(clContext, device->oclDeviceID, 0, &clStatus);
cl_command_queue clQueue = clCreateCommandQueue(clContext, rDevice.aDeviceID, 0, &clStatus);
DS_CHECK_STATUS(clStatus, "evaluateScoreForDevice::clCreateCommandQueue");
/* Build program */
cl_program clProgram = clCreateProgramWithSource(clContext, 1, &source, sourceSize, &clStatus);
DS_CHECK_STATUS(clStatus, "evaluateScoreForDevice::clCreateProgramWithSource");
clStatus = clBuildProgram(clProgram, 1, &device->oclDeviceID, buildOption, nullptr, nullptr);
clStatus = clBuildProgram(clProgram, 1, &rDevice.aDeviceID, buildOption, nullptr, nullptr);
DS_CHECK_STATUS(clStatus, "evaluateScoreForDevice::clBuildProgram");
if (CL_SUCCESS != clStatus)
{
/* Build program failed */
size_t length;
char* buildLog;
clStatus = clGetProgramBuildInfo(clProgram, device->oclDeviceID, CL_PROGRAM_BUILD_LOG, 0, nullptr, &length);
clStatus = clGetProgramBuildInfo(clProgram, rDevice.aDeviceID, CL_PROGRAM_BUILD_LOG, 0, nullptr, &length);
buildLog = static_cast<char*>(malloc(length));
clGetProgramBuildInfo(clProgram, device->oclDeviceID, CL_PROGRAM_BUILD_LOG, length, buildLog, &length);
clGetProgramBuildInfo(clProgram, rDevice.aDeviceID, CL_PROGRAM_BUILD_LOG, length, buildLog, &length);
SAL_INFO("opencl.device", "Build Errors:\n" << buildLog);
free(buildLog);
device->score = static_cast<void*>(new LibreOfficeDeviceScore);
static_cast<LibreOfficeDeviceScore*>(device->score)->fTime = DBL_MAX;
static_cast<LibreOfficeDeviceScore*>(device->score)->bNoCLErrors = false;
rDevice.fTime = DBL_MAX;
rDevice.bErrors = true;
}
else
{
@@ -310,7 +277,6 @@ ds_status evaluateScoreForDevice(ds_device* device, void* evalData)
timerStart(&kernelTime);
/* Run kernel */
LibreOfficeDeviceEvaluationIO* testData = static_cast<LibreOfficeDeviceEvaluationIO*>(evalData);
cl_kernel clKernel = clCreateKernel(clProgram, "DynamicKernel", &clStatus);
DS_CHECK_STATUS(clStatus, "evaluateScoreForDevice::clCreateKernel");
cl_mem clResult = clCreateBuffer(clContext, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, sizeof(cl_double) * testData->outputSize, &testData->output[0], &clStatus);
@@ -345,9 +311,8 @@ ds_status evaluateScoreForDevice(ds_device* device, void* evalData)
clReleaseMemObject(clResult);
clReleaseKernel(clKernel);
device->score = static_cast<void*>(new LibreOfficeDeviceScore);
static_cast<LibreOfficeDeviceScore*>(device->score)->fTime = timerCurrent(&kernelTime);
static_cast<LibreOfficeDeviceScore*>(device->score)->bNoCLErrors = true;
rDevice.fTime = timerCurrent(&kernelTime);
rDevice.bErrors = false;
}
clReleaseProgram(clProgram);
@@ -362,7 +327,6 @@ ds_status evaluateScoreForDevice(ds_device* device, void* evalData)
timer kernelTime;
timerStart(&kernelTime);
LibreOfficeDeviceEvaluationIO* testData = static_cast<LibreOfficeDeviceEvaluationIO*>(evalData);
for (unsigned long j = 0; j < testData->outputSize; j++)
{
double fAverage = 0.0f;
@@ -384,28 +348,45 @@ ds_status evaluateScoreForDevice(ds_device* device, void* evalData)
// slower than the above.
float fInterpretTailFactor = 10.0;
device->score = static_cast<void*>(new LibreOfficeDeviceScore);
static_cast<LibreOfficeDeviceScore*>(device->score)->fTime = timerCurrent(&kernelTime);
static_cast<LibreOfficeDeviceScore*>(device->score)->bNoCLErrors = true;
static_cast<LibreOfficeDeviceScore*>(device->score)->fTime *= fInterpretTailFactor;
rDevice.fTime = timerCurrent(&kernelTime);
rDevice.fTime *= fInterpretTailFactor;
rDevice.bErrors = false;
}
return DS_SUCCESS;
}
ds_status profileDevices(std::unique_ptr<ds_profile>& pProfile, std::unique_ptr<LibreOfficeDeviceEvaluationIO>& pTestData)
{
ds_status status = DS_SUCCESS;
if (!pProfile)
return DS_INVALID_PROFILE;
for (ds_device& rDevice : pProfile->devices)
{
ds_status evaluatorStatus = evaluateScoreForDevice(rDevice, pTestData);
if (evaluatorStatus != DS_SUCCESS)
{
status = evaluatorStatus;
return status;
}
}
return status;
}
/* Pick best device */
ds_status pickBestDevice(ds_profile* profile, int* bestDeviceIdx)
ds_status pickBestDevice(std::unique_ptr<ds_profile>& profile, int& rBestDeviceIndex)
{
double bestScore = DBL_MAX;
*bestDeviceIdx = -1;
for (unsigned int d = 0; d < profile->numDevices; d++)
rBestDeviceIndex = -1;
for (unsigned int d = 0; d < profile->devices.size(); d++)
{
ds_device device = profile->devices[d];
LibreOfficeDeviceScore *pScore = static_cast<LibreOfficeDeviceScore*>(device.score);
ds_device& device = profile->devices[d];
// Check blacklist and whitelist for actual devices
if (device.type == DS_DEVICE_OPENCL_DEVICE)
if (device.eType == DeviceType::OpenCLDevice)
{
// There is a silly impedance mismatch here. Why do we
// need two different ways to describe an OpenCL platform
@@ -415,32 +396,32 @@ ds_status pickBestDevice(ds_profile* profile, int* bestDeviceIdx)
OpenCLDeviceInfo aDevice;
// We know that only the below fields are used by checkForKnownBadCompilers()
aPlatform.maVendor = OUString(device.oclPlatformVendor, strlen(device.oclPlatformVendor), RTL_TEXTENCODING_UTF8);
aDevice.maName = OUString(device.oclDeviceName, strlen(device.oclDeviceName), RTL_TEXTENCODING_UTF8);
aDevice.maDriver = OUString(device.oclDriverVersion, strlen(device.oclDriverVersion), RTL_TEXTENCODING_UTF8);
aPlatform.maVendor = OStringToOUString(device.sPlatformVendor, RTL_TEXTENCODING_UTF8);
aDevice.maName = OStringToOUString(device.sDeviceName, RTL_TEXTENCODING_UTF8);
aDevice.maDriver = OStringToOUString(device.sDriverVersion, RTL_TEXTENCODING_UTF8);
// If blacklisted or not whitelisted, ignore it
if (OpenCLConfig::get().checkImplementation(aPlatform, aDevice))
{
SAL_INFO("opencl.device", "Device[" << d << "] " << device.oclDeviceName << " is blacklisted or not whitelisted");
pScore->fTime = DBL_MAX;
pScore->bNoCLErrors = true;
SAL_INFO("opencl.device", "Device[" << d << "] " << device.sDeviceName << " is blacklisted or not whitelisted");
device.fTime = DBL_MAX;
device.bErrors = false;
}
}
double fScore = DBL_MAX;
if (pScore)
if (device.fTime >= 0.0 || device.fTime == DBL_MAX)
{
fScore = pScore->fTime;
fScore = device.fTime;
}
else
{
SAL_INFO("opencl.device", "Unusual null score");
}
if (DS_DEVICE_OPENCL_DEVICE == device.type)
if (device.eType == DeviceType::OpenCLDevice)
{
SAL_INFO("opencl.device", "Device[" << d << "] " << device.oclDeviceName << " (OpenCL) score is " << fScore);
SAL_INFO("opencl.device", "Device[" << d << "] " << device.sDeviceName << " (OpenCL) score is " << fScore);
}
else
{
@@ -449,59 +430,135 @@ ds_status pickBestDevice(ds_profile* profile, int* bestDeviceIdx)
if (fScore < bestScore)
{
bestScore = fScore;
*bestDeviceIdx = d;
rBestDeviceIndex = d;
}
}
if (DS_DEVICE_OPENCL_DEVICE == profile->devices[*bestDeviceIdx].type)
if (profile->devices[rBestDeviceIndex].eType == DeviceType::OpenCLDevice)
{
SAL_INFO("opencl.device", "Selected Device[" << *bestDeviceIdx << "]: " << profile->devices[*bestDeviceIdx].oclDeviceName << "(OpenCL).");
SAL_INFO("opencl.device", "Selected Device[" << rBestDeviceIndex << "]: " << profile->devices[rBestDeviceIndex].sDeviceName << "(OpenCL).");
}
else
{
SAL_INFO("opencl.device", "Selected Device[" << *bestDeviceIdx << "]: CPU (Native).");
SAL_INFO("opencl.device", "Selected Device[" << rBestDeviceIndex << "]: CPU (Native).");
}
return DS_SUCCESS;
}
/* Return device ID for matching device name */
int matchDevice(ds_profile* profile, char* deviceName)
int matchDevice(std::unique_ptr<ds_profile>& profile, char* deviceName)
{
int deviceMatch = -1;
for (unsigned int d = 0; d < profile->numDevices - 1; d++)
for (unsigned int d = 0; d < profile->devices.size() - 1; d++)
{
if ((std::string(profile->devices[d].oclDeviceName)).find(deviceName) != std::string::npos) deviceMatch = d;
if ((std::string(profile->devices[d].sDeviceName.getStr())).find(deviceName) != std::string::npos)
deviceMatch = d;
}
if (std::string("NATIVE_CPU").find(deviceName) != std::string::npos) deviceMatch = profile->numDevices - 1;
if (std::string("NATIVE_CPU").find(deviceName) != std::string::npos)
deviceMatch = profile->devices.size() - 1;
return deviceMatch;
}
/*************************************************************************/
/* EXTERNAL FUNCTIONS */
/*************************************************************************/
ds_device getDeviceSelection(const char* sProfilePath, bool bForceSelection)
class LogWriter
{
private:
SvFileStream maStream;
public:
LogWriter(OUString aFileName)
: maStream(aFileName, StreamMode::WRITE)
{}
void text(const OString& rText)
{
maStream.WriteOString(rText);
maStream.WriteChar('\n');
}
void log(const OString& rKey, const OString& rValue)
{
maStream.WriteOString(rKey);
maStream.WriteCharPtr(": ");
maStream.WriteOString(rValue);
maStream.WriteChar('\n');
}
void log(const OString& rKey, const OUString& rValue)
{
log(rKey, OUStringToOString(rValue, RTL_TEXTENCODING_UTF8));
}
void log(const OString& rKey, int rValue)
{
log(rKey, OString::number(rValue));
}
void log(const OString& rKey, bool rValue)
{
log(rKey, OString::boolean(rValue));
}
};
void writeDevicesLog(std::unique_ptr<ds_profile>& rProfile, OUString sProfilePath, int nSelectedIndex)
{
OUString aCacheFile(sProfilePath + "opencl_devices.log");
LogWriter aWriter(aCacheFile);
int nIndex = 0;
for (ds_device& rDevice : rProfile->devices)
{
if (rDevice.eType == DeviceType::OpenCLDevice)
{
aWriter.log("Device Index", nIndex);
aWriter.log(" Selected", nIndex == nSelectedIndex);
aWriter.log(" Device Name", rDevice.sDeviceName);
aWriter.log(" Device Vendor", rDevice.sDeviceVendor);
aWriter.log(" Device Version", rDevice.sDeviceVersion);
aWriter.log(" Driver Version", rDevice.sDriverVersion);
aWriter.log(" Device Type", rDevice.sDeviceType);
aWriter.log(" Device Extensions", rDevice.sDeviceExtensions);
aWriter.log(" Device OpenCL C Version", rDevice.sDeviceOpenCLVersion);
aWriter.log(" Device Available", rDevice.bDeviceAvailable);
aWriter.log(" Device Compiler Available", rDevice.bDeviceCompilerAvailable);
aWriter.log(" Device Linker Available", rDevice.bDeviceLinkerAvailable);
aWriter.log(" Platform Name", rDevice.sPlatformName);
aWriter.log(" Platform Vendor", rDevice.sPlatformVendor);
aWriter.log(" Platform Version", rDevice.sPlatformVersion);
aWriter.log(" Platform Profile", rDevice.sPlatformProfile);
aWriter.log(" Platform Extensions", rDevice.sPlatformExtensions);
aWriter.text("");
}
nIndex++;
}
}
} // end anonymous namespace
ds_device getDeviceSelection(OUString sProfilePath, bool bForceSelection)
{
/* Run only if device is not yet selected */
if (!bIsDeviceSelected || bForceSelection)
{
/* Setup */
ds_profile* profile = nullptr;
initDSProfile(&profile, "LibreOffice v0.1");
std::unique_ptr<ds_profile> aProfile;
ds_status status;
status = initDSProfile(aProfile, "LibreOffice v1");
if (!profile)
if (status != DS_SUCCESS)
{
// failed to initialize profile.
selectedDevice.type = DS_DEVICE_NATIVE_CPU;
selectedDevice.eType = DeviceType::NativeCPU;
return selectedDevice;
}
/* Try reading scores from file */
std::string tmpStr(sProfilePath);
const char* fileName = tmpStr.append("sc_opencl_device_profile.dat").c_str();
ds_status status;
OUString sFilePath = sProfilePath + OUString("opencl_profile.xml");
if (!bForceSelection)
{
status = readProfileFromFile(profile, deserializeScore, fileName);
status = readProfile(sFilePath, aProfile);
}
else
{
@@ -512,7 +569,7 @@ ds_device getDeviceSelection(const char* sProfilePath, bool bForceSelection)
{
if (!bForceSelection)
{
SAL_INFO("opencl.device", "Profile file not available (" << fileName << "); performing profiling.");
SAL_INFO("opencl.device", "Profile file not available (" << sFilePath << "); performing profiling.");
}
/* Populate input data for micro-benchmark */
@@ -524,23 +581,22 @@ ds_device getDeviceSelection(const char* sProfilePath, bool bForceSelection)
testData->input2.resize(testData->inputSize);
testData->input3.resize(testData->inputSize);
testData->output.resize(testData->outputSize);
populateInput(testData.get());
populateInput(testData);
/* Perform evaluations */
unsigned int numUpdates;
status = profileDevices(profile, DS_EVALUATE_ALL, evaluateScoreForDevice, static_cast<void*>(testData.get()), &numUpdates);
status = profileDevices(aProfile, testData);
if (DS_SUCCESS == status)
{
/* Write scores to file */
status = writeProfileToFile(profile, serializeScore, fileName);
status = writeProfile(sFilePath, aProfile);
if (DS_SUCCESS == status)
{
SAL_INFO("opencl.device", "Scores written to file (" << fileName << ").");
SAL_INFO("opencl.device", "Scores written to file (" << sFilePath << ").");
}
else
{
SAL_INFO("opencl.device", "Error saving scores to file (" << fileName << "); scores not written to file.");
SAL_INFO("opencl.device", "Error saving scores to file (" << sFilePath << "); scores not written to file.");
}
}
else
@@ -550,25 +606,25 @@ ds_device getDeviceSelection(const char* sProfilePath, bool bForceSelection)
}
else
{
SAL_INFO("opencl.device", "Profile read from file (" << fileName << ").");
SAL_INFO("opencl.device", "Profile read from file (" << sFilePath << ").");
}
/* Pick best device */
int bestDeviceIdx;
pickBestDevice(profile, &bestDeviceIdx);
pickBestDevice(aProfile, bestDeviceIdx);
/* Override if necessary */
char* overrideDeviceStr = getenv("SC_OPENCL_DEVICE_OVERRIDE");
if (nullptr != overrideDeviceStr)
{
int overrideDeviceIdx = matchDevice(profile, overrideDeviceStr);
int overrideDeviceIdx = matchDevice(aProfile, overrideDeviceStr);
if (-1 != overrideDeviceIdx)
{
SAL_INFO("opencl.device", "Overriding Device Selection (SC_OPENCL_DEVICE_OVERRIDE=" << overrideDeviceStr << ").");
bestDeviceIdx = overrideDeviceIdx;
if (DS_DEVICE_OPENCL_DEVICE == profile->devices[bestDeviceIdx].type)
if (aProfile->devices[bestDeviceIdx].eType == DeviceType::OpenCLDevice)
{
SAL_INFO("opencl.device", "Selected Device[" << bestDeviceIdx << "]: " << profile->devices[bestDeviceIdx].oclDeviceName << " (OpenCL).");
SAL_INFO("opencl.device", "Selected Device[" << bestDeviceIdx << "]: " << aProfile->devices[bestDeviceIdx].sDeviceName << " (OpenCL).");
}
else
{
@@ -582,11 +638,10 @@ ds_device getDeviceSelection(const char* sProfilePath, bool bForceSelection)
}
/* Final device selection */
selectedDevice = profile->devices[bestDeviceIdx];
selectedDevice = aProfile->devices[bestDeviceIdx];
bIsDeviceSelected = true;
/* Release profile */
releaseDSProfile(profile, releaseScore);
writeDevicesLog(aProfile, sProfilePath, bestDeviceIdx);
}
return selectedDevice;
}
diff --git a/opencl/source/openclwrapper.cxx b/opencl/source/openclwrapper.cxx
index 74a925b..b17dc3e 100644
--- a/opencl/source/openclwrapper.cxx
+++ b/opencl/source/openclwrapper.cxx
@@ -703,9 +703,8 @@ bool switchOpenCLDevice(const OUString* pDevice, bool bAutoSelect, bool bForceEv
rtl::Bootstrap::expandMacros(url);
OUString path;
osl::FileBase::getSystemPathFromFileURL(url,path);
OString dsFileName = rtl::OUStringToOString(path, RTL_TEXTENCODING_UTF8);
ds_device pSelectedDevice = getDeviceSelection(dsFileName.getStr(), bForceEvaluation);
pDeviceId = pSelectedDevice.oclDeviceID;
ds_device pSelectedDevice = getDeviceSelection(path, bForceEvaluation);
pDeviceId = pSelectedDevice.aDeviceID;
}