[+] Initial commit

This commit is contained in:
Hykilpikonna
2021-12-22 03:30:29 -05:00
commit ee16ca411e
258 changed files with 177309 additions and 0 deletions
+2909
View File
File diff suppressed because it is too large Load Diff
+251
View File
@@ -0,0 +1,251 @@
// Copyright 2018 Intel Corporation.
//
// This is the PlaidML base library interface, handling functionality common across the Vertex.AI libraries.
#pragma once
#if defined _WIN32 || defined __CYGWIN__
#ifdef VAI_DLL
#define VAI_API __declspec(dllexport)
#else
#define VAI_API __declspec(dllimport)
#endif
#elif __GNUC__ >= 4
#define VAI_API __attribute__((visibility("default")))
#else
#define VAI_API
#endif
#ifdef __cplusplus
#include <cstddef>
#include <memory>
extern "C" {
#else
#include <stdbool.h>
#include <stddef.h>
#endif // __cplusplus
// Error handling.
//
// In the Vertex.AI C APIs, functions that can fail return either a pointer or a
// boolean; success is represented as a non-NULL or true value, and failure is
// represented as a NULL or false value.
//
// When a call fails, the callee is responsible for recording additional
// information in thread-local storage. This information can be retrieved by
// calling vai_last_status(). Otherwise (if a call is successful), no
// guarantees are made; the callee may clobber existing thread-local error
// information, leaving it in an undefined state.
//
// Note that errors may occur asynchronously. Asynchronous errors are reported
// to the various callback functions used to collect the results of
// asynchronous, operations, again via a NULL or false value. In this case,
// the last error can be retrieved from thread-local-storage within the
// callback function.
//
// Failed calls always propagate errors, poisoning the state of any objects
// being updated. Additionally, NULL inputs are valid for all calls, causing
// them to fail (on the assumption that a NULL input indicates an earlier
// out-of-memory condition). Callers may take advantage of this by performing
// a sequence of calls without checking for errors, and then checking the last
// dependent call for errors; this obscures the exact call that produced an
// error, but in most cases the caller is more interested in the fact that the
// overall computation failed, and less interested in exactly which call
// failed.
// These are the various status codes the application may observe.
// Note that additional status codes may be added in subsquent releases.
//
// The set of status codes attempts to provide enough information for software components to determine the appropriate
// recovery action to take. For diagnostics, the human-readable string associated with the status is typically more
// useful.
typedef enum {
// A status representing "No error".
VAI_STATUS_OK = 0,
// Indicates that an asynchronous operations was cancelled.
VAI_STATUS_CANCELLED = 1,
// A generic catch-all error, used when an error condition must be signalled but
// there is no appropriate API-level status code.
VAI_STATUS_UNKNOWN = 2,
// Indicates that at least one invalid argument was passed to a function.
VAI_STATUS_INVALID_ARGUMENT = 3,
// The operation deadline was exceeded.
VAI_STATUS_DEADLINE_EXCEEDED = 4,
// The requested object was not found.
VAI_STATUS_NOT_FOUND = 5,
// The requested object already exists.
VAI_STATUS_ALREADY_EXISTS = 6,
// The caller does not have permission to access a resource required by the operation.
VAI_STATUS_PERMISSION_DENIED = 7,
// A resource required by the operation is exhausted. (For example, this is returned when the implementation is
// unable to allocate sufficient memory.)
VAI_STATUS_RESOURCE_EXHAUSTED = 8,
// A precondition required by the operation is unmet. (For example, this is returned when an object supplied to a
// call is not in the correct state for the call to take place).
VAI_STATUS_FAILED_PRECONDITION = 9,
// A transactional operation was aborted by the system. Generally, this is a transient condition.
VAI_STATUS_ABORTED = 10,
// A call parameter is out of the range accepted by the implementation.
VAI_STATUS_OUT_OF_RANGE = 11,
// The requested functionality is not implemented.
VAI_STATUS_UNIMPLEMENTED = 12,
// An internal error occurred. Typically, this indicates that the implementation has detected that its internal state
// is inconsistent.
VAI_STATUS_INTERNAL = 13,
// A resource required by the operation (such as a hardware device) is unavailable for use.
VAI_STATUS_UNAVAILABLE = 14,
// The system has lost data required by the operation, typically due to hardware failure.
VAI_STATUS_DATA_LOSS = 15,
// The caller is unauthenticated, but authenticated access is required to access some resource.
VAI_STATUS_UNAUTHENTICATED = 16,
} vai_status;
// Returns the last status recorded in the current thread's thread-local storage,
// or VAI_STATUS_OK if no status has been recorded.
VAI_API vai_status vai_last_status();
// Resets the current thread's thread-local status storage to VAI_STATUS_OK.
VAI_API void vai_clear_status();
// Returns a NUL-terminated UTF-8 message describing the status of the call
// errors recorded by the current thread's thread-local storage. If no error has
// been recorded, an empty string will be returned.
//
// The returned string will remain alive until vai_clear_error is called, another Vertex.AI
// call is made, or until the current thread exits.
//
// The error string may be dependent on the locale installed when the error occurred.
VAI_API const char* vai_last_status_str();
// Logger configuration.
typedef enum {
VAI_LOG_SEVERITY_TRACE = 2,
VAI_LOG_SEVERITY_DEBUG = 4,
VAI_LOG_SEVERITY_FATAL = 8,
VAI_LOG_SEVERITY_ERROR = 16,
VAI_LOG_SEVERITY_WARNING = 32,
VAI_LOG_SEVERITY_VERBOSE = 64,
VAI_LOG_SEVERITY_INFO = 128,
} vai_log_severity;
// Sets the process-global logging callback.
VAI_API void vai_set_logger(void (*logger)(void*, vai_log_severity, const char*), void* arg);
// Dynamic feature detection.
//
// Invoking this API with an unknown / unsupported feature ID will return NULL.
// Invoking it with a supported feature ID will return a pointer to a static
// feature-specific value.
typedef enum {
VAI_FEATURE_ID_RESERVED = 0,
} vai_feature_id;
VAI_API void* vai_query_feature(vai_feature_id id);
// A Vertex.AI context provides a scope for Vertex.AI library operations. In
// particular, it provides an asynchronous execution context, allowing callers
// to correctly synchronize with asynchronous callbacks during shutdown.
//
// NULL semantically points to a valid, but cancelled, context.
#ifdef __cplusplus
struct vai_ctx;
#else
typedef struct vai_ctx vai_ctx;
#endif // __cplusplus
// Allocate and returns a context, or returns NULL if the library
// cannot allocate sufficient memory.
VAI_API vai_ctx* vai_alloc_ctx();
// Frees a context. After this call, the context should not be used for
// any subsequent calls. Freeing a NULL context is a no-op.
//
// Freeing a context will block until pending asynchronous operations are complete.
VAI_API void vai_free_ctx(vai_ctx* ctx);
// Cancels outstanding asynchronous operations associated with the context
// (ensuring that callbacks are completed before returning to the caller), and
// causes future callbacks issued using the context to synchronously fail.
//
// Note that there is no call to go from "cancelled" back to "uncancelled".
//
// This does not block waiting for asynchronous operations to complete.
VAI_API void vai_cancel_ctx(vai_ctx* ctx);
// Sets the context to log events according to the specified
// configuration. For instance, to point the context's event log at
// "eventlog.gz", use the JSON string:
//
// "@type": "type.vertex.ai/vertexai.eventing.file.proto.EventLog",
// "filename": "eventlog.gz"
//
// If the context already has an associated eventlog, that eventlog
// will be finalized and closed asynchronously, once all asynchronous
// activity using that eventlog has completed.
//
// A NULL config sets the context to not use the event logging
// subsystem for future calls.
VAI_API bool vai_set_eventlog(vai_ctx* ctx, const char* config);
// Gets the current value of a performance counter based on the name.
// If there is no performance counter with that name, returns -1.
VAI_API int64_t vai_get_perf_counter(const char* name);
// Sets the current value of a performance counter based on the name.
// If there is no performance counter with that name, no action is taken.
VAI_API void vai_set_perf_counter(const char* name, int64_t value);
// A PlaidML datatype indicates the type of data stored within a buffer, as
// observed by a program.
typedef enum {
PLAIDML_DATA_INVALID = 0,
PLAIDML_DATA_BOOLEAN = 0x02,
PLAIDML_DATA_INT8 = 0x10,
PLAIDML_DATA_INT16 = 0x11,
PLAIDML_DATA_INT32 = 0x12,
PLAIDML_DATA_INT64 = 0x13,
PLAIDML_DATA_INT128 = 0x14,
PLAIDML_DATA_UINT8 = 0x20,
PLAIDML_DATA_UINT16 = 0x21,
PLAIDML_DATA_UINT32 = 0x22,
PLAIDML_DATA_UINT64 = 0x23,
PLAIDML_DATA_FLOAT16 = 0x31,
PLAIDML_DATA_FLOAT32 = 0x32,
PLAIDML_DATA_FLOAT64 = 0x33,
PLAIDML_DATA_BFLOAT16 = 0x38,
PLAIDML_DATA_PRNG = 0x40,
} plaidml_datatype;
#ifdef __cplusplus
} // extern "C"
namespace std {
template <>
struct default_delete<::vai_ctx> {
void operator()(::vai_ctx* ctx) const noexcept { ::vai_free_ctx(ctx); }
};
} // namespace std
#endif // __cplusplus
+68
View File
@@ -0,0 +1,68 @@
// Copyright 2018 Intel Corporation.
//
// This is the Vertex.AI common C++ interface, which provides a higher level object
// oriented wrapper on top of the Vertex.AI common C API.
#pragma once
#include <exception>
#include <memory>
#include <string>
#include <utility>
#include "plaidml/base/base.h"
namespace vertexai {
class vai_exception : public std::runtime_error {
public:
vai_exception(vai_status status, const std::string& what) : std::runtime_error(what), status_(status) {}
vai_status status() { return status_; }
template <typename T>
static void check_and_throw(const T& good) {
if (good) {
return;
}
vai_status status = vai_last_status();
std::string err = vai_last_status_str();
vai_clear_status();
throw vai_exception{status, err.c_str()};
}
static std::exception_ptr current() noexcept {
try {
vai_status status = vai_last_status();
std::string err = vai_last_status_str();
vai_clear_status();
return std::make_exception_ptr(vai_exception{status, err.c_str()});
} catch (...) {
return std::current_exception();
}
}
private:
vai_status status_;
};
class ctx final {
public:
ctx() : ctx_{vai_alloc_ctx()} {
if (!ctx_) {
throw std::bad_alloc();
}
}
explicit ctx(std::unique_ptr<vai_ctx> ctx) : ctx_{std::move(ctx)} {
if (!ctx_) {
throw std::bad_alloc();
}
}
vai_ctx* get_ctx() const { return ctx_.get(); }
private:
std::unique_ptr<vai_ctx> ctx_;
};
} // namespace vertexai
+826
View File
@@ -0,0 +1,826 @@
// Copyright 2018 Intel Corporation.
//
// This is the PlaidML C++ interface, which provides a higher level object
// oriented wrapper on top of the PlaidML C API.
#pragma once
#include <exception>
#include <future>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <half.hpp>
#include "plaidml/base/base_cpp.h"
#include "plaidml/plaidml.h"
using half_float::half;
namespace vertexai {
namespace plaidml {
// Import plaidml_datatype into the namespace
typedef plaidml_datatype datatype;
// Make a map for c++ types to PlaidML types
template <typename T>
struct to_plaidml_datatype {};
template <>
struct to_plaidml_datatype<int8_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_INT8;
};
template <>
struct to_plaidml_datatype<int16_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_INT16;
};
template <>
struct to_plaidml_datatype<int32_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_INT32;
};
template <>
struct to_plaidml_datatype<int64_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_INT64;
};
template <>
struct to_plaidml_datatype<uint8_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_UINT8;
};
template <>
struct to_plaidml_datatype<uint16_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_UINT16;
};
template <>
struct to_plaidml_datatype<uint32_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_UINT32;
};
template <>
struct to_plaidml_datatype<uint64_t> {
static constexpr plaidml_datatype value = PLAIDML_DATA_UINT64;
};
template <>
struct to_plaidml_datatype<half> {
static constexpr plaidml_datatype value = PLAIDML_DATA_FLOAT16;
};
template <>
struct to_plaidml_datatype<float> {
static constexpr plaidml_datatype value = PLAIDML_DATA_FLOAT32;
};
template <>
struct to_plaidml_datatype<double> {
static constexpr plaidml_datatype value = PLAIDML_DATA_FLOAT64;
};
// Predeclare classes
class application;
class base_shape;
class base_tensor;
class buffer;
class compose;
class device;
class device_config;
class function;
class gradient;
class invoker;
template <typename T>
class mapping;
class placeholder;
template <typename T>
class shape;
template <typename T>
class tensor;
class variable;
// A dimension containing both size + stride
struct dimension {
uint64_t size;
int64_t stride;
};
inline std::vector<device_config> _enumerate_devices(const std::shared_ptr<ctx>& ctx,
std::shared_ptr<plaidml_device_enumerator> dev_enum);
inline std::vector<device_config> enumerate_devices(const std::shared_ptr<ctx>& ctx);
inline std::vector<device_config> enumerate_devices(const std::shared_ptr<ctx>& ctx, const std::string& config);
class base_shape {
friend class base_tensor;
friend class invoker;
public:
// Construct an empty shape with a specific data type
explicit base_shape(const std::shared_ptr<ctx>& ctx, datatype dtype = PLAIDML_DATA_FLOAT32)
: ctx_{ctx}, ptr_(plaidml_alloc_shape(ctx->get_ctx(), dtype), plaidml_free_shape) {
vai_exception::check_and_throw(ptr_);
}
// Simple passthrough of C api for setup
void add_dimension(size_t size, ptrdiff_t stride) {
bool r = plaidml_add_dimension(ctx_->get_ctx(), ptr_.get(), size, stride);
vai_exception::check_and_throw(r);
}
void set_offset(size_t offset) {
bool r = plaidml_set_shape_offset(ctx_->get_ctx(), ptr_.get(), offset);
vai_exception::check_and_throw(r);
}
// Add a dimension
void push_back(const dimension& d) { add_dimension(d.size, d.stride); }
// Add multiple dimensions
template <typename L>
void add_dimensions(const L& dims, ptrdiff_t initial_stride = 1) {
ptrdiff_t stride = initial_stride;
for (const auto& sz : dims) {
stride *= sz;
}
for (const auto& sz : dims) {
stride /= sz;
add_dimension(sz, stride);
}
}
// Make a simple shape with packed strides, last dimension lowest stride
base_shape(const std::shared_ptr<ctx>& ctx, datatype dtype, const std::initializer_list<size_t>& il,
uint64_t offset = 0)
: base_shape(ctx, dtype) {
add_dimensions(il);
}
// Get information about a shape
datatype type() const { return plaidml_get_shape_type(ptr_.get()); }
size_t dimensions() const { return plaidml_get_shape_dimension_count(ptr_.get()); }
dimension operator[](size_t i) const { return dimension{size(i), stride(i)}; }
uint64_t size(size_t i) const { return plaidml_get_shape_dimension_size(ptr_.get(), i); }
int64_t stride(size_t i) const { return plaidml_get_shape_dimension_stride(ptr_.get(), i); }
uint64_t buffer_size() const { return plaidml_get_shape_buffer_size(ptr_.get()); }
const std::shared_ptr<ctx>& get_context() const { return ctx_; }
protected:
explicit base_shape(const std::shared_ptr<ctx>& ctx, const std::shared_ptr<plaidml_shape>& ptr)
: ctx_{ctx}, ptr_{ptr} {}
std::shared_ptr<ctx> ctx_;
std::shared_ptr<plaidml_shape> ptr_;
};
template <typename T>
class shape : public base_shape {
public:
explicit shape(const std::shared_ptr<ctx>& ctx, datatype dt = to_plaidml_datatype<T>::value) : base_shape(ctx, dt) {}
explicit shape(const base_shape& base, datatype dt = to_plaidml_datatype<T>::value) : base_shape(base) {
if (dt != base.type()) {
throw vai_exception(VAI_STATUS_INVALID_ARGUMENT, "Mismatched shape");
}
}
shape(const std::shared_ptr<ctx>& ctx, const std::initializer_list<size_t>& il, uint64_t offset = 0)
: base_shape(ctx, to_plaidml_datatype<T>::value, il, offset) {}
};
class buffer {
friend class device;
friend class base_tensor;
public:
buffer() {}
void copy_into(const std::shared_ptr<ctx>& ctx, void* dst) {
plaidml_mapping* mapping = plaidml_map_buffer_discard(ctx->get_ctx(), ptr_.get());
char* src = plaidml_get_mapping_base(ctx->get_ctx(), mapping);
size_t size = plaidml_get_mapping_size(ctx->get_ctx(), mapping);
memcpy(dst, src, size);
plaidml_free_mapping(mapping);
}
void copy_from(const std::shared_ptr<ctx>& ctx, const void* src) {
plaidml_mapping* mapping = plaidml_map_buffer_current(ptr_.get(), nullptr, nullptr);
char* dst = plaidml_get_mapping_base(ctx->get_ctx(), mapping);
size_t size = plaidml_get_mapping_size(ctx->get_ctx(), mapping);
memcpy(dst, src, size);
plaidml_writeback_mapping(ctx->get_ctx(), mapping);
plaidml_free_mapping(mapping);
}
private:
std::shared_ptr<plaidml_buffer> ptr_;
explicit buffer(const std::shared_ptr<plaidml_buffer>& ptr) : ptr_(ptr) {}
};
class base_tensor {
friend class variable;
public:
base_tensor() {}
base_tensor(const std::shared_ptr<ctx>& ctx, const buffer& buf, const base_shape& shape)
: ctx_(ctx), buf_(buf.ptr_), shape_(shape.ptr_) {}
base_shape get_shape() { return base_shape(ctx_, shape_); }
buffer get_buffer() { return buffer(buf_); }
std::shared_ptr<ctx> get_context() { return ctx_; }
protected:
std::shared_ptr<ctx> ctx_;
std::shared_ptr<plaidml_buffer> buf_;
std::shared_ptr<plaidml_shape> shape_;
};
// Indicates that the mapping will be used for reading the buffer. The mapping
// will reflect the buffer's current contents. By default, the implementation
// is free to either discard the mapping's contents or to write them back to
// the underlying buffer.
struct map_for_read_t {};
static constexpr map_for_read_t map_for_read = {};
// Indicates that the mapping will be used for writing to the buffer. The
// mapping may not reflect the buffer's current contents; the implementation is
// free to construct the mapping with garbage data. By default, the mapping's
// contents will be written back to the buffer when the mapping is deleted
// unless the deletion is due to an exceptional condition.
struct map_for_write_t {};
static constexpr map_for_write_t map_for_write = {};
// Indicates that the mapping will be used for read-write access to the buffer.
// The mapping will reflect the buffer's current contents. By default, the
// mapping's contents will be written back to the buffer when the mapping is
// deleted unless the deletion is due to an exceptional condition.
struct map_for_update_t {};
static constexpr map_for_update_t map_for_update = {};
enum class mapping_destructor_behavior {
writeback_if_normal, // Write the contents on normal exits
writeback_always, // Always write the contents (including on exceptions)
discard // The implementation may discard the contents
};
template <typename T>
class mapping {
public:
// Construct an uninitialized mapping.
mapping() {}
~mapping() { release(); }
// Disallow copy + default construction
mapping(const mapping& rhs) = delete;
mapping& operator=(const mapping& rhs) = delete;
// Allow moves
mapping(mapping&& rhs)
: ctx_{std::move(rhs.ctx_)},
buf_{std::move(rhs.buf_)},
sizes_{std::move(rhs.sizes_)},
strides_{std::move(rhs.strides_)},
map_{std::move(rhs.map_)},
behavior_{std::move(rhs.behavior_)},
mapped_{rhs.mapped_} {
rhs.mapped_ = nullptr;
}
mapping& operator=(mapping&& rhs) {
release();
ctx_ = std::move(rhs.ctx_);
buf_ = std::move(rhs.buf_);
sizes_ = std::move(rhs.sizes_);
strides_ = std::move(rhs.strides_);
map_ = std::move(rhs.map_);
behavior_ = std::move(rhs.behavior_);
mapped_ = rhs.mapped_;
rhs.mapped_ = nullptr;
return *this;
}
// Provide access to raw buffer
T* raw() { return mapped_; }
// Explicitly set the destruction behavior.
void set_destructor_behavior(mapping_destructor_behavior behavior) { behavior_ = behavior; }
// Compute location of index, also do bounds check. Note: this is a convience function;
// it is not designed to be performant.
T& at(const std::initializer_list<size_t>& idx) {
if (idx.size() != sizes_.size()) {
throw vai_exception(VAI_STATUS_OUT_OF_RANGE, "Invalid number of indexes in mapping access");
}
ptrdiff_t off = 0;
for (size_t i = 0; i < sizes_.size(); i++) {
if (*(idx.begin() + i) >= sizes_[i]) {
throw vai_exception(VAI_STATUS_OUT_OF_RANGE, "Index out of bound on mapping access");
}
off += strides_[i] * *(idx.begin() + i);
}
return mapped_[off];
}
// Syntactic sugar
template <typename... Args>
T& operator()(Args... args) {
return at({args...});
}
private:
friend class tensor<T>;
mapping(std::shared_ptr<ctx> ctx, std::shared_ptr<plaidml_buffer> buf, const std::shared_ptr<plaidml_shape>& shape,
std::unique_ptr<plaidml_mapping> map, mapping_destructor_behavior behavior)
: ctx_{std::move(ctx)}, buf_{std::move(buf)}, map_{std::move(map)}, behavior_{behavior} {
sizes_.resize(plaidml_get_shape_dimension_count(shape.get()));
strides_.resize(plaidml_get_shape_dimension_count(shape.get()));
for (size_t i = 0; i < strides_.size(); i++) {
sizes_[i] = plaidml_get_shape_dimension_size(shape.get(), i);
strides_[i] = plaidml_get_shape_dimension_stride(shape.get(), i);
}
mapped_ = reinterpret_cast<T*>(plaidml_get_mapping_base(ctx_->get_ctx(), map_.get()));
vai_exception::check_and_throw(mapped_);
}
void release() {
if (!mapped_) {
return;
}
switch (behavior_) {
case mapping_destructor_behavior::writeback_if_normal:
#ifdef __cpp_lib_uncaught_exceptions
if (std::uncaught_exceptions()) {
break;
}
#else
if (std::uncaught_exception()) {
break;
}
#endif
// fallthrough
case mapping_destructor_behavior::writeback_always:
plaidml_writeback_mapping(ctx_->get_ctx(), map_.get());
break;
case mapping_destructor_behavior::discard:
break;
}
mapped_ = nullptr;
}
std::shared_ptr<ctx> ctx_;
std::shared_ptr<plaidml_buffer> buf_;
std::vector<size_t> sizes_;
std::vector<ptrdiff_t> strides_;
std::unique_ptr<plaidml_mapping> map_;
mapping_destructor_behavior behavior_;
T* mapped_ = nullptr;
};
template <typename T>
class tensor : public base_tensor {
friend class mapping<T>;
public:
tensor() {}
tensor(const std::shared_ptr<ctx>& ctx, const buffer& buf, const shape<T>& shape) : base_tensor(ctx, buf, shape) {}
mapping<T> map(map_for_read_t) const {
std::unique_ptr<plaidml_mapping> m{plaidml_map_buffer_current(buf_.get(), NULL, NULL)};
return mapping<T>{ctx_, buf_, shape_, std::move(m), mapping_destructor_behavior::discard};
}
// Asynchronously creates a readable mapping. The completion function should take a std::future<mapping<T>>,
// which will be a ready future for the result of the mapping call.
template <typename C>
void map(map_for_read_t, vai_ctx* ctx, C&& on_complete) const {
std::unique_ptr<completion> comp{
static_cast<completion*>(new typed_completion<C>(ctx_, buf_, shape_, std::forward<C>(on_complete)))};
plaidml_map_buffer_current(buf_.get(), &OnMapped, comp.release());
}
mapping<T> map(map_for_write_t) {
std::unique_ptr<plaidml_mapping> m{plaidml_map_buffer_discard(ctx_->get_ctx(), buf_.get())};
return mapping<T>{ctx_, buf_, shape_, std::move(m), mapping_destructor_behavior::writeback_if_normal};
}
mapping<T> map(map_for_update_t) {
std::unique_ptr<plaidml_mapping> m{plaidml_map_buffer_current(buf_.get(), NULL, NULL)};
return mapping<T>{ctx_, buf_, shape_, std::move(m), mapping_destructor_behavior::writeback_if_normal};
}
private:
class completion {
public:
virtual ~completion() {}
virtual void complete(plaidml_mapping* result) = 0;
};
template <typename C>
class typed_completion final : public completion {
public:
typed_completion(std::shared_ptr<ctx> ctx, std::shared_ptr<plaidml_buffer> buf,
std::shared_ptr<plaidml_shape> shape, C&& on_complete)
: ctx_{std::move(ctx)},
buf_{std::move(buf)},
shape_{std::move(shape)},
on_complete_{std::forward<C>(on_complete)} {}
void complete(plaidml_mapping* result) final {
if (!result) {
prom_.set_exception(vai_exception::current());
} else {
std::unique_ptr<plaidml_mapping> mp{result};
prom_.set_value(mapping<T>{ctx_, std::move(buf_), shape_, std::move(mp), mapping_destructor_behavior::discard});
}
on_complete_(prom_.get_future());
}
private:
std::shared_ptr<ctx> ctx_;
std::shared_ptr<plaidml_buffer> buf_;
std::shared_ptr<plaidml_shape> shape_;
std::promise<mapping<T>> prom_;
C on_complete_;
};
static void OnMapped(void* arg, plaidml_mapping* result) noexcept {
std::unique_ptr<completion> comp{static_cast<completion*>(arg)};
comp->complete(result);
}
};
class placeholder {
friend class variable;
friend class compose;
public:
placeholder() {}
explicit placeholder(size_t ndims) : ptr_(plaidml_alloc_placeholder(ndims), plaidml_free_var) {
vai_exception::check_and_throw(ptr_);
}
private:
std::shared_ptr<plaidml_var> ptr_;
};
class variable {
friend class application;
friend class compose;
friend class function;
friend class gradient;
friend class invoker;
public:
variable() {}
variable(const int64_t& val) : ptr_(plaidml_alloc_int64(val), plaidml_free_var) { // NOLINT(runtime/explicit)
vai_exception::check_and_throw(ptr_);
}
variable(const double& val) : ptr_(plaidml_alloc_real(val), plaidml_free_var) { // NOLINT(runtime/explicit)
vai_exception::check_and_throw(ptr_);
}
variable(const placeholder& val) : ptr_(val.ptr_) {} // NOLINT(runtime/explicit)
variable(const base_tensor& val) // NOLINT(runtime/explicit)
: ptr_(plaidml_alloc_tensor(val.ctx_->get_ctx(), val.buf_.get(), val.shape_.get()), plaidml_free_var) {
vai_exception::check_and_throw(ptr_);
}
private:
std::shared_ptr<plaidml_var> ptr_;
};
class application {
friend class function;
friend class compose;
public:
application() {}
operator variable() {
if (plaidml_get_function_output_count(func_.get()) != 1) {
throw std::runtime_error("Function application with non-unique return used in variable context");
}
return get_output(0);
}
variable get_output(size_t i) {
if (i >= plaidml_get_function_output_count(func_.get())) {
throw std::runtime_error("Attempting to get an invalid output index");
}
std::string oname = plaidml_get_function_output(func_.get(), i);
return get_output(oname);
}
variable get_output(const std::string& name) {
variable r;
std::shared_ptr<plaidml_var> out(plaidml_apply_alloc_output(ptr_.get(), name.c_str()), plaidml_free_var);
vai_exception::check_and_throw(out);
r.ptr_ = out;
return r;
}
private:
std::shared_ptr<plaidml_function> func_;
std::shared_ptr<plaidml_applier> ptr_;
application(const std::shared_ptr<plaidml_function> func, const std::shared_ptr<plaidml_applier>& ptr)
: func_(func), ptr_(ptr) {}
};
class function {
friend class compose;
friend class invoker;
public:
typedef std::vector<std::pair<std::string, variable>> parameters_t;
typedef std::vector<variable> positional_t;
// Invalid function
function() {}
// Make a function from code
explicit function(const std::string& str, const std::string& id = "")
: ptr_(plaidml_build_coded_function(str.c_str(), id.c_str()), plaidml_free_function) {
vai_exception::check_and_throw(ptr_);
}
// Load and save function
inline void load(const std::shared_ptr<ctx>& ctx, const device& dev,
const std::string& file); // Later, after dev is defined
void save(const std::string& file) {
vai_exception::check_and_throw(plaidml_save_function(ptr_.get(), file.c_str()));
}
// Get information
size_t num_inputs() { return plaidml_get_function_input_count(ptr_.get()); }
size_t num_outputs() { return plaidml_get_function_output_count(ptr_.get()); }
std::string input_name(size_t i) {
const char* name = plaidml_get_function_input(ptr_.get(), i);
return (name == NULL ? "" : name);
}
std::string output_name(size_t i) {
const char* name = plaidml_get_function_output(ptr_.get(), i);
return (name == NULL ? "" : name);
}
// Apply a function to values, produce new values, named parameters
application apply(const parameters_t& inputs, const std::vector<application> prev = {}) {
std::shared_ptr<plaidml_applier> app(plaidml_alloc_applier(ptr_.get()), plaidml_free_applier);
vai_exception::check_and_throw(app);
for (const auto& papp : prev) {
bool r = plaidml_apply_add_dependency(app.get(), papp.ptr_.get());
vai_exception::check_and_throw(r);
}
for (const auto& arg : inputs) {
bool r = plaidml_apply_add_input(app.get(), arg.first.c_str(), arg.second.ptr_.get());
vai_exception::check_and_throw(r);
}
return application(ptr_, app);
}
application apply(const positional_t& inputs, const std::vector<application> prev = {}) {
if (inputs.size() != num_inputs()) {
throw std::runtime_error("Mismatched number of input in application: " + std::to_string(inputs.size()) + " vs " +
std::to_string(num_inputs()));
}
std::shared_ptr<plaidml_applier> app(plaidml_alloc_applier(ptr_.get()), plaidml_free_applier);
vai_exception::check_and_throw(app);
for (const auto& papp : prev) {
bool r = plaidml_apply_add_dependency(app.get(), papp.ptr_.get());
vai_exception::check_and_throw(r);
}
for (size_t idx = 0; idx < inputs.size(); ++idx) {
bool r = plaidml_apply_add_input(app.get(), input_name(idx).c_str(), inputs[idx].ptr_.get());
vai_exception::check_and_throw(r);
}
return application(ptr_, app);
}
// Operator() for ease of use in apply
template <typename... Params>
application operator()(Params... params) {
return apply(std::vector<variable>{params...});
}
private:
std::shared_ptr<plaidml_function> ptr_;
explicit function(const std::shared_ptr<plaidml_function>& ptr) : ptr_(ptr) {}
};
class compose {
public:
explicit compose(const std::string name = "") : ptr_(plaidml_alloc_composer(), plaidml_free_composer) {
vai_exception::check_and_throw(ptr_);
}
compose& input(const std::string& name, const placeholder& p) {
bool r = plaidml_add_composer_input(ptr_.get(), name.c_str(), p.ptr_.get());
vai_exception::check_and_throw(r);
return *this;
}
compose& output(const std::string& name, const variable& p) {
bool r = plaidml_add_composer_output(ptr_.get(), name.c_str(), p.ptr_.get());
vai_exception::check_and_throw(r);
return *this;
}
compose& dependency(const application& prev) {
bool r = plaidml_add_composer_dependency(ptr_.get(), prev.ptr_.get());
vai_exception::check_and_throw(r);
return *this;
}
compose& update(const base_tensor& lhs, const variable& rhs) {
variable tvar = lhs;
bool r = plaidml_add_composer_update(ptr_.get(), tvar.ptr_.get(), rhs.ptr_.get());
vai_exception::check_and_throw(r);
return *this;
}
operator function() {
std::shared_ptr<plaidml_function> func(plaidml_build_composed_function(ptr_.get()), plaidml_free_function);
vai_exception::check_and_throw(func);
return function(func);
}
private:
std::shared_ptr<plaidml_composer> ptr_;
};
class invoker {
public:
invoker() {}
invoker(const invoker&) = delete;
invoker(invoker&&) = default;
invoker& operator=(const invoker&) = delete;
invoker& operator=(invoker&&) = default;
invoker(const std::shared_ptr<ctx>& ctx, const function& func)
: ctx_{ctx}, invoker_{plaidml_alloc_invoker(ctx_->get_ctx(), func.ptr_.get())} {
vai_exception::check_and_throw(invoker_);
}
invoker& set_input(const std::string& name, const variable& var) {
auto r = plaidml_set_invoker_input(invoker_.get(), name.c_str(), var.ptr_.get());
vai_exception::check_and_throw(r);
return *this;
}
invoker& set_output(const std::string& name, const variable& var) {
auto r = plaidml_set_invoker_output(invoker_.get(), name.c_str(), var.ptr_.get());
vai_exception::check_and_throw(r);
return *this;
}
base_shape output_shape(const std::string& name) {
std::shared_ptr<plaidml_shape> shp{plaidml_alloc_invoker_output_shape(invoker_.get(), name.c_str()),
plaidml_free_shape};
vai_exception::check_and_throw(shp);
return base_shape{ctx_, std::move(shp)};
}
void save(const std::string& file, plaidml_file_format format) {
vai_exception::check_and_throw(plaidml_save_invoker(invoker_.get(), file.c_str(), format));
}
void set_const() { vai_exception::check_and_throw(plaidml_set_invoker_const(invoker_.get())); }
std::unique_ptr<plaidml_invocation> invoke() {
std::unique_ptr<plaidml_invocation> invocation{plaidml_schedule_invocation(ctx_->get_ctx(), invoker_.get())};
vai_exception::check_and_throw(invocation);
return invocation;
}
std::unique_ptr<plaidml_invocation> invoke(const std::shared_ptr<ctx>& ctx) {
std::unique_ptr<plaidml_invocation> invocation{plaidml_schedule_invocation(ctx->get_ctx(), invoker_.get())};
vai_exception::check_and_throw(invocation);
return invocation;
}
private:
std::shared_ptr<ctx> ctx_;
std::unique_ptr<plaidml_invoker> invoker_;
};
// TODO: Fix this!
class gradient {
public:
explicit gradient(const variable& var) : ptr_(plaidml_alloc_gradient(var.ptr_.get()), plaidml_free_gradient) {
vai_exception::check_and_throw(ptr_);
}
variable operator()(const variable& v) {
variable r;
plaidml_var* var = plaidml_compute_grad_wrt(ptr_.get(), v.ptr_.get());
vai_exception::check_and_throw(var);
r.ptr_ = std::shared_ptr<plaidml_var>(var, plaidml_free_var);
return r;
}
private:
std::shared_ptr<plaidml_gradient> ptr_;
};
class device {
friend class function;
friend class device_config;
public:
device() = default;
bool operator!() const { return !ptr_; }
buffer allocate(uint64_t size) const {
buffer r;
r.ptr_ =
std::shared_ptr<plaidml_buffer>(plaidml_alloc_buffer(ctx_->get_ctx(), ptr_.get(), size), plaidml_free_buffer);
vai_exception::check_and_throw(r.ptr_);
return r;
}
base_tensor allocate(const base_shape& s) const { return base_tensor(s.get_context(), allocate(s.buffer_size()), s); }
template <class T>
tensor<T> allocate(const shape<T>& s) const {
return tensor<T>(s.get_context(), allocate(s.buffer_size()), s);
}
private:
explicit device(const std::shared_ptr<ctx>& ctx, plaidml_device* raw) : ctx_{ctx}, ptr_(raw, plaidml_close_device) {}
std::shared_ptr<ctx> ctx_;
std::shared_ptr<plaidml_device> ptr_;
const std::shared_ptr<ctx>& get_context() const { return ctx_; }
};
class device_config {
friend std::vector<device_config> _enumerate_devices(const std::shared_ptr<ctx>& ctx,
std::shared_ptr<plaidml_device_enumerator> dev_enum);
public:
// Get any string based property
std::string get_string_prop(plaidml_device_property prop) const {
size_t out_size;
bool r = plaidml_query_devconf(ctx_->get_ctx(), config_, prop, NULL, 0, &out_size);
vai_exception::check_and_throw(r);
std::string str(out_size, '\0');
r = plaidml_query_devconf(ctx_->get_ctx(), config_, prop, &str[0], str.size(), NULL);
str.pop_back();
vai_exception::check_and_throw(r);
return str;
}
// Convenience functions for current properties
std::string id() const { return get_string_prop(PLAIDML_DEVICE_ID); }
std::string config() const { return get_string_prop(PLAIDML_DEVICE_CONFIG); }
std::string description() const { return get_string_prop(PLAIDML_DEVICE_DESCRIPTION); }
std::string details() const { return get_string_prop(PLAIDML_DEVICE_DETAILS); }
// Open the device
device open() const {
device dev(ctx_, plaidml_open_device(ctx_->get_ctx(), config_));
vai_exception::check_and_throw(dev.ptr_);
return dev;
}
private:
device_config(const std::shared_ptr<ctx>& ctx, const std::shared_ptr<plaidml_device_enumerator>& dev_enum,
plaidml_devconf* config)
: ctx_(ctx), dev_enum_(dev_enum), config_(config) {}
std::shared_ptr<ctx> ctx_;
std::shared_ptr<plaidml_device_enumerator> dev_enum_;
plaidml_devconf* config_;
};
std::vector<device_config> _enumerate_devices(const std::shared_ptr<ctx>& ctx,
std::shared_ptr<plaidml_device_enumerator> dev_enum) {
std::vector<device_config> out;
size_t i = 0;
while (true) {
plaidml_devconf* conf = plaidml_get_devconf(ctx->get_ctx(), dev_enum.get(), i);
if (conf == NULL) break;
i++;
out.push_back(device_config(ctx, dev_enum, conf));
}
vai_clear_status(); // Since we always walk off the list, clear errors
return out;
}
std::vector<device_config> enumerate_devices(const std::shared_ptr<ctx>& ctx) {
std::shared_ptr<plaidml_device_enumerator> dev_enum(plaidml_alloc_device_enumerator(ctx->get_ctx(), NULL, NULL),
plaidml_free_device_enumerator);
vai_exception::check_and_throw(dev_enum);
return _enumerate_devices(ctx, dev_enum);
}
std::vector<device_config> enumerate_devices(const std::shared_ptr<ctx>& ctx, const std::string& config) {
std::vector<device_config> out;
std::shared_ptr<plaidml_device_enumerator> dev_enum(
plaidml_alloc_device_enumerator_with_config(ctx->get_ctx(), config.c_str(), NULL, NULL),
plaidml_free_device_enumerator);
vai_exception::check_and_throw(dev_enum);
return _enumerate_devices(ctx, dev_enum);
}
// Actually needs definitions of both classes
void function::load(const std::shared_ptr<ctx>& ctx, const device& dev, const std::string& file) {
ptr_ = std::shared_ptr<plaidml_function>(plaidml_load_function(ctx->get_ctx(), dev.ptr_.get(), file.c_str()),
plaidml_free_function);
vai_exception::check_and_throw(ptr_);
}
} // namespace plaidml
} // namespace vertexai
+637
View File
@@ -0,0 +1,637 @@
// Copyright 2018 Intel Corporation.
//
// This is the PlaidML library interface, used to construct and manipulate
// programs defined as a sequence of operations over tensors.
#pragma once
#ifdef __cplusplus
#include <cstddef>
#include <memory>
#else
#include <stdbool.h>
#include <stddef.h>
#endif // __cplusplus
#include "plaidml/base/base.h"
#define PLAIDML_API VAI_API
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// PlaidML devconf objects represent device configurations. Devconf objects are
// contained by PlaidML device enumerators, examined and potentially modified by
// PlaidML library consumers, and then used to open PlaidML devices for compute
// access.
#ifdef __cplusplus
struct plaidml_devconf;
#else
typedef struct plaidml_devconf plaidml_devconf;
#endif // __cplusplus
// Platform configuration properties. This set may be extended in the future.
typedef enum {
// Platform names are NUL-terminated strings.
PLAIDML_DEVICE_ID = 1,
// Platform configs are NUL-terminated prototxt.
PLAIDML_DEVICE_CONFIG = 2,
// Platform descriptions are NUL-terminated prototxt.
PLAIDML_DEVICE_DESCRIPTION = 3,
// Platform descriptions are NUL-terminated prototxt.
PLAIDML_DEVICE_DETAILS = 4
} plaidml_device_property;
// Returns the version of plaidml
PLAIDML_API const char* plaidml_get_version();
// Queries the supplied device configuration property.
//
// The supplied output buffer pointer should point to a property-specific value
// to be filled in with the requested information; the output size is used for
// property versioning and buffer overflow protection. Fields in the output
// buffer not supported by the property implementation will be zero-filled
// (i.e. zero is the default value for all properties, including unsupported
// properties).
//
// The value pointed to by output_buffer_size_required, if provided, will be
// filled in with the size required for the output buffer. This is most useful
// with queries that return string values or arrays of values; a common pattern
// is to make a call with a NULL output buffer and zero size, allocate a buffer
// of the indicated output size required, and then to re-issue the query.
PLAIDML_API bool plaidml_query_devconf(vai_ctx* ctx, plaidml_devconf* devconf, plaidml_device_property property,
void* output_buffer, size_t output_buffer_size,
size_t* output_buffer_size_required);
// PlaidML devices are used to supply resources that might be backed by a
// variety of hardware.
#ifdef __cplusplus
struct plaidml_device;
#else
typedef struct plaidml_device plaidml_device;
#endif // __cplusplus
// Opens a device, using the supplied device configuration. A NULL
// configuration is permitted; this will use the system default PlaidML compute
// device.
PLAIDML_API plaidml_device* plaidml_open_device(vai_ctx* ctx, plaidml_devconf* devconf);
// Closes a device. After this call, the device should not be used for any
// subsequent calls. The device may share resources with other objects (such
// as buffers and functions); those resources will only be released when they
// are no longer needed by those other objects. Closing a NULL device is a
// no-op.
PLAIDML_API void plaidml_close_device(plaidml_device* device);
// PlaidML device enumerators offer access to sets of compute devices. Enumerators may
// supply devices that are in-process, machine-local, or cross-network, depending on
// the deployment configuration.
#ifdef __cplusplus
struct plaidml_device_enumerator;
#else
typedef struct plaidml_device_enumerator plaidml_device_enumerator;
#endif // __cplusplus
// Allocates a device enumerator and initializes it using automatic configuration.
//
// If the supplied callback is NULL, the call will block until the enumerator
// is fully initialized, and then will return a pointer to the enumerator
// (unless an error occurs).
//
// If the supplied callback is non-NULL, the call will immediately return NULL,
// and will arrange for the callback to be invoked with the enumerator once the
// enumerator becomes available (calling it with a NULL enumerator on error). The
// library guarantees to invoke the callback exactly once.
//
// If the library returns an enumerator, there will be at least one
// configured device available; otherwise, the call will fail with
// VAI_STATUS_NOT_FOUND.
//
// The library may invoke the callback synchronously if the enumerator is
// immediately available or in error conditions.
PLAIDML_API plaidml_device_enumerator* plaidml_alloc_device_enumerator(
vai_ctx* ctx, void (*callback)(void* arg, plaidml_device_enumerator* enumerator), void* arg);
// Allocates a device enumerator and initializes it using the supplied
// configuration. Otherwise identical to the version that uses system
// config.
PLAIDML_API plaidml_device_enumerator* plaidml_alloc_device_enumerator_with_config(
vai_ctx* ctx, const char* configuration, void (*callback)(void* arg, plaidml_device_enumerator* enumerator),
void* arg);
// Frees a device enumerator. After this call, the enumerator should not be
// used for any subsequent calls. The enumerator may share resources with
// other objects (such as devices, buffers, and functions); those resources
// will only be released when they are no longer needed by those other objects.
// Freeing a NULL enumerator is a no-op.
PLAIDML_API void plaidml_free_device_enumerator(plaidml_device_enumerator* enumerator);
// Gets the configuration file that was used to initialize devices.
PLAIDML_API const char* plaidml_get_enumerator_config_source(plaidml_device_enumerator* enumerator);
// Gets the number of device valid or invalid configurations available.
PLAIDML_API size_t plaidml_get_devconf_count(vai_ctx* ctx, plaidml_device_enumerator* enumerator, bool valid_devices);
// Gets one device configuration from a device enumerator. The lifetime of the
// device configuration is bounded by the device enumerator; there's no need to
// separately free the configuration. If the requested configuration index is
// out of range, or if the enumerator is NULL, this call will return NULL.
PLAIDML_API plaidml_devconf* plaidml_get_devconf(vai_ctx* ctx, plaidml_device_enumerator* enumerator, size_t index);
// Same as above, only returns invalid devices
PLAIDML_API plaidml_devconf* plaidml_get_invalid_devconf(vai_ctx* ctx, plaidml_device_enumerator* enumerator,
size_t index);
// PlaidML buffers are used to create bindings between actual data and the data
// elements in PlaidML programs.
#ifdef __cplusplus
struct plaidml_buffer;
#else
typedef struct plaidml_buffer plaidml_buffer;
#endif // __cplusplus
// PlaidML mappings are used to view and manipulate the contents of buffers.
#ifdef __cplusplus
struct plaidml_mapping;
#else
typedef struct plaidml_mapping plaidml_mapping;
#endif // __cplusplus
// Allocates a buffer of the supplied raw memory size, or returns NULL if the
// library cannot allocate sufficient memory, or if the supplied device is
// NULL.
PLAIDML_API plaidml_buffer* plaidml_alloc_buffer(vai_ctx* ctx, plaidml_device* device, uint64_t size);
// Frees a buffer. After this call, the buffer should not be used for any
// subsequent calls. Freeing a NULL buffer is a no-op.
PLAIDML_API void plaidml_free_buffer(plaidml_buffer* buffer);
// Maps a buffer's current contents into memory.
//
// If the supplied callback is NULL, the call will block until the buffer is
// available, and then will return a pointer to a mapping for the buffer
// (unless an error occurs).
//
// If the supplied callback is non-NULL, the call will immediately return NULL,
// and will arrange for the callback to be invoked with the mapping once the
// buffer's data becomes available (calling it with a NULL mapping on error).
//
// The library may invoke the callback synchronously if the buffer's data is
// already available or in error conditions.
//
// A NULL buffer may be supplied; this will always result in a NULL address,
// and an out-of-memory error in the current thread's thread-local storage.
PLAIDML_API plaidml_mapping* plaidml_map_buffer_current(plaidml_buffer* buffer,
void (*callback)(void* arg, plaidml_mapping* mapping),
void* arg);
// Maps a buffer into memory, possibly discarding its current contents.
//
// The implementation may preserve or discard the buffer's contents when
// constructing the mapping; callers should not assume that the buffer has been
// initialized.
//
// The implementation may construct a non-coherent mapping: reads from the
// buffer may return arbitrary values, even after writing the same memory from
// the same processor.
//
// A NULL buffer may be supplied; this will always result in a NULL address,
// and an out-of-memory error in the current thread's thread-local storage.
PLAIDML_API plaidml_mapping* plaidml_map_buffer_discard(vai_ctx* ctx, plaidml_buffer* buffer);
// Gets the base address of a mapping's mapped memory region. If the mapping
// has been written back to the buffer, this call will return NULL. A NULL
// mapping may be supplied; this will always return NULL.
PLAIDML_API char* plaidml_get_mapping_base(vai_ctx* ctx, plaidml_mapping* mapping);
// Gets the size of a mapping's mapped memory region. If the mapping has been
// written back to the buffer, this call will return 0. A NULL mapping may be
// supplied; this will always return 0.
PLAIDML_API size_t plaidml_get_mapping_size(vai_ctx* ctx, plaidml_mapping* mapping);
// Synchronizes a mapping with its backing store (if required by the underlying
// device implementation), possibly removing the mapping from the current
// virtual address space.
//
// After this call, callers must not access the mapping's previous virtual
// memory region.
//
// Callers are allowed to free the mapping and use the buffer as a program input
// immediately after this call.
//
// A NULL mapping may be supplied; this will always result in a false result
// and an out-of-memory error in the current thread's thread-local storage.
PLAIDML_API bool plaidml_writeback_mapping(vai_ctx* ctx, plaidml_mapping* mapping);
// Removes a mapping that's no longer required by the caller.
//
// After this call, callers must not access the mapping's previous virtual
// memory region.
//
// Freeing a NULL mapping is a no-op.
PLAIDML_API void plaidml_free_mapping(plaidml_mapping* mapping);
// PlaidML shapes describe the layout of the data within a buffer as observed by a
// program.
#ifdef __cplusplus
struct plaidml_shape;
#else
typedef struct plaidml_shape plaidml_shape;
#endif // __cplusplus
// Set the default datatype for floating-point computations.
PLAIDML_API void plaidml_set_floatx(plaidml_datatype datatype);
// Allocates a shape, or returns NULL if the library cannot allocate sufficient
// memory. Note that shapes must have dimensions added before use.
PLAIDML_API plaidml_shape* plaidml_alloc_shape(vai_ctx* ctx, plaidml_datatype datatype);
// Frees a shape. After this call, the shape should not be used for any
// subsequent calls. Freeing a NULL shape is a no-op.
PLAIDML_API void plaidml_free_shape(plaidml_shape* shape);
// Sets a shape's offset, in elements, from the beginning of the data.
PLAIDML_API bool plaidml_set_shape_offset(vai_ctx* ctx, plaidml_shape* shape, uint64_t offset_in_elements);
// Set a shape's layout
PLAIDML_API bool plaidml_shape_set_layout(vai_ctx* ctx, plaidml_shape* shape, const char* layout);
// Adds a dimension to a shape. Dimension sizes and strides are measured in
// elements of the shape's datatype, not by local buffer byte counts.
PLAIDML_API bool plaidml_add_dimension(vai_ctx* ctx, plaidml_shape* shape, uint64_t size_in_elements,
int64_t stride_in_elements);
// Gets a shape's type.
PLAIDML_API plaidml_datatype plaidml_get_shape_type(plaidml_shape* shape);
// Gets a shape's offset.
PLAIDML_API uint64_t plaidml_get_shape_offset(plaidml_shape* shape);
// Get the number of dimensions for a shape.
PLAIDML_API size_t plaidml_get_shape_dimension_count(plaidml_shape* shape);
// Gets the size in elements for a given shape dimension.
// If the dimension is out of range, zero will be returned.
PLAIDML_API uint64_t plaidml_get_shape_dimension_size(plaidml_shape* shape, size_t dim);
// Gets the stride in elements for a given shape dimension.
// If the dimension is out of range, zero will be returned.
PLAIDML_API int64_t plaidml_get_shape_dimension_stride(plaidml_shape* shape, size_t dim);
// Gets the byte size required for a buffer to hold the given shape.
PLAIDML_API uint64_t plaidml_get_shape_buffer_size(plaidml_shape* shape);
// Gets the underlying element count described by the given shape.
PLAIDML_API uint64_t plaidml_get_shape_element_count(plaidml_shape* shape);
// A PlaidML function defines a transformation from some set of inputs to
// some set of outputs.
#ifdef __cplusplus
struct plaidml_function;
#else
typedef struct plaidml_function plaidml_function;
#endif // __cplusplus
// Frees a function. After this call, the function should not be used for any
// subsequent calls. Freeing a NULL function is a no-op.
PLAIDML_API void plaidml_free_function(plaidml_function* function);
// Return the number of inputs to a function, 0 if function is NULL
PLAIDML_API size_t plaidml_get_function_input_count(plaidml_function* function);
// Return the name of input i for a function, or NULL if function is NULL or out of bounds
PLAIDML_API const char* plaidml_get_function_input(plaidml_function* function, size_t i);
// Return the number of outpus from function, 0 if function is NULL
PLAIDML_API size_t plaidml_get_function_output_count(plaidml_function* function);
// Return the name of output i for a function, or NULL if function is NULL or out of bounds
PLAIDML_API const char* plaidml_get_function_output(plaidml_function* function, size_t i);
// A PlaidML var is an input to or an output from a PlaidML function.
#ifdef __cplusplus
struct plaidml_var;
#else
typedef struct plaidml_var plaidml_var;
#endif // __cplusplus
// Frees a var. After this call, the var should not be used for any
// subsequent calls. Freeing a NULL var is a no-op.
PLAIDML_API void plaidml_free_var(plaidml_var* var);
// Allocate a placeholder var.
//
// A placeholder can be used during function application: used as the output of
// one function application and the the input of another function application,
// the placeholder defines an information flow between the functions. A
// placeholder can also during function composition: the placeholder can be
// bound to the inputs or outputs of the composed function.
//
// num_dimensions specifies the placeholder dimension count: PlaidML functions are
// polymorphic with respect to tensor sizes and datatypes, but not to the
// actual dimension count of their inputs and outputs, so placeholders need to
// indicate the number of dimensions of the variables they will eventually be
// bound to. For scalar placeholders, specify zero for the dimension count.
PLAIDML_API plaidml_var* plaidml_alloc_placeholder(size_t num_dimensions);
// Allocates a var representing a signed integer constant.
PLAIDML_API plaidml_var* plaidml_alloc_int64(int64_t value);
// Allocates a var representing a floating point constant.
PLAIDML_API plaidml_var* plaidml_alloc_real(double value);
// Allocates a var representing a tensor, bound to the given shape and buffer.
PLAIDML_API plaidml_var* plaidml_alloc_tensor(vai_ctx* ctx, plaidml_buffer* buffer, plaidml_shape* shape);
// Attaches quantization parameters to a weights tensor
PLAIDML_API bool plaidml_tensor_attach_qparams(plaidml_var* tensor, plaidml_var* qparams);
// Builds a function from the supplied code written in the PlaidML operation
// description language. If 'id' is not NULL, attach the id to the function
// for tracking purposes.
PLAIDML_API plaidml_function* plaidml_build_coded_function(const char* code, const char* id);
// TODO: Make more general method to serialize things.
// Load a function (possibly with bound tensors) from a file
PLAIDML_API plaidml_function* plaidml_load_function(vai_ctx* ctx, plaidml_device* dev, const char* filename);
// Store a function (possibly with bound tensors) from to a file
PLAIDML_API bool plaidml_save_function(plaidml_function* func, const char* filename);
// Predeclare applier
// A PlaidML applier describes the application of a PlaidML function to some
// particular set of inputs, yielding some particular set of outputs. (For
// example, you can think of "+" as a function; applying it to "2" and "3"
// yields a particular output, "5".)
#ifdef __cplusplus
struct plaidml_applier;
#else
typedef struct plaidml_applier plaidml_applier;
#endif // __cplusplus
// A PlaidML composer builds a new function out of a set of vars, where the values
// of the output vars have been previously defined (by using an applier),
// either in terms of placeholders (which become the new function inputs), or
// in terms of mutable tensors (which will be mutated each time the function is
// run).
#ifdef __cplusplus
struct plaidml_composer;
#else
typedef struct plaidml_composer plaidml_composer;
#endif // __cplusplus
// Allocates a composer, or returns NULL if the library cannot allocate sufficient memory.
PLAIDML_API plaidml_composer* plaidml_alloc_composer();
// Binds a placeholder var to a named input of a composed function.
PLAIDML_API bool plaidml_add_composer_input(plaidml_composer* composer, const char* name, plaidml_var* var);
// Binds a computed value var to a named output of a composed function.
PLAIDML_API bool plaidml_add_composer_output(plaidml_composer* composer, const char* name, plaidml_var* var);
// Adds a dependency to the composed function. Any updates induced by the function
// application will be updates of the newly generated function (in addition to any
// explicit updates, which will superseed them).
PLAIDML_API bool plaidml_add_composer_dependency(plaidml_composer* composer, plaidml_applier* must_run_before);
// Adds a tensor update to a composed function. This allows the composed
// function to have externally visible side effects when run: the source tensor
// (which should be a placeholder bound to an output of some function) will be
// assigned to the destination tensor (either a placeholder or a tensor var)
// each time the composed function is run.
PLAIDML_API bool plaidml_add_composer_update(plaidml_composer* composer, plaidml_var* dest_tensor,
plaidml_var* src_tensor);
// Builds the function described by the composer. This should be called at
// most once per composer; after this call, the only valid operation on the
// composer is plaidml_free_composer().
PLAIDML_API plaidml_function* plaidml_build_composed_function(plaidml_composer* composer);
// Frees a composer. After this call, the composer should not be used for any
// subsequent calls. Freeing a NULL composer is a no-op.
PLAIDML_API void plaidml_free_composer(plaidml_composer* composer);
// Allocates an applier describing the application of the given function to some
// number of inputs, or returns NULL if the library cannot allocate sufficient memory.
PLAIDML_API plaidml_applier* plaidml_alloc_applier(plaidml_function* function);
// Adds a dependency to the applied function. This is used to sequence tensor
// updates: if a sub-function of the applied function uses a mutable tensor as
// an input, the value it will observe for that tensor will be the value after
// the indicated function has run (presumably updating the tensor). In addition
// the new function application will carry the updates forward.
PLAIDML_API bool plaidml_apply_add_dependency(plaidml_applier* applier, plaidml_applier* must_run_before);
// Adds a named input to a function application. Note that the input variable
// is not consumed; the caller remains responsible for calling plaidml_free_var()
// when the supplied var is no longer needed.
PLAIDML_API bool plaidml_apply_add_input(plaidml_applier* applier, const char* name, plaidml_var* var);
// Allocates a var corresponding to the output of a function application. The
// caller is responsible for calling plaidml_free_var() on the result when the
// variable is no longer needed.
//
// At the time when the output is allocated, all inputs to the function
// application must already be added (either as concrete values or as
// placeholders).
PLAIDML_API plaidml_var* plaidml_apply_alloc_output(plaidml_applier* applier, const char* name);
// Frees an applier. After this call, the applier should not be used for any
// subsequent calls. Freeing a NULL applier is a no-op.
PLAIDML_API void plaidml_free_applier(plaidml_applier* applier);
// A PlaidML invoker provides a mechanism for scheduling runs of a
// PlaidML function.
//
// The function need not be completely bound when supplied to the
// invoker; the invoker may be mutated to set the function inputs and
// outputs. The input and output bindings must be fully specified,
// and must be dimensionally consistent with each other, at the time
// the invoker is used to invoke the supplied function.
//
// Invokers are not threadsafe, and they are stateful; callers are
// advised to synchronize concurrent access from the time the
// invoker's inputs are set through to when the function has completed
// running.
#ifdef __cplusplus
struct plaidml_invoker;
#else
typedef struct plaidml_invoker plaidml_invoker;
#endif // __cplusplus
// Allocates an invoker for the supplied function, or returns NULL if
// the library cannot allocate sufficient memory, or if the supplied
// context or function is NULL.
PLAIDML_API plaidml_invoker* plaidml_alloc_invoker(vai_ctx* ctx, plaidml_function* function);
// Frees an invoker. After this call, the invoker should not be used for any
// subsequent calls. Freeing a NULL invoker is a no-op.
PLAIDML_API void plaidml_free_invoker(plaidml_invoker* invoker);
// Sets a named input for an invocation. The variable must be NULL or
// a concrete object; placeholders are not permitted. Note that the
// input variable is not consumed; the caller remains responsible for
// calling plaidml_free_var() when the supplied var is no longer
// needed.
PLAIDML_API bool plaidml_set_invoker_input(plaidml_invoker* invoker, const char* name, plaidml_var* var);
// Allocates a shape corresponding to the output of an invocation.
// The caller is responsible for calling plaidml_free_shape() on the
// result when the shape is no longer needed.
//
// At the time when the shape is allocated, all inputs to the
// invocation must already be set to concrete values that are
// consistent in size.
PLAIDML_API plaidml_shape* plaidml_alloc_invoker_output_shape(plaidml_invoker* invoker, const char* name);
// Sets a named output for an invocation. The variable must be NULL
// or a concrete tensor; placeholders are not permitted. Note that
// the output variable is not consumed; the caller remains responsible
// for calling plaidml_free_var() when the supplied var is no longer
// needed.
PLAIDML_API bool plaidml_set_invoker_output(plaidml_invoker* invoker, const char* name, plaidml_var* var);
// PlaidML Stripe file formats.
typedef enum {
PLAIDML_FILE_FORMAT_TILE = 1,
PLAIDML_FILE_FORMAT_STRIPE_HUMAN = 2,
PLAIDML_FILE_FORMAT_STRIPE_PROTOTXT = 3,
PLAIDML_FILE_FORMAT_STRIPE_BINARY = 4,
} plaidml_file_format;
// Mark a functions inputs as 'const', that is, subject to constant folding
// and other operations that assume they will no longer be changed
PLAIDML_API bool plaidml_set_invoker_const(plaidml_invoker* invoker);
// Serializes an invoker to a file. All inputs to the invoker must
// already be set to concrete values that are consistent in size.
PLAIDML_API bool plaidml_save_invoker(plaidml_invoker* invoker, const char* filename, plaidml_file_format format);
// A PlaidML invocation describes one particular run of a function.
#ifdef __cplusplus
struct plaidml_invocation;
#else
typedef struct plaidml_invocation plaidml_invocation;
#endif // __cplusplus
// Schedules a run of an invoker's function with the invoker's current
// input and output bindings.
//
// The invocation must be fully specified: all function inputs and
// outputs must have corresponding inputs and outputs set in the
// invoker. Furthermore, all inputs and outputs must be consistently
// sized relative to each other and the function.
//
// All buffers used to back tensors that will be updated by the
// invocation should already be unmapped. When this call returns,
// those buffers logically contain the updated values, and may be
// remapped; remapping requests will complete once the underlying
// invocation is complete.
//
// Note that this call may return before the computation described by
// the function has actually completed; the computation is scheduled,
// not complete. Errors that occur asynchronously will be reported
// when the buffers updated by running the function are remapped.
//
// Once this call returns, the invoker's inputs and outputs may be set
// by the caller, and the invoker may be used for another run of the
// invoker's function, even if the first run has not yet completed.
PLAIDML_API plaidml_invocation* plaidml_schedule_invocation(vai_ctx* ctx, plaidml_invoker* invoker);
// Frees an invocation. After this call, the invocation should not be
// used for any subsequent calls. Freeing a NULL invocation is a no-op.
PLAIDML_API void plaidml_free_invocation(plaidml_invocation* invocation);
// A PlaidML gradient computes gradient data for a given scalar.
#ifdef __cplusplus
struct plaidml_gradient;
#else
typedef struct plaidml_gradient plaidml_gradient;
#endif // __cplusplus
// Allocate and returns a gradient computer for a given scalar or NULL if error
PLAIDML_API plaidml_gradient* plaidml_alloc_gradient(plaidml_var* var);
// Frees a gradient computer. After this call, the context should not be used for
// any subsequent calls. Freeing a NULL context is a no-op.
PLAIDML_API void plaidml_free_gradient(plaidml_gradient* grad);
// Determines the gradient of with respect to some value
PLAIDML_API plaidml_var* plaidml_compute_grad_wrt(plaidml_gradient* grad, plaidml_var* wrt);
#ifdef __cplusplus
} // extern "C"
namespace std {
template <>
struct default_delete<::plaidml_device> {
void operator()(::plaidml_device* device) const noexcept { ::plaidml_close_device(device); }
};
template <>
struct default_delete<::plaidml_device_enumerator> {
void operator()(::plaidml_device_enumerator* enumerator) const noexcept {
::plaidml_free_device_enumerator(enumerator);
}
};
template <>
struct default_delete<::plaidml_buffer> {
void operator()(::plaidml_buffer* buffer) const noexcept { ::plaidml_free_buffer(buffer); }
};
template <>
struct default_delete<::plaidml_mapping> {
void operator()(::plaidml_mapping* mapping) const noexcept { ::plaidml_free_mapping(mapping); }
};
template <>
struct default_delete<::plaidml_shape> {
void operator()(::plaidml_shape* shape) const noexcept { ::plaidml_free_shape(shape); }
};
template <>
struct default_delete<::plaidml_function> {
void operator()(::plaidml_function* function) const noexcept { ::plaidml_free_function(function); }
};
template <>
struct default_delete<::plaidml_var> {
void operator()(::plaidml_var* var) const noexcept { ::plaidml_free_var(var); }
};
template <>
struct default_delete<::plaidml_composer> {
void operator()(::plaidml_composer* composer) const noexcept { ::plaidml_free_composer(composer); }
};
template <>
struct default_delete<::plaidml_applier> {
void operator()(::plaidml_applier* applier) const noexcept { ::plaidml_free_applier(applier); }
};
template <>
struct default_delete<::plaidml_invoker> {
void operator()(::plaidml_invoker* invoker) const noexcept { ::plaidml_free_invoker(invoker); }
};
template <>
struct default_delete<::plaidml_invocation> {
void operator()(::plaidml_invocation* invocation) const noexcept { ::plaidml_free_invocation(invocation); }
};
template <>
struct default_delete<::plaidml_gradient> {
void operator()(::plaidml_gradient* gradient) const noexcept { ::plaidml_free_gradient(gradient); }
};
} // namespace std
#endif // __cplusplus