threshold part of the Spec, allowing to write tests (test_special_specs_*) that test non-default values. PiperOrigin-RevId: 254464005
167 lines
5.2 KiB
C++
167 lines
5.2 KiB
C++
/* Copyright 2019 Google LLC. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
==============================================================================*/
|
|
|
|
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_RUY_MATRIX_H_
|
|
#define TENSORFLOW_LITE_EXPERIMENTAL_RUY_MATRIX_H_
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <type_traits>
|
|
|
|
#include "tensorflow/lite/experimental/ruy/check_macros.h"
|
|
|
|
namespace ruy {
|
|
|
|
// Layout storage order. Here and elsewhere, 'col' is short for 'column'.
|
|
// 'column-major' means that each column is contiguous in memory.
|
|
enum class Order : std::uint8_t { kColMajor, kRowMajor };
|
|
|
|
// Describes the shape and storage layout of a matrix.
|
|
struct Layout final {
|
|
std::int32_t rows = 0;
|
|
std::int32_t cols = 0;
|
|
// Stride is the offset between two adjacent matrix elements
|
|
// in the non-contiguous direction.
|
|
std::int32_t stride = 0;
|
|
Order order = Order::kColMajor;
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
// Thin wrapper around a pointer that tracks its constness dynamically.
|
|
//
|
|
// This is our take on the C++ problem of enforcing constness of data
|
|
// wrapped in a containers class: it's not worth the hassle of trying to
|
|
// make it fully work at compile-time.
|
|
// Instead, we only enforce constness at runtime, and to make it
|
|
// zero-overhead, we only enforce it in debug builds.
|
|
template <typename T>
|
|
class ConstCheckingPtr final {
|
|
public:
|
|
using element_type = T;
|
|
|
|
// Convenience methods. Most `set` calls go through these.
|
|
ConstCheckingPtr& operator=(T* ptr) {
|
|
set(ptr);
|
|
return *this;
|
|
}
|
|
ConstCheckingPtr& operator=(const T* ptr) {
|
|
set(ptr);
|
|
return *this;
|
|
}
|
|
ConstCheckingPtr& operator=(std::nullptr_t) {
|
|
set(static_cast<T*>(nullptr));
|
|
return *this;
|
|
}
|
|
|
|
// Core accessors. These encapsulate the main logic:
|
|
// - for `set`, the constness of the argument determines whether internal
|
|
// pointer should be tracked as const/mutable.
|
|
// - for `get`, the constness of `this` determines whether the call
|
|
// counts as a const or mutable use of the internal pointer.
|
|
void set(T* ptr) {
|
|
ptr_ = ptr;
|
|
set_mutable(true);
|
|
}
|
|
void set(const T* ptr) {
|
|
ptr_ = ptr;
|
|
set_mutable(false);
|
|
}
|
|
T* get() /* NOT const */ {
|
|
assert_mutable();
|
|
return const_cast<T*>(ptr_);
|
|
}
|
|
const T* get() const { return ptr_; }
|
|
|
|
private:
|
|
static_assert(!std::is_const<T>::value, "");
|
|
const T* ptr_ = nullptr;
|
|
#ifndef NDEBUG
|
|
bool is_mutable_ = true;
|
|
void set_mutable(bool val) { is_mutable_ = val; }
|
|
void assert_mutable() { RUY_DCHECK(is_mutable_); }
|
|
#else
|
|
void set_mutable(bool) {}
|
|
void assert_mutable() {}
|
|
#endif
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
// A Matrix is really what Eigen and gemmlowp would have called a 'matrix map':
|
|
// it merely wraps existing data as a matrix. It doesn't own any buffer.
|
|
// Scalar may be any floating-point or integral type. When integral, it may be
|
|
// signed or unsigned.
|
|
template <typename Scalar>
|
|
struct Matrix final {
|
|
void operator=(const Matrix& other) {
|
|
data = other.data;
|
|
layout = other.layout;
|
|
zero_point = other.zero_point;
|
|
}
|
|
|
|
// The underlying buffer wrapped by this matrix.
|
|
detail::ConstCheckingPtr<Scalar> data;
|
|
// The shape and data layout of this matrix.
|
|
Layout layout;
|
|
// The zero_point, i.e. which Scalar value is to be interpreted as zero.
|
|
// When Scalar is floating-point, this must be 0.
|
|
Scalar zero_point = 0;
|
|
};
|
|
|
|
inline void MakeSimpleLayout(int rows, int cols, Order order, Layout* layout) {
|
|
layout->rows = rows;
|
|
layout->cols = cols;
|
|
layout->order = order;
|
|
layout->stride = order == Order::kColMajor ? rows : cols;
|
|
}
|
|
|
|
// Opaque data structure representing a pre-packed matrix, as obtained from
|
|
// Ruy's advanced API.
|
|
struct PrepackedMatrix {
|
|
void* data = nullptr;
|
|
std::size_t data_size = 0;
|
|
void* sums = nullptr;
|
|
std::size_t sums_size = 0;
|
|
};
|
|
|
|
template <typename StreamType, typename Scalar>
|
|
StreamType& operator<<(StreamType& stream, const Matrix<Scalar>& mat) {
|
|
for (int row = 0; row < mat.layout.rows; row++) {
|
|
for (int col = 0; col < mat.layout.cols; col++) {
|
|
stream << static_cast<double>(Element(mat, row, col)) << " ";
|
|
}
|
|
stream << "\n";
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
// Compile-time version of KernelLayout, used to declare kernel layouts in a
|
|
// way that can be consumed by compile-time logic.
|
|
// See how partial specializations of Kernel use it to declare their layouts.
|
|
// The only reason why this is currently part of the public API is to
|
|
// allow testing various layouts for the Path::kStandardCpp kernel, as a
|
|
// testing-only feature. See Spec::StandardCppKernelLhsLayout.
|
|
template <Order tOrder, int tRows, int tCols>
|
|
struct FixedKernelLayout {
|
|
static constexpr Order kOrder = tOrder;
|
|
static constexpr int kRows = tRows;
|
|
static constexpr int kCols = tCols;
|
|
};
|
|
|
|
} // namespace ruy
|
|
|
|
#endif // TENSORFLOW_LITE_EXPERIMENTAL_RUY_MATRIX_H_
|