SkelCL
SkelCL is a high level multi GPU skeleton library developed at the university of Münster, Germany.
 All Classes Namespaces Files Functions Variables Typedefs Groups
Program.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright (c) 2011-2012 The SkelCL Team as listed in CREDITS.txt *
3  * http://skelcl.uni-muenster.de *
4  * *
5  * This file is part of SkelCL. *
6  * SkelCL is available under multiple licenses. *
7  * The different licenses are subject to terms and condition as provided *
8  * in the files specifying the license. See "LICENSE.txt" for details *
9  * *
10  *****************************************************************************
11  * *
12  * SkelCL is free software: you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation, either version 3 of the License, or *
15  * (at your option) any later version. See "LICENSE-gpl.txt" for details. *
16  * *
17  * SkelCL is distributed in the hope that it will be useful, *
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
20  * GNU General Public License for more details. *
21  * *
22  *****************************************************************************
23  * *
24  * For non-commercial academic use see the license specified in the file *
25  * "LICENSE-academic.txt". *
26  * *
27  *****************************************************************************
28  * *
29  * If you are interested in other licensing models, including a commercial- *
30  * license, please contact the author at michel.steuwer@uni-muenster.de *
31  * *
32  *****************************************************************************/
33 
39 
40 #include <algorithm>
41 #include <fstream>
42 #include <iterator>
43 #include <sstream>
44 #include <string>
45 #include <utility>
46 
47 #define __CL_ENABLE_EXCEPTIONS
48 #include <CL/cl.hpp>
49 #undef __CL_ENABLE_EXCEPTIONS
50 
51 #include <stooling/SourceCode.h>
52 
53 #include <pvsutil/Assert.h>
54 #include <pvsutil/Logger.h>
55 
56 #include "SkelCL/detail/Program.h"
57 
58 #include "SkelCL/detail/Device.h"
59 #include "SkelCL/detail/DeviceList.h"
60 #include "SkelCL/detail/Util.h"
61 
62 namespace {
63 
64 std::string binaryFilename(const std::string& hash,
65  const std::shared_ptr<skelcl::detail::Device>&
66  devicePtr)
67 {
68  std::stringstream filename;
69  // escape spaces in device name
70  std::string devName = devicePtr->name();
71  std::replace( devName.begin(), devName.end(), ' ', '_');
72  filename << "." << hash
73  << "-" << devName
74  << "-" << devicePtr->id()
75  << ".skelcl";
76  return filename.str();
77 }
78 
79 } // namespace
80 
81 namespace skelcl {
82 
83 namespace detail {
84 
85 Program::Program(const std::string& source, const std::string& hash)
86  : _source(source),
87  _hash(hash),
88  _clPrograms()
89 {
90  LOG_DEBUG_INFO("Program instance created with source:\n", source,
91  "\n");
92 }
93 
94 Program::Program(Program&& rhs)
95  : _source(std::move(rhs._source)),
96  _hash(std::move(rhs._hash)),
97  _clPrograms(std::move(rhs._clPrograms))
98 {
99 }
100 
101 Program& Program::operator=(Program&& rhs)
102 {
103  _source = std::move(rhs._source);
104  _hash = std::move(rhs._hash);
105  _clPrograms = std::move(rhs._clPrograms);
106  return *this;
107 }
108 
109 void Program::transferParameters(const std::string& from,
110  unsigned indexFrom,
111  const std::string& to)
112 {
113  _source.transferParameters(from, indexFrom, to);
114  _source.fixKernelParameter(to);
115 }
116 
117 void Program::transferArguments(const std::string& from,
118  unsigned indexFrom,
119  const std::string& to)
120 {
121  _source.transferArguments(from, indexFrom, to);
122 }
123 
124 void Program::renameFunction(const std::string& from,
125  const std::string& to)
126 {
127  _source.renameFunction(from, to);
128 }
129 
130 void Program::renameType(const int i, const std::string& typeName)
131 {
132  std::stringstream identifier;
133  identifier << "SCL_TYPE_" << i;
134 
135  _source.redefineTypedef(identifier.str(), typeName);
136 }
137 
138 bool Program::loadBinary()
139 {
140  // if hash is empty no binary is loaded (maybe be more gentle and just return)
141  ASSERT(!_hash.empty());
142 
143  for (auto& devicePtr : globalDeviceList) {
144  std::ifstream binaryFile(binaryFilename(_hash, devicePtr),
145  std::ios_base::in
146  | std::ios_base::binary
147  | std::ios_base::ate);
148  if (binaryFile.fail()) {
149  _clPrograms.clear();
150  return false;
151  }
152 
153  // get the size of the file
154  std::ifstream::pos_type size = binaryFile.tellg();
155  // allocate memory
156  std::unique_ptr<char[]> binary(new char[size]);
157  // set position in file to the beginning
158  binaryFile.seekg(0, std::ios::beg);
159  // read the hole file
160  binaryFile.read(binary.get(), size);
161  // close it
162  binaryFile.close();
163  // push the binary on the vector
164  cl::Program::Binaries binaries(1, std::make_pair(binary.get(), size));
165  std::vector<cl::Device> devices{ devicePtr->clDevice() };
166  _clPrograms.push_back( cl::Program( devicePtr->clContext(),
167  devices, binaries ) );
168 
169  LOG_DEBUG_INFO("Load binary for device ", devicePtr->id(),
170  " from file ", binaryFilename(_hash, devicePtr));
171  }
172  ASSERT(_clPrograms.size() == globalDeviceList.size());
173  return true;
174 }
175 
176 void Program::build()
177 {
178  bool createdProgramsFromSource = false;
179  if (_clPrograms.empty()) {
180  createProgramsFromSource();
181  createdProgramsFromSource = true;
182  }
183 
184  try {
185  // build program for each device
186  // TODO: how to build the program only for a subset of devices?
187  for (auto& devicePtr : globalDeviceList) {
188  _clPrograms[devicePtr->id()].build(
189  std::vector<cl::Device>(1, devicePtr->clDevice()) );
190  }
191 
192  if (createdProgramsFromSource) {
193  saveBinary();
194  }
195 
196  } catch (cl::Error& err) {
197  if (err.err() == CL_BUILD_PROGRAM_FAILURE) {
198  auto& devicePtr = globalDeviceList.front();
199  auto buildLog =
200  _clPrograms[
201  devicePtr->id()].getBuildInfo<CL_PROGRAM_BUILD_LOG>(
202  devicePtr->clDevice() );
203  LOG_ERROR(err, "\nBuild log:\n", buildLog);
204  abort();
205  } else {
206  ABORT_WITH_ERROR(err);
207  }
208  }
209 }
210 
211 cl::Kernel Program::kernel(const Device& device,
212  const std::string& name) const
213 {
214  return cl::Kernel(_clPrograms[device.id()], name.c_str());
215 }
216 
217 void Program::createProgramsFromSource()
218 {
219  // insert programs into _clPrograms
220  std::transform( globalDeviceList.begin(), globalDeviceList.end(),
221  std::back_inserter(_clPrograms),
222  [this](DeviceList::const_reference devicePtr) -> cl::Program {
223  std::stringstream ss;
224  ss << "#define skelcl_get_device_id() " << devicePtr->id() << "\n";
225 
226  std::string s(ss.str());
227  s.append(_source.code());
228 
229  LOG_DEBUG_INFO("Create cl::Program for device ", devicePtr->id(),
230  " with source:\n", s, "\n");
231 
232  return cl::Program(devicePtr->clContext(),
233  cl::Program::Sources(1, std::make_pair(s.c_str(),
234  s.length()))
235  );
236  });
237 
238 }
239 
240 void Program::saveBinary()
241 {
242  if (_hash.empty()) return;
243  // don't save binary, expect the user has explicitly requested so
244  if (util::envVarValue("SKELCL_SAVE_BINARY") != "YES") return;
245 
246  for (auto& devicePtr : globalDeviceList) {
247  try {
248  auto size = _clPrograms[
249  devicePtr->id()].getInfo<CL_PROGRAM_BINARY_SIZES>();
250  ASSERT(size.size() == 1);
251 
252  std::unique_ptr<char[]> charPtr(new char[size.front()]);
253 
254  std::vector<char *> binary{ charPtr.get() };
255 
256  _clPrograms[devicePtr->id()].getInfo(CL_PROGRAM_BINARIES, &binary);
257 
258  std::ofstream outfile(binaryFilename(_hash, devicePtr),
260  | std::ios_base::trunc
261  | std::ios_base::binary);
262  outfile.write(binary.front(), static_cast<long>(size.front()));
263  LOG_DEBUG_INFO("Saved binary for device ", devicePtr->id(),
264  " to file ", binaryFilename(_hash, devicePtr));
265  } catch (cl::Error err) {
266  ABORT_WITH_ERROR(err);
267  }
268  }
269 }
270 
271 } // namespace detail
272 
273 } // namespace skelcl
Out< ContainerType< T > > out(ContainerType< T > &c)
Helper function to create a Out wrapper object.
Definition: Out.h:94
SKELCL_DLL detail::DeviceID device(size_t dID)
Creates an OpenCL device ID to be used as parameter of the init(detail::PlatformID, detail::DeviceID) function.
Definition: SkelCL.cpp:76