Home > other >  Binaries in openCL (C )
Binaries in openCL (C )

Time:10-17

Suppose I had a program in openCL:

...

const unsigned char* binaries;
size_t binaries_size;

assign_binaries(&binaries, &binaries_size); // my assign function, assigns bin and bin_size

std::vector<std::vector<unsigned char>> Binaries;

cl::Program prog = cl::Program(context, device_list, Binaries, &binary_status, &err);

...

How would I go about pushing

binaries

and

binaries_size

into

Binaries

?

In the OpenCL docs, it says:

Binaries: A vector of pairs of a pointer to a binary object and its length.

However, this makes no sense to me, as there is an obvious type mismatch.

CodePudding user response:

How would I go about pushing

binaries

and

binaries_size

into

Binaries

?

There are many ways to accomplish this.

TL;DR: The most compact code to achieve OPs intention is probably:

cl::Program prog
  = cl::Program(context, device_list,
    { std::vector<unsigned char>(binaries, binaries   binaries_size) }, 
    &binary_status, &err);

A longer story:

An MCVE to demonstrate two:

#include <iomanip>
#include <iostream>
#include <vector>

// the OpenCL typedef
using Binaries = std::vector<std::vector<unsigned char>>;

void print(const Binaries& binaries)
{
  std::cout << "Binaries: [" << binaries.size() << "]: {\n";
  for (const std::vector<unsigned char>& binary : binaries) {
    std::cout << "  [" << binary.size() << "]: {" ;
    const char* sep = " ";
    for (const unsigned char value : binary) {
      std::cout << sep << "0x"
        << std::hex << std::setw(2) << std::setfill('0') << (unsigned)value;
      sep = ", ";
    }
    std::cout << " }\n";
  }
  std::cout << "}\n";
}

int main()
{
  // sample data
  const unsigned char data[] = {
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
  };
  // OPs initial exposure of data
  const unsigned char *binaries = data;
  const size_t binaries_size = std::size(data);
  // filling a Binaries var.:
  Binaries vecBinaries1;
  vecBinaries1.push_back(
    std::vector<unsigned char>(binaries, binaries   binaries_size));
  print(vecBinaries1);
  // or
  const Binaries vecBinaries2(1, std::vector<unsigned char>(binaries, binaries   binaries_size));
  print(vecBinaries2);
}

Output:

Binaries: [1]: {
  [8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}
Binaries: [1]: {
  [8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}

Please, note that data is prepared in binaries, and then copied into the constructor std::vector<unsigned char>(binaries, binaries binaries_size) of std::vector.

std::vector provides a variety of constructors. In this case, the constructor with first and last iterator is used:

template< class InputIt >
vector( InputIt first, InputIt last,
        const Allocator& alloc = Allocator() );

(It's flavor (5) in the linked doc.)

However, this might be an unnecessary copy-step. Providing the data in a std::vector<unsigned char> from the beginning could be used to get rid of this.

The push_back() can be used with move semantic (which prevents a 2nd unnecessary deep-copy). (It's flavor (2) in the linked doc.) If the constructor of the inner vector is passed as argument directly, it results in an RValue which will trigger the move semantic automagically.

If there is an intermediate storage of the inner vector (e.g. in a local variable), the move semantic still can be forced with std::move():

  std::vector<unsigned char> vecBinary3(binaries, binaries   binaries_size);
  Binaries vecBinaries3;
  vecBinaries3.push_back(std::move(vecBinary3));
  print(vecBinaries3);

Output:

Binaries: [1]: {
  [8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}

Live Demo on coliru


I also added brace initialization (which in this case is direct-list-initialization).

  const Binaries vecBinaries4 {
    std::vector<unsigned char>(binaries, binaries   binaries_size)
  };
  print(vecBinaries4);

Output:

Binaries: [1]: {
  [8]: { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
}

(I must admit, I still consider brace init. a little bit scaring. Hence, I struggled to mention it at all but I should for completeness.)

In certain cases, e.g. if you have a vector of integrals, you have to be careful to distinguish the constructor with size n and an initial fill value from the constructor with an initialization list of two values. For both, you have to provide two integral values, but the kind and number of brackets decide which constructor is used. This can result in confusion easily.

Example:

#include <iostream>
#include <vector>

void print(const std::vector<int>& values)
{
  std::cout << '{';
  const char* sep = " ";
  for (const int value : values) {
    std::cout << sep << value;
    sep = ", ";
  }
  std::cout << " }\n";
}

int main()
{
  // sample 1:
  print({ 1, 2 });
  // sample 2:
  print(std::vector<int>(1, 2));
  // sample 3:
  print(std::vector<int>{ 1, 2 });
  // sample 4:
  print(std::vector<int>({ 1, 2 }));
}

Output:

{ 1, 2 }
{ 2 }
{ 1, 2 }
{ 1, 2 }

Live Demo on coliru

  • Related