Fabcoin Core  0.16.2
P2P Digital Currency
libclwrapper.cpp
Go to the documentation of this file.
1 
2 #define _CRT_SECURE_NO_WARNINGS
3 
4 #include <cstdio>
5 #include <cstdlib>
6 //#include <chrono>
7 #include <fstream>
8 #include <streambuf>
9 #include <iostream>
10 #include <queue>
11 #include <vector>
12 #include <random>
13 //#include <atomic>
14 #include "libclwrapper.h"
15 #include "kernels/silentarmy.h" // Created from CMake
16 #include "../primitives/block.h"
17 
18 // workaround lame platforms
19 #if !CL_VERSION_1_2
20 #define CL_MAP_WRITE_INVALIDATE_REGION CL_MAP_WRITE
21 #define CL_MEM_HOST_READ_ONLY 0
22 #endif
23 
24 #undef min
25 #undef max
26 
27 //#define DEBUG
28 
29 using namespace std;
30 
31 unsigned const cl_gpuminer::c_defaultLocalWorkSize = 32;
32 unsigned const cl_gpuminer::c_defaultGlobalWorkSizeMultiplier = 4096; // * CL_DEFAULT_LOCAL_WORK_SIZE
33 unsigned const cl_gpuminer::c_defaultMSPerBatch = 0;
34 bool cl_gpuminer::s_allowCPU = false;
39 
40 #if defined(_WIN32)
41 extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
42 static std::atomic_flag s_logSpin = ATOMIC_FLAG_INIT;
43 #define CL_LOG(_contents) \
44  do \
45  { \
46  std::stringstream ss; \
47  ss << _contents; \
48  while (s_logSpin.test_and_set(std::memory_order_acquire)) {} \
49  OutputDebugStringA(ss.str().c_str()); \
50  cerr << ss.str() << endl << flush; \
51  s_logSpin.clear(std::memory_order_release); \
52  } while (false)
53 #else
54 #define CL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
55 #endif
56 
57 // Types of OpenCL devices we are interested in
58 #define CL_QUERIED_DEVICE_TYPES (CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_ACCELERATOR)
59 
61 : m_openclOnePointOne()
62 {
63 
64  dst_solutions = (uint32_t *) malloc(10*NUM_INDICES*sizeof(uint32_t));
65  if(dst_solutions == NULL)
66  std::cout << "Error allocating dst_solutions array!" << std::endl;
67 
68 }
69 
71 {
72  if(dst_solutions != NULL)
73  free(dst_solutions);
74  finish();
75 }
76 
77 std::vector<cl::Platform> cl_gpuminer::getPlatforms()
78 {
79  vector<cl::Platform> platforms;
80  try
81  {
82  cl::Platform::get(&platforms);
83  }
84  catch(cl::Error const& err)
85  {
86 #if defined(CL_PLATFORM_NOT_FOUND_KHR)
87  if (err.err() == CL_PLATFORM_NOT_FOUND_KHR)
88  CL_LOG("No OpenCL platforms found");
89  else
90 #endif
91  throw err;
92  }
93  return platforms;
94 }
95 
96 string cl_gpuminer::platform_info(unsigned _platformId, unsigned _deviceId)
97 {
98  vector<cl::Platform> platforms = getPlatforms();
99  if (platforms.empty())
100  return {};
101  // get GPU device of the selected platform
102  unsigned platform_num = min<unsigned>(_platformId, platforms.size() - 1);
103  vector<cl::Device> devices = getDevices(platforms, _platformId);
104  if (devices.empty())
105  {
106  CL_LOG("No OpenCL devices found.");
107  return {};
108  }
109 
110  // use selected default device
111  unsigned device_num = min<unsigned>(_deviceId, devices.size() - 1);
112  cl::Device& device = devices[device_num];
113  string device_version = device.getInfo<CL_DEVICE_VERSION>();
114 
115  return "{ \"platform\": \"" + platforms[platform_num].getInfo<CL_PLATFORM_NAME>() + "\", \"device\": \"" + device.getInfo<CL_DEVICE_NAME>() + "\", \"version\": \"" + device_version + "\" }";
116 }
117 
118 std::vector<cl::Device> cl_gpuminer::getDevices(std::vector<cl::Platform> const& _platforms, unsigned _platformId)
119 {
120  vector<cl::Device> devices;
121  unsigned platform_num = min<unsigned>(_platformId, _platforms.size() - 1);
122  try
123  {
124  _platforms[platform_num].getDevices(
125  s_allowCPU ? CL_DEVICE_TYPE_ALL : CL_QUERIED_DEVICE_TYPES,
126  &devices
127  );
128  }
129  catch (cl::Error const& err)
130  {
131  // if simply no devices found return empty vector
132  if (err.err() != CL_DEVICE_NOT_FOUND)
133  throw err;
134  }
135  return devices;
136 }
137 
139 {
140  vector<cl::Platform> platforms = getPlatforms();
141  if (platforms.empty())
142  return 0;
143  return platforms.size();
144 }
145 
146 unsigned cl_gpuminer::getNumDevices(unsigned _platformId)
147 {
148  vector<cl::Platform> platforms = getPlatforms();
149  if (platforms.empty())
150  return 0;
151 
152  vector<cl::Device> devices = getDevices(platforms, _platformId);
153  if (devices.empty())
154  {
155  CL_LOG("No OpenCL devices found.");
156  return 0;
157  }
158  return devices.size();
159 }
160 
161 // This needs customizing apon completion of the kernel - Checks memory requirements - May not be applicable
163  unsigned _platformId,
164  unsigned _localWorkSize,
165  unsigned _globalWorkSize
166 )
167 {
168  // Set the local/global work sizes
169  s_workgroupSize = _localWorkSize;
170  s_initialGlobalWorkSize = _globalWorkSize;
171 
172  return searchForAllDevices(_platformId, [](cl::Device const& _device) -> bool
173  {
174  cl_ulong result;
175  _device.getInfo(CL_DEVICE_GLOBAL_MEM_SIZE, &result);
176 
177  CL_LOG(
178  "Found suitable OpenCL device [" << _device.getInfo<CL_DEVICE_NAME>()
179  << "] with " << result << " bytes of GPU memory"
180  );
181  return true;
182  }
183  );
184 }
185 
186 bool cl_gpuminer::searchForAllDevices(function<bool(cl::Device const&)> _callback)
187 {
188  vector<cl::Platform> platforms = getPlatforms();
189  if (platforms.empty())
190  return false;
191  for (unsigned i = 0; i < platforms.size(); ++i)
192  if (searchForAllDevices(i, _callback))
193  return true;
194 
195  return false;
196 }
197 
198 bool cl_gpuminer::searchForAllDevices(unsigned _platformId, function<bool(cl::Device const&)> _callback)
199 {
200  vector<cl::Platform> platforms = getPlatforms();
201  if (platforms.empty())
202  return false;
203  if (_platformId >= platforms.size())
204  return false;
205 
206  vector<cl::Device> devices = getDevices(platforms, _platformId);
207  for (cl::Device const& device: devices)
208  if (_callback(device))
209  return true;
210 
211  return false;
212 }
213 
214 void cl_gpuminer::doForAllDevices(function<void(cl::Device const&)> _callback)
215 {
216  vector<cl::Platform> platforms = getPlatforms();
217  if (platforms.empty())
218  return;
219  for (unsigned i = 0; i < platforms.size(); ++i)
220  doForAllDevices(i, _callback);
221 }
222 
223 void cl_gpuminer::doForAllDevices(unsigned _platformId, function<void(cl::Device const&)> _callback)
224 {
225  vector<cl::Platform> platforms = getPlatforms();
226  if (platforms.empty())
227  return;
228  if (_platformId >= platforms.size())
229  return;
230 
231  vector<cl::Device> devices = getDevices(platforms, _platformId);
232  for (cl::Device const& device: devices)
233  _callback(device);
234 }
235 
237 {
238  string outString ="\nListing OpenCL devices.\nFORMAT: [deviceID] deviceName\n";
239  unsigned int i = 0;
240  doForAllDevices([&outString, &i](cl::Device const _device)
241  {
242  outString += "[" + to_string(i) + "] " + _device.getInfo<CL_DEVICE_NAME>() + "\n";
243  outString += "\tCL_DEVICE_TYPE: ";
244  switch (_device.getInfo<CL_DEVICE_TYPE>())
245  {
246  case CL_DEVICE_TYPE_CPU:
247  outString += "CPU\n";
248  break;
249  case CL_DEVICE_TYPE_GPU:
250  outString += "GPU\n";
251  break;
252  case CL_DEVICE_TYPE_ACCELERATOR:
253  outString += "ACCELERATOR\n";
254  break;
255  default:
256  outString += "DEFAULT\n";
257  break;
258  }
259  outString += "\tCL_DEVICE_GLOBAL_MEM_SIZE: " + to_string(_device.getInfo<CL_DEVICE_GLOBAL_MEM_SIZE>()) + "\n";
260  outString += "\tCL_DEVICE_MAX_MEM_ALLOC_SIZE: " + to_string(_device.getInfo<CL_DEVICE_MAX_MEM_ALLOC_SIZE>()) + "\n";
261  outString += "\tCL_DEVICE_MAX_WORK_GROUP_SIZE: " + to_string(_device.getInfo<CL_DEVICE_MAX_WORK_GROUP_SIZE>()) + "\n";
262  ++i;
263  }
264  );
265  CL_LOG(outString);
266 }
267 
268 void cl_gpuminer::finish()
269 {
270 
271  if (m_queue())
272  m_queue.finish();
273 }
274 
275 // Customise given kernel - This builds the kernel and creates memory buffers
276 bool cl_gpuminer::init(
277  unsigned _platformId,
278  unsigned _deviceId,
279  const std::vector<std::string> _kernels
280 )
281 {
282  // get all platforms
283  try
284  {
285  vector<cl::Platform> platforms = getPlatforms();
286  if (platforms.empty())
287  return false;
288 
289  // use selected platform
290  _platformId = min<unsigned>(_platformId, platforms.size() - 1);
291  CL_LOG("Using platform: " << platforms[_platformId].getInfo<CL_PLATFORM_NAME>().c_str());
292 
293  // get GPU device of the default platform
294  vector<cl::Device> devices = getDevices(platforms, _platformId);
295  if (devices.empty())
296  {
297  CL_LOG("No OpenCL devices found.");
298  return false;
299  }
300 
301  // use selected device
302  cl::Device& device = devices[min<unsigned>(_deviceId, devices.size() - 1)];
303  string device_version = device.getInfo<CL_DEVICE_VERSION>();
304  CL_LOG("Using device: " << device.getInfo<CL_DEVICE_NAME>().c_str() << "(" << device_version.c_str() << ")");
305 
306  if (strncmp("OpenCL 1.0", device_version.c_str(), 10) == 0)
307  {
308  CL_LOG("OpenCL 1.0 is not supported.");
309  return false;
310  }
311  if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
312  m_openclOnePointOne = true;
313 
314  // create context
315  m_context = cl::Context(vector<cl::Device>(&device, &device + 1));
317 
318  // make sure that global work size is evenly divisible by the local workgroup size
322  // remember the device's address bits
323  m_deviceBits = device.getInfo<CL_DEVICE_ADDRESS_BITS>();
324  // make sure first step of global work size adjustment is large enough
325  m_stepWorkSizeAdjust = pow(2, m_deviceBits / 2 + 1);
326 
327  // patch source code
328  // note: CL_MINER_KERNEL is simply cl_gpuminer_kernel.cl compiled
329  // into a byte array by bin2h.cmake. There is no need to load the file by hand in runtime
330 
331  // Uncomment for loading kernel from compiled cl file.
332 #ifdef DEBUG
333  ifstream kernel_file("./libgpuminer/kernels/silentarmy.cl");
334  string code((istreambuf_iterator<char>(kernel_file)), istreambuf_iterator<char>());
335  kernel_file.close();
336 #else
338 #endif
339  // create miner OpenCL program
340  cl::Program::Sources sources;
341  sources.push_back({ code.c_str(), code.size() });
342 
343  cl::Program program(m_context, sources);
344  try
345  {
346  program.build({ device });
347  CL_LOG("Printing program log");
348  CL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
349  }
350  catch (cl::Error const&)
351  {
352  CL_LOG(program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device).c_str());
353  return false;
354  }
355 
356  try
357  {
358  for (auto & _kernel : _kernels)
359  m_gpuKernels.push_back(cl::Kernel(program, _kernel.c_str()));
360  }
361  catch (cl::Error const& err)
362  {
363  CL_LOG("gpuKERNEL Creation failed: " << err.what() << "(" << err.err() << "). Bailing.");
364  return false;
365  }
366 
367  buf_dbg = cl::Buffer(m_context, CL_MEM_READ_WRITE, dbg_size, NULL, NULL);
368 
370  buf_ht[0] = cl::Buffer(m_context, CL_MEM_READ_WRITE, HT_SIZE, NULL, NULL);
371  buf_ht[1] = cl::Buffer(m_context, CL_MEM_READ_WRITE, HT_SIZE, NULL, NULL);
372  buf_sols = cl::Buffer(m_context, CL_MEM_READ_WRITE, sizeof (sols_t), NULL, NULL);
373  rowCounters[0] = cl::Buffer(m_context, CL_MEM_READ_WRITE, NR_ROWS, NULL,NULL);
374  rowCounters[1] = cl::Buffer(m_context, CL_MEM_READ_WRITE, NR_ROWS, NULL, NULL);
375 
376  m_queue.finish();
377 
378  }
379  catch (cl::Error const& err)
380  {
381  CL_LOG("CL ERROR:" << get_error_string(err.err()));
382  return false;
383  }
384  return true;
385 }
386 
387 
388 void cl_gpuminer::run(uint8_t *header, size_t header_len, uint256 nonce, sols_t * indices, uint32_t * n_sol, uint256 * ptr)
389 {
390  try
391  {
392  blake2b_state_t blake;
393  cl::Buffer buf_blake_st;
394  cl::Buffer databuf;
395  uint32_t sol_found = 0;
396  size_t local_ws = 64;
397  size_t global_ws;
398  unsigned char buf[136] = {0};
399 
400  assert(header_len == CBlockHeader::HEADER_SIZE || header_len == CBlockHeader::HEADER_NEWSIZE);
401  *ptr = *(uint256 *)(header + header_len - FABCOIN_NONCE_LEN);
402 
404 
405  zcash_blake2b_update(&blake, header, 128, 0);
406 
407  memcpy( buf + 8, header + 128, header_len - 128);
408  buf[0] = (header_len - 128)/8+1;
409 
410  buf_blake_st = cl::Buffer(m_context, CL_MEM_READ_ONLY, sizeof (blake.h), NULL, NULL);
411  m_queue.enqueueWriteBuffer(buf_blake_st, true, 0, sizeof(blake.h), blake.h);
412 
413  databuf = cl::Buffer(m_context, CL_MEM_READ_ONLY, 136, NULL, NULL);
414  m_queue.enqueueWriteBuffer(databuf, true, 0, 136, buf);
415  m_queue.finish();
416 
417  for (unsigned round = 0; round < PARAM_K; round++)
418  {
419  m_gpuKernels[0].setArg(0, buf_ht[round % 2]);
420  m_gpuKernels[0].setArg(1, rowCounters[round % 2]);
422 
423  if (!round)
424  {
425  m_gpuKernels[1+round].setArg(0, buf_blake_st);
426  m_gpuKernels[1+round].setArg(1, buf_ht[round % 2]);
427  m_gpuKernels[1+round].setArg(2, databuf);
428  m_gpuKernels[1+round].setArg(3, rowCounters[round % 2]);
429  global_ws = select_work_size_blake();
430  }
431  else
432  {
433  m_gpuKernels[1+round].setArg(0, buf_ht[(round - 1) % 2]);
434  m_gpuKernels[1+round].setArg(1, buf_ht[round % 2]);
435  m_gpuKernels[1+round].setArg(2, rowCounters[(round - 1) % 2]);
436  m_gpuKernels[1+round].setArg(3, rowCounters[round % 2]);
437  global_ws = NR_ROWS;
438  }
439  m_gpuKernels[1+round].setArg(4, buf_dbg);
440 
441  if (round == PARAM_K - 1)
442  {
443  m_gpuKernels[1+round].setArg(5, buf_sols);
444  }
445 
446  m_queue.enqueueNDRangeKernel(m_gpuKernels[1+round], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws));
447  }
448 
449  m_gpuKernels[10].setArg(0, buf_ht[0]);
450  m_gpuKernels[10].setArg(1, buf_ht[1]);
451  m_gpuKernels[10].setArg(2, buf_sols);
452  m_gpuKernels[10].setArg(3, rowCounters[0]);
453  m_gpuKernels[10].setArg(4, rowCounters[1]);
454  global_ws = NR_ROWS;
455  m_queue.enqueueNDRangeKernel(m_gpuKernels[10], cl::NullRange, cl::NDRange(global_ws), cl::NDRange(local_ws));
456 
457  sols_t * sols;
458  size_t sz = sizeof(sols_t)*1;
459 
460  sols = (sols_t *)malloc(sz);
461  m_queue.enqueueReadBuffer(buf_sols, true, 0, sz, sols);
462  m_queue.finish();
463 
464  if (sols->nr > MAX_SOLS)
465  {
466  sols->nr = MAX_SOLS;
467  }
468 
469  for (unsigned sol_i = 0; sol_i < sols->nr; sol_i++)
470  sol_found += verify_sol(sols, sol_i);
471 
472  *n_sol = sol_found;
473  memcpy(indices, sols, sizeof(sols_t));
474  free(sols);
475  }
476  catch (cl::Error const& err)
477  {
478  CL_LOG("CL ERROR:" << get_error_string(err.err()));
479  }
480 }
static std::string platform_info(unsigned _platformId=0, unsigned _deviceId=0)
unsigned int m_stepWorkSizeAdjust
The step used in the work size adjustment.
Definition: libclwrapper.h:232
const cl_int zero
Definition: libclwrapper.h:223
const unsigned char CL_MINER_KERNEL[]
Definition: silentarmy.h:1
#define CL_QUERIED_DEVICE_TYPES
cl::CommandQueue m_queue
Definition: libclwrapper.h:212
cl_int getBuildInfo(const Device &device, cl_program_build_info name, T *param) const
Definition: cl.hpp:2506
#define NUM_INDICES
Definition: libclwrapper.h:40
static unsigned const c_defaultMSPerBatch
Default value of the milliseconds per global work size (per batch)
Definition: libclwrapper.h:110
unsigned m_globalWorkSize
Definition: libclwrapper.h:227
void zcash_blake2b_init(blake2b_state_t *st, uint8_t hash_len, uint32_t n, uint32_t k)
Definition: blake.cpp:34
static unsigned s_msPerBatch
The target milliseconds per batch for the search. If 0, then no adjustment will happen.
Definition: libclwrapper.h:241
void run(uint8_t *header, size_t header_len, uint256 nonce, sols_t *indices, uint32_t *n_sol, uint256 *ptr)
unsigned long HT_SIZE()
Definition: libclwrapper.h:298
unsigned int FABCOIN_HASH_LEN()
Definition: libclwrapper.h:305
cl_int enqueueWriteBuffer(const Buffer &buffer, cl_bool blocking,::size_t offset,::size_t size, const void *ptr, const VECTOR_CLASS< Event > *events=NULL, Event *event=NULL) const
Definition: cl.hpp:2638
std::hash for asio::adress
Definition: Common.h:323
assert(len-trim+(2 *lenIndices)<=WIDTH)
static unsigned s_extraRequiredGPUMem
GPU memory required for other things, like window rendering e.t.c.
Definition: libclwrapper.h:246
static std::vector< cl::Platform > getPlatforms()
bytes code
Definition: SmartVM.cpp:45
static unsigned const c_defaultLocalWorkSize
Default value of the local work size. Also known as workgroup size.
Definition: libclwrapper.h:106
#define FABCOIN_NONCE_LEN
Definition: libclwrapper.h:8
static bool searchForAllDevices(unsigned _platformId, std::function< bool(cl::Device const &)> _callback)
cl_int finish() const
Definition: cl.hpp:3135
unsigned long NR_ROWS()
Definition: libclwrapper.h:288
size_t dbg_size
Definition: libclwrapper.h:221
static unsigned getNumDevices(unsigned _platformId=0)
static bool s_allowCPU
Allow CPU to appear as an OpenCL device or not. Default is false.
Definition: libclwrapper.h:243
bool init(unsigned _platformId, unsigned _deviceId, std::vector< std::string > _kernels)
cl_int enqueueFillBuffer(const Buffer &buffer, const void *ptr,::size_t pattern_size,::size_t offset,::size_t size, const VECTOR_CLASS< Event > *events=NULL, Event *event=NULL) const
Definition: cl.hpp:2657
std::vector< cl::Kernel > m_gpuKernels
Definition: libclwrapper.h:213
uint32_t * dst_solutions
Definition: libclwrapper.h:225
const char * get_error_string(cl_int error)
Definition: libclwrapper.h:318
cl_int enqueueNDRangeKernel(const Kernel &kernel, const NDRange &offset, const NDRange &global, const NDRange &local, const VECTOR_CLASS< Event > *events=NULL, Event *event=NULL) const
Definition: cl.hpp:2961
cl::Buffer buf_sols
Definition: libclwrapper.h:215
VECTOR_CLASS< std::pair< const char *,::size_t > > Sources
Definition: cl.hpp:2397
uint64_t nonce
Definition: libclwrapper.h:219
void zcash_blake2b_update(blake2b_state_t *st, const uint8_t *_msg, uint32_t msg_len, uint32_t is_final)
Definition: blake.cpp:78
static void listDevices()
cl::Context m_context
Definition: libclwrapper.h:211
unsigned int ROWS_PER_UINT()
Definition: libclwrapper.h:310
unsigned m_deviceBits
Definition: libclwrapper.h:229
uint64_t h[8]
Definition: blake.h:3
#define MAX_SOLS
Definition: libclwrapper.h:10
cl::Buffer buf_dbg
Definition: libclwrapper.h:216
256-bit opaque blob.
Definition: uint256.h:132
CommandQueue interface for cl_command_queue.
Definition: cl.hpp:2566
uint nr
Definition: libclwrapper.h:15
static const size_t HEADER_SIZE
Definition: block.h:39
const size_t CL_MINER_KERNEL_SIZE
Definition: silentarmy.h:709
void * memcpy(void *a, const void *b, size_t c)
bool m_openclOnePointOne
Definition: libclwrapper.h:228
cl_int build(const VECTOR_CLASS< Device > &devices, const char *options=NULL, void(CL_CALLBACK *notifyFptr)(cl_program, void *)=NULL, void *data=NULL) const
Definition: cl.hpp:2466
#define round(a, b, c, x, mul)
cl_int getInfo(cl_device_info name, T *param) const
Definition: cl.hpp:1208
static cl_int get(VECTOR_CLASS< Platform > *platforms)
Definition: cl.hpp:1397
unsigned int PARAM_K
Definition: libclwrapper.h:250
Memory buffer interface.
Definition: cl.hpp:1748
NDRange interface.
Definition: cl.hpp:2218
cl_int enqueueReadBuffer(const Buffer &buffer, cl_bool blocking,::size_t offset,::size_t size, void *ptr, const VECTOR_CLASS< Event > *events=NULL, Event *event=NULL) const
Definition: cl.hpp:2619
size_t select_work_size_blake(void)
Definition: libclwrapper.h:152
uint32_t verify_sol(sols_t *sols, unsigned sol_i)
Definition: libclwrapper.h:183
Kernel interface that implements cl_kernel.
Definition: cl.hpp:2296
static void doForAllDevices(unsigned _platformId, std::function< void(cl::Device const &)> _callback)
static unsigned getNumPlatforms()
static unsigned s_workgroupSize
The local work size for the search.
Definition: libclwrapper.h:237
cl::Buffer rowCounters[2]
Definition: libclwrapper.h:217
Device interface for cl_device_id.
Definition: cl.hpp:1190
cl::Buffer buf_ht[2]
Definition: libclwrapper.h:214
__declspec(dllimport)
Definition: util_win32.c:26
static const size_t HEADER_NEWSIZE
Definition: block.h:40
Program interface that implements cl_program.
Definition: cl.hpp:2393
static std::vector< cl::Device > getDevices(std::vector< cl::Platform > const &_platforms, unsigned _platformId)
static unsigned const c_defaultGlobalWorkSizeMultiplier
Default value of the global work size as a multiplier of the local work size.
Definition: libclwrapper.h:108
struct sols_s sols_t
#define CL_LOG(_contents)
static bool configureGPU(unsigned _platformId, unsigned _localWorkSize, unsigned _globalWorkSize)
static unsigned s_initialGlobalWorkSize
The initial global work size for the searches.
Definition: libclwrapper.h:239
unsigned int PARAM_N
Definition: libclwrapper.h:249