mirror of https://github.com/google/gemma.cpp.git
Compare commits
1 Commits
28ca001d5e
...
c6558c64b6
| Author | SHA1 | Date |
|---|---|---|
|
|
c6558c64b6 |
|
|
@ -58,7 +58,6 @@ struct CompressTraits {};
|
|||
template <>
|
||||
struct CompressTraits<float> {
|
||||
using MatT = float;
|
||||
static constexpr bool kSupportsEvenOdd = false;
|
||||
|
||||
template <class DF, HWY_IF_F32_D(DF)>
|
||||
static HWY_INLINE void Compress(DF df, const float* HWY_RESTRICT in,
|
||||
|
|
@ -112,7 +111,6 @@ struct CompressTraits<float> {
|
|||
template <>
|
||||
struct CompressTraits<hwy::bfloat16_t> {
|
||||
using MatT = hwy::bfloat16_t;
|
||||
static constexpr bool kSupportsEvenOdd = true;
|
||||
|
||||
template <class DF, HWY_IF_F32_D(DF)>
|
||||
static HWY_INLINE void Compress(DF df, const float* HWY_RESTRICT in,
|
||||
|
|
@ -221,60 +219,11 @@ struct CompressTraits<hwy::bfloat16_t> {
|
|||
// bf16*bf16.
|
||||
return hn::Dot::Compute<kAssumptions>(d_vec, vec_aligned, in + in_ofs, num);
|
||||
}
|
||||
|
||||
// Computes the dot product of an even-odd deinterleaved, f32 `vec_aligned`
|
||||
// and a column- major matrix `in`. `vec_aligned` should be aligned and
|
||||
// alternate even-indexed `hn::Lanes(df32)` elements followed by odd-indexed
|
||||
// `hn::Lanes(df32)` elements.
|
||||
template <class DF, HWY_IF_F32_D(DF)>
|
||||
static HWY_INLINE float DotEO(
|
||||
const DF df32, const hwy::bfloat16_t* HWY_RESTRICT in, size_t in_ofs,
|
||||
const float* HWY_RESTRICT vec_aligned, size_t num) {
|
||||
HWY_DASSERT(num >= (hn::Lanes(df32) * 2) && (num % (hn::Lanes(df32) * 2)) == 0);
|
||||
HWY_DASSERT((in_ofs % (hn::Lanes(df32) * 2)) == 0);
|
||||
HWY_DASSERT(hn::IsAligned(df32, vec_aligned));
|
||||
|
||||
const hn::Repartition<hwy::bfloat16_t, DF> dbf16;
|
||||
using VF32 = decltype(Zero(df32));
|
||||
const size_t N = Lanes(dbf16);
|
||||
|
||||
VF32 sum0 = Zero(df32);
|
||||
VF32 sum1 = Zero(df32);
|
||||
VF32 sum2 = Zero(df32);
|
||||
VF32 sum3 = Zero(df32);
|
||||
|
||||
const hn::RebindToUnsigned<decltype(df32)> du32;
|
||||
using VU32 = hn::VFromD<decltype(du32)>;
|
||||
const VU32 odd = Set(du32, 0xFFFF0000u);
|
||||
|
||||
VF32 be0, bo0, be1, bo1;
|
||||
for (size_t i = 0; i < num; /* i += 2 * N */) {
|
||||
const auto interleaved0 = hn::LoadU(dbf16, in + in_ofs + i);
|
||||
const VF32 ae0 = Load(df32, vec_aligned + i);
|
||||
const VF32 ao0 = Load(df32, vec_aligned + i + (N / 2));
|
||||
sum0 = hn::MulAdd(ae0, hn::PromoteEvenTo(df32, interleaved0), sum0);
|
||||
sum1 = hn::MulAdd(ao0, hn::PromoteOddTo(df32, interleaved0), sum1);
|
||||
i += N;
|
||||
|
||||
const auto interleaved1 = hn::LoadU(dbf16, in + in_ofs + i);
|
||||
const VF32 ae1 = Load(df32, vec_aligned + i);
|
||||
const VF32 ao1 = Load(df32, vec_aligned + i + (N / 2));
|
||||
sum2 = hn::MulAdd(ae1, hn::PromoteEvenTo(df32, interleaved1), sum2);
|
||||
sum3 = hn::MulAdd(ao1, hn::PromoteOddTo(df32, interleaved1), sum3);
|
||||
i += N;
|
||||
}
|
||||
|
||||
sum0 = Add(sum0, sum1);
|
||||
sum2 = Add(sum2, sum3);
|
||||
sum0 = Add(sum0, sum2);
|
||||
return ReduceSum(df32, sum0);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CompressTraits<SfpStream> {
|
||||
using MatT = SfpStream;
|
||||
static constexpr bool kSupportsEvenOdd = false;
|
||||
|
||||
template <class DF, HWY_IF_F32_D(DF)>
|
||||
static HWY_INLINE void Compress(DF df, const float* in, size_t num,
|
||||
|
|
@ -324,7 +273,6 @@ struct CompressTraits<SfpStream> {
|
|||
template <>
|
||||
struct CompressTraits<NuqStream> {
|
||||
using MatT = NuqStream;
|
||||
static constexpr bool kSupportsEvenOdd = false;
|
||||
|
||||
template <class DF, HWY_IF_F32_D(DF)>
|
||||
static HWY_INLINE void Compress(DF df, const float* in, size_t num,
|
||||
|
|
@ -477,22 +425,16 @@ HWY_INLINE float Dot(DF df, const ArrayT& compressed, size_t compressed_ofs,
|
|||
}
|
||||
|
||||
// Returns dot product with `vec_aligned` of length `num`.
|
||||
template <bool kVecEO, class DF, typename MatT, size_t kCapacity, typename VecT>
|
||||
template <class DF, typename MatT, size_t kCapacity, typename VecT>
|
||||
HWY_INLINE float Dot(DF df, const CompressedArray<MatT, kCapacity>& compressed,
|
||||
size_t compressed_ofs, const VecT* vec_aligned,
|
||||
size_t num) {
|
||||
HWY_DASSERT(compressed_ofs + num <= compressed.size());
|
||||
HWY_DASSERT(hn::IsAligned(df, vec_aligned));
|
||||
using Traits = CompressTraits<MatT>;
|
||||
float dot_result;
|
||||
if constexpr (kVecEO) {
|
||||
dot_result = Traits::DotEO(df, compressed.data(), compressed_ofs,
|
||||
vec_aligned, num);
|
||||
} else {
|
||||
dot_result = Traits::Dot(df, compressed.size(), compressed.data(),
|
||||
compressed_ofs, vec_aligned, num);
|
||||
}
|
||||
return compressed.scale() * dot_result;
|
||||
return (compressed.scale() * Traits::Dot(df, compressed.size(),
|
||||
compressed.data(), compressed_ofs,
|
||||
vec_aligned, num));
|
||||
}
|
||||
|
||||
// Callback used by ForeachTensor.
|
||||
|
|
|
|||
|
|
@ -402,11 +402,10 @@ struct Activations {
|
|||
static constexpr size_t kCacheLayerSize = kKVHeads * kQKVDim * 2;
|
||||
static constexpr size_t kCachePosSize =
|
||||
TConfig::kGemmaLayers * kCacheLayerSize;
|
||||
static constexpr size_t kQDim = kHeads == kKVHeads ? kQKVDim * 3 : kQKVDim;
|
||||
|
||||
std::array<float, kBatchSize * kModelDim> x; // input
|
||||
std::array<float, kBatchSize * kModelDim> pre_att_rms_out;
|
||||
std::array<float, kBatchSize * kHeads * kQDim> q; // query vector
|
||||
std::array<float, kBatchSize * kHeads * kQKVDim> q; // query vector
|
||||
std::array<float, kBatchSize * kHeads * TConfig::kSeqLen>
|
||||
att; // attention vector
|
||||
std::array<float, kBatchSize * kHeads * kQKVDim> att_out; // attention output
|
||||
|
|
@ -711,9 +710,10 @@ HWY_NOINLINE void Attention(size_t batch_start, size_t batch_idx, size_t layer,
|
|||
|
||||
float* x = activations.pre_att_rms_out.data() + batch_idx * kModelDim;
|
||||
|
||||
auto Attn = [&](float* q, uint64_t head, size_t head_offset,
|
||||
size_t thread) HWY_ATTR {
|
||||
auto Attn = [&](uint64_t head, size_t head_offset, size_t thread) HWY_ATTR {
|
||||
// Calculate scores
|
||||
float* HWY_RESTRICT q =
|
||||
activations.q.data() + head * kQKVDim + batch_idx * kHeads * kQKVDim;
|
||||
float* HWY_RESTRICT head_att = activations.att.data() +
|
||||
head * TConfig::kSeqLen +
|
||||
batch_idx * kHeads * kQKVDim;
|
||||
|
|
@ -745,23 +745,34 @@ HWY_NOINLINE void Attention(size_t batch_start, size_t batch_idx, size_t layer,
|
|||
|
||||
if constexpr (kHeads == kKVHeads) {
|
||||
// Multi-Head Attention
|
||||
static_assert(TConfig::kInterleaveQKV);
|
||||
|
||||
float* HWY_RESTRICT qkv =
|
||||
activations.q.data() + batch_idx * kHeads * kQKVDim * 3;
|
||||
MatVec<kHeads * kQKVDim * 3, kModelDim>(
|
||||
layer_weights->qkv_einsum_w, 0, x, activations.even_odd.data(), qkv,
|
||||
pool);
|
||||
|
||||
pool.Run(0, kHeads, [&](const uint64_t head, size_t thread) HWY_ATTR {
|
||||
float* HWY_RESTRICT q = qkv + head * kQKVDim * 3;
|
||||
// linear projections to QKV
|
||||
const size_t head_offset = TConfig::kInterleaveQKV
|
||||
? 3 * kQKVDim * kModelDim
|
||||
: kQKVDim * kModelDim;
|
||||
const size_t mat_offset =
|
||||
TConfig::kInterleaveQKV ? kQKVDim * kModelDim : kModelDim * kModelDim;
|
||||
const size_t q_offset = head * head_offset + 0 * mat_offset;
|
||||
const size_t k_offset = head * head_offset + 1 * mat_offset;
|
||||
const size_t v_offset = head * head_offset + 2 * mat_offset;
|
||||
|
||||
// ProjQ
|
||||
float* HWY_RESTRICT q =
|
||||
activations.q.data() + head * kQKVDim + batch_idx * kHeads * kQKVDim;
|
||||
MatVecLoop<kQKVDim, kModelDim>(
|
||||
layer_weights->qkv_einsum_w, q_offset + 0 * kQKVDim * kModelDim, x,
|
||||
activations.even_odd.data() + thread * kModelDim, q);
|
||||
|
||||
// ProjKV
|
||||
const size_t kv_offset = cache_pos * kCachePosSize +
|
||||
layer * kCacheLayerSize + head * kQKVDim * 2;
|
||||
float* HWY_RESTRICT kv = kv_cache.kv_cache.get() + kv_offset;
|
||||
float* HWY_RESTRICT k = kv_cache.kv_cache.get() + kv_offset;
|
||||
float* HWY_RESTRICT v = k + kQKVDim;
|
||||
TwoOfsMatVecLoop<kQKVDim, kModelDim>(layer_weights->qkv_einsum_w,
|
||||
k_offset, v_offset, x, k, v);
|
||||
Rope(k, TConfig::kUseHalfRope ? kQKVDim / 2 : kQKVDim, pos);
|
||||
|
||||
memcpy(kv, q + kQKVDim, 2 * kQKVDim * sizeof(float));
|
||||
Rope(kv, TConfig::kUseHalfRope ? kQKVDim / 2 : kQKVDim, pos);
|
||||
Attn(q, head, head * kQKVDim * 2, thread);
|
||||
Attn(head, head * kQKVDim * 2, thread);
|
||||
});
|
||||
} else {
|
||||
// Multi-Query Attention
|
||||
|
|
@ -779,7 +790,7 @@ HWY_NOINLINE void Attention(size_t batch_start, size_t batch_idx, size_t layer,
|
|||
Rope(kv, TConfig::kUseHalfRope ? kQKVDim / 2 : kQKVDim, pos);
|
||||
|
||||
pool.Run(0, kHeads, [&](const uint64_t head, size_t thread) HWY_ATTR {
|
||||
Attn(q + head * kQKVDim, head, 0, thread);
|
||||
Attn(head, 0, thread);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
193
gemma/ops.h
193
gemma/ops.h
|
|
@ -25,7 +25,6 @@
|
|||
#include <random>
|
||||
#include <type_traits> // std::enable_if_t
|
||||
|
||||
#include "compression/compress.h" // CompressedArray
|
||||
#include "hwy/base.h"
|
||||
#include "hwy/contrib/thread_pool/thread_pool.h"
|
||||
#include "hwy/profiler.h"
|
||||
|
|
@ -108,37 +107,6 @@ HWY_INLINE void MatMul(const float* HWY_RESTRICT a, const float* HWY_RESTRICT b,
|
|||
}
|
||||
}
|
||||
|
||||
HWY_INLINE void ToEvenOddF32(const hwy::bfloat16_t* HWY_RESTRICT vec_aligned,
|
||||
const size_t size, float* HWY_RESTRICT out) {
|
||||
const hn::ScalableTag<float> df;
|
||||
const hn::Repartition<hwy::bfloat16_t, decltype(df)> dbf16;
|
||||
|
||||
HWY_DASSERT(size % hn::Lanes(dbf16) == 0);
|
||||
HWY_DASSERT(hn::IsAligned(df, vec_aligned));
|
||||
|
||||
for (size_t i = 0; i < size; i += hn::Lanes(dbf16)) {
|
||||
const auto interleaved = hn::LoadU(dbf16, vec_aligned + i);
|
||||
hn::Store(hn::PromoteEvenTo(df, interleaved), df, out + i);
|
||||
hn::Store(hn::PromoteOddTo(df, interleaved), df, out + i + hn::Lanes(df));
|
||||
}
|
||||
}
|
||||
|
||||
HWY_INLINE void ToEvenOddF32(const float* HWY_RESTRICT vec_aligned,
|
||||
const size_t size, float* HWY_RESTRICT out) {
|
||||
const hn::ScalableTag<float> df;
|
||||
using VF = hn::Vec<decltype(df)>;
|
||||
|
||||
HWY_DASSERT(size % (hn::Lanes(df) * 2) == 0);
|
||||
HWY_DASSERT(hn::IsAligned(df, vec_aligned));
|
||||
|
||||
VF vec0, vec1;
|
||||
for (size_t i = 0; i < size; i += hn::Lanes(df) * 2) {
|
||||
hn::LoadInterleaved2(df, vec_aligned + i, vec0, vec1);
|
||||
hn::Store(vec0, df, out + i);
|
||||
hn::Store(vec1, df, out + i + hn::Lanes(df));
|
||||
}
|
||||
}
|
||||
|
||||
// Simple version without tiling nor threading.
|
||||
// even_odd is precomputed for the current thread.
|
||||
template <bool kAdd, size_t kOuter, size_t kInner, typename ArrayT,
|
||||
|
|
@ -151,6 +119,12 @@ HWY_INLINE void MatVecAddLoop(const ArrayT& mat, const size_t mat_ofs,
|
|||
PROFILER_ZONE("MatVecAddLoop");
|
||||
const hn::ScalableTag<float> df;
|
||||
|
||||
// Sanity check: we can write without race conditions.
|
||||
if (HWY_IS_TSAN) {
|
||||
even_odd[0] = hwy::ConvertScalarTo<float>(vec_aligned[0]);
|
||||
even_odd[kInner - 1] = -even_odd[0];
|
||||
}
|
||||
|
||||
for (size_t idx_row = 0; idx_row < kOuter; ++idx_row) {
|
||||
const size_t row_ofs = mat_ofs + idx_row * kInner;
|
||||
if constexpr (kAdd) {
|
||||
|
|
@ -162,40 +136,14 @@ HWY_INLINE void MatVecAddLoop(const ArrayT& mat, const size_t mat_ofs,
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(HWY_NATIVE_DOT_BF16) || !HWY_NATIVE_DOT_BF16
|
||||
template <bool kAdd, size_t kOuter, size_t kInner, typename VecT, typename AddT,
|
||||
size_t kCapacity>
|
||||
HWY_INLINE void MatVecAddLoop(
|
||||
const CompressedArray<hwy::bfloat16_t, kCapacity>& mat,
|
||||
const size_t mat_ofs, const VecT* HWY_RESTRICT vec_aligned,
|
||||
const AddT* HWY_RESTRICT add, float* HWY_RESTRICT even_odd,
|
||||
float* HWY_RESTRICT out) {
|
||||
PROFILER_ZONE("MatVecAddLoop");
|
||||
constexpr bool kVecIsEvenOdd = true;
|
||||
|
||||
const hn::ScalableTag<float> df;
|
||||
ToEvenOddF32(vec_aligned, kInner, even_odd);
|
||||
for (size_t idx_row = 0; idx_row < kOuter; ++idx_row) {
|
||||
const size_t row_ofs = mat_ofs + idx_row * kInner;
|
||||
if constexpr (kAdd) {
|
||||
out[idx_row] = hwy::ConvertScalarTo<float>(add[idx_row]) +
|
||||
Dot<kVecIsEvenOdd>(df, mat, row_ofs, even_odd, kInner);
|
||||
} else {
|
||||
out[idx_row] = Dot<kVecIsEvenOdd>(df, mat, row_ofs, even_odd, kInner);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// even_odd is precomputed for the current thread.
|
||||
template <size_t kOuter, size_t kInner, typename ArrayT, typename VecT>
|
||||
HWY_INLINE void MatVecLoop(const ArrayT& mat, const size_t mat_ofs,
|
||||
const VecT* HWY_RESTRICT vec_aligned,
|
||||
float* HWY_RESTRICT even_odd,
|
||||
float* HWY_RESTRICT out) {
|
||||
MatVecAddLoop</*kAdd=*/false, kOuter, kInner>(
|
||||
mat, mat_ofs, vec_aligned, /*add=*/static_cast<VecT*>(nullptr), even_odd,
|
||||
out);
|
||||
MatVecAddLoop</*kAdd=*/false, kOuter, kInner, ArrayT, VecT, VecT>(
|
||||
mat, mat_ofs, vec_aligned, /*add=*/nullptr, even_odd, out);
|
||||
}
|
||||
|
||||
// Simple version without tiling nor threading, but two offsets/outputs.
|
||||
|
|
@ -243,23 +191,20 @@ namespace detail {
|
|||
// For each i = [0, num_rows), compute partial (length `num_cols`) dot product
|
||||
// of row i with `vec_aligned` and add into `out[i]`. The upper-left coordinate
|
||||
// of the tile is r0, c0.
|
||||
template <bool kVecEO, class DF, typename ArrayT, typename VecT>
|
||||
template <class DF, typename ArrayT, typename VecT>
|
||||
HWY_INLINE void AccumulatePartialDotProducts(
|
||||
DF df, const ArrayT& mat, size_t mat_ofs, size_t mat_stride, size_t r0,
|
||||
size_t c0, size_t num_rows, size_t num_cols,
|
||||
const VecT* HWY_RESTRICT vec_aligned, float* HWY_RESTRICT out) {
|
||||
for (size_t idx_row = 0; idx_row < num_rows; ++idx_row) {
|
||||
const size_t row_ofs = mat_ofs + (r0 + idx_row) * mat_stride;
|
||||
out[idx_row] +=
|
||||
Dot<kVecEO>(df, mat, row_ofs + c0, vec_aligned + c0, num_cols);
|
||||
out[idx_row] += Dot(df, mat, row_ofs + c0, vec_aligned + c0, num_cols);
|
||||
}
|
||||
}
|
||||
|
||||
// Same as AccumulatePartialDotProducts, but sets out[i] to the first partial
|
||||
// dot product + init (if kInit), which avoids having to zero-initialize and
|
||||
// accumulate.
|
||||
template <bool kVecEO, bool kInit, class DF, typename ArrayT, typename VecT,
|
||||
typename InitT>
|
||||
// Same as above, but sets out[i] to the first partial dot product +
|
||||
// init (if kInit), which avoids having to zero-initialize and accumulate.
|
||||
template <bool kInit, class DF, typename ArrayT, typename VecT, typename InitT>
|
||||
HWY_INLINE void SetFirstPartialDotProducts(DF df, const ArrayT& mat,
|
||||
size_t mat_ofs, size_t mat_stride,
|
||||
size_t r0, size_t c0,
|
||||
|
|
@ -270,12 +215,10 @@ HWY_INLINE void SetFirstPartialDotProducts(DF df, const ArrayT& mat,
|
|||
for (size_t idx_row = 0; idx_row < num_rows; ++idx_row) {
|
||||
const size_t row_ofs = mat_ofs + (r0 + idx_row) * mat_stride;
|
||||
if constexpr (kInit) {
|
||||
out[idx_row] =
|
||||
hwy::ConvertScalarTo<float>(init[idx_row + r0]) +
|
||||
Dot<kVecEO>(df, mat, row_ofs + c0, vec_aligned + c0, num_cols);
|
||||
out[idx_row] = hwy::ConvertScalarTo<float>(init[idx_row + r0]) +
|
||||
Dot(df, mat, row_ofs + c0, vec_aligned + c0, num_cols);
|
||||
} else {
|
||||
out[idx_row] =
|
||||
Dot<kVecEO>(df, mat, row_ofs + c0, vec_aligned + c0, num_cols);
|
||||
out[idx_row] = Dot(df, mat, row_ofs + c0, vec_aligned + c0, num_cols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -284,8 +227,7 @@ HWY_INLINE void SetFirstPartialDotProducts(DF df, const ArrayT& mat,
|
|||
// horizontal strip of the entire matrix); the result is the full dot product
|
||||
// for rows r in [r0, r0 + num_rows) + optionally the add vector, which we store
|
||||
// into in out[r - r0].
|
||||
template <bool kVecEO, bool kAdd, class DF, typename ArrayT, typename VecT,
|
||||
typename AddT>
|
||||
template <bool kAdd, class DF, typename ArrayT, typename VecT, typename AddT>
|
||||
HWY_INLINE void FullDotProductsForStrip(DF df, const ArrayT& mat,
|
||||
size_t mat_ofs, size_t mat_stride,
|
||||
size_t r0, size_t num_rows,
|
||||
|
|
@ -294,37 +236,42 @@ HWY_INLINE void FullDotProductsForStrip(DF df, const ArrayT& mat,
|
|||
float* HWY_RESTRICT out) {
|
||||
// Tall and skinny: set `out` to the single dot product.
|
||||
if (mat_stride < MaxCols()) {
|
||||
SetFirstPartialDotProducts<kVecEO, kAdd>(df, mat, mat_ofs, mat_stride, r0,
|
||||
0, num_rows, mat_stride,
|
||||
vec_aligned, add, out);
|
||||
SetFirstPartialDotProducts<kAdd>(df, mat, mat_ofs, mat_stride, r0, 0,
|
||||
num_rows, mat_stride, vec_aligned, add,
|
||||
out);
|
||||
return;
|
||||
}
|
||||
|
||||
// We have at least MaxCols, so start by setting `out` to that:
|
||||
SetFirstPartialDotProducts<kVecEO, kAdd>(df, mat, mat_ofs, mat_stride, r0, 0,
|
||||
num_rows, MaxCols(), vec_aligned,
|
||||
add, out);
|
||||
SetFirstPartialDotProducts<kAdd>(df, mat, mat_ofs, mat_stride, r0, 0,
|
||||
num_rows, MaxCols(), vec_aligned, add, out);
|
||||
// For further multiples of MaxCols, accumulate. Remainders handled below.
|
||||
size_t c0 = MaxCols();
|
||||
for (; c0 <= mat_stride - MaxCols(); c0 += MaxCols()) {
|
||||
AccumulatePartialDotProducts<kVecEO>(df, mat, mat_ofs, mat_stride, r0, c0,
|
||||
num_rows, MaxCols(), vec_aligned, out);
|
||||
AccumulatePartialDotProducts(df, mat, mat_ofs, mat_stride, r0, c0, num_rows,
|
||||
MaxCols(), vec_aligned, out);
|
||||
}
|
||||
|
||||
if (c0 < mat_stride) { // Final cols
|
||||
AccumulatePartialDotProducts<kVecEO>(df, mat, mat_ofs, mat_stride, r0, c0,
|
||||
num_rows, mat_stride - c0, vec_aligned,
|
||||
out);
|
||||
AccumulatePartialDotProducts(df, mat, mat_ofs, mat_stride, r0, c0, num_rows,
|
||||
mat_stride - c0, vec_aligned, out);
|
||||
}
|
||||
}
|
||||
|
||||
template <bool kVecIsEvenOdd, bool kAdd, size_t kOuter, size_t kInner,
|
||||
typename ArrayT, typename VecT, typename AddT>
|
||||
HWY_INLINE void MatVecAddInner(const ArrayT& mat, const size_t mat_ofs,
|
||||
const VecT* HWY_RESTRICT const vec_aligned,
|
||||
const AddT* HWY_RESTRICT const add,
|
||||
float* HWY_RESTRICT even_odd,
|
||||
float* HWY_RESTRICT out, hwy::ThreadPool& pool) {
|
||||
} // namespace detail
|
||||
|
||||
// Stores dot products of rows with `vec_aligned` + add the values from `add`
|
||||
// (if kAdd), then stores them to `out`.
|
||||
// `even_odd` has kInner elements for each thread.
|
||||
template <bool kAdd, size_t kOuter, size_t kInner, typename ArrayT,
|
||||
typename VecT, typename AddT>
|
||||
HWY_INLINE void MatVecAdd(const ArrayT& mat, const size_t mat_ofs,
|
||||
const VecT* HWY_RESTRICT const vec_aligned,
|
||||
const AddT* HWY_RESTRICT const add,
|
||||
float* HWY_RESTRICT even_odd, float* HWY_RESTRICT out,
|
||||
hwy::ThreadPool& pool) {
|
||||
PROFILER_ZONE("MatVecAdd");
|
||||
|
||||
const hn::ScalableTag<float> df;
|
||||
constexpr size_t kRowsPerStrip = RowsPerStrip<kOuter>();
|
||||
constexpr size_t kNumStrips = kOuter / kRowsPerStrip;
|
||||
|
|
@ -342,9 +289,9 @@ HWY_INLINE void MatVecAddInner(const ArrayT& mat, const size_t mat_ofs,
|
|||
pool.Run(0, kNumStrips, [&](const uint64_t strip, size_t thread) HWY_ATTR {
|
||||
PROFILER_ZONE("MatVec.lambda");
|
||||
const size_t r0 = strip * kRowsPerStrip;
|
||||
detail::FullDotProductsForStrip<kVecIsEvenOdd, kAdd>(
|
||||
df, mat, mat_ofs, kInner, r0, kRowsPerStrip, vec_aligned, add,
|
||||
out + r0);
|
||||
detail::FullDotProductsForStrip<kAdd>(df, mat, mat_ofs, kInner, r0,
|
||||
kRowsPerStrip, vec_aligned, add,
|
||||
out + r0);
|
||||
});
|
||||
|
||||
// Remaining rows
|
||||
|
|
@ -352,47 +299,18 @@ HWY_INLINE void MatVecAddInner(const ArrayT& mat, const size_t mat_ofs,
|
|||
if (r0 < kOuter) {
|
||||
PROFILER_ZONE("MatVec remainder");
|
||||
const size_t num_rows = kOuter - r0;
|
||||
detail::FullDotProductsForStrip<kVecIsEvenOdd, kAdd>(
|
||||
df, mat, mat_ofs, kInner, r0, num_rows, vec_aligned, add, out + r0);
|
||||
detail::FullDotProductsForStrip<kAdd>(df, mat, mat_ofs, kInner, r0,
|
||||
num_rows, vec_aligned, add, out + r0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Stores dot products of rows with `vec_aligned` + add the values from `add`
|
||||
// (if kAdd), then stores them to `out`.
|
||||
//
|
||||
template <bool kAdd, size_t kOuter, size_t kInner, typename ArrayT,
|
||||
typename VecT, typename AddT>
|
||||
HWY_INLINE void MatVecAdd(const ArrayT& mat, const size_t mat_ofs,
|
||||
const VecT* HWY_RESTRICT const vec_aligned,
|
||||
const AddT* HWY_RESTRICT const add,
|
||||
float* HWY_RESTRICT even_odd, float* HWY_RESTRICT out,
|
||||
hwy::ThreadPool& pool) {
|
||||
PROFILER_ZONE("MatVecAdd");
|
||||
|
||||
#if !defined(HWY_NATIVE_DOT_BF16) || !HWY_NATIVE_DOT_BF16
|
||||
if constexpr (CompressTraits<typename ArrayT::value_type>::kSupportsEvenOdd &&
|
||||
hwy::IsSameEither<VecT, float, hwy::bfloat16_t>()) {
|
||||
ToEvenOddF32(vec_aligned, kInner, even_odd);
|
||||
detail::MatVecAddInner</*kVecIsEvenOdd=*/true, kAdd, kOuter, kInner>(
|
||||
mat, mat_ofs, even_odd, add, even_odd, out, pool);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
detail::MatVecAddInner</*kVecIsEvenOdd=*/false, kAdd, kOuter, kInner>(
|
||||
mat, mat_ofs, vec_aligned, add, even_odd, out, pool);
|
||||
}
|
||||
|
||||
template <size_t kOuter, size_t kInner, typename ArrayT, typename VecT>
|
||||
HWY_INLINE void MatVec(const ArrayT& mat, const size_t mat_ofs,
|
||||
const VecT* HWY_RESTRICT const vec_aligned,
|
||||
float* HWY_RESTRICT even_odd, float* HWY_RESTRICT out,
|
||||
hwy::ThreadPool& pool) {
|
||||
MatVecAdd</*kAdd=*/false, kOuter, kInner>(mat, mat_ofs, vec_aligned,
|
||||
/*add=*/static_cast<VecT*>(nullptr),
|
||||
even_odd, out, pool);
|
||||
MatVecAdd</*kAdd=*/false, kOuter, kInner, ArrayT, VecT, VecT>(
|
||||
mat, mat_ofs, vec_aligned, /*add=*/nullptr, even_odd, out, pool);
|
||||
}
|
||||
|
||||
template <class D, HWY_IF_F32_D(D)>
|
||||
|
|
@ -514,18 +432,17 @@ HWY_NOINLINE void TwoMatVecAdd(
|
|||
const hn::ScalableTag<float> df;
|
||||
constexpr size_t kRowsPerStrip = RowsPerStrip<kOuter>();
|
||||
constexpr size_t kNumStrips = kOuter / kRowsPerStrip;
|
||||
constexpr bool kVecIsEvenOdd = false;
|
||||
|
||||
// For each entire strip.
|
||||
pool.Run(0, kNumStrips, [&](const uint64_t strip, size_t thread) HWY_ATTR {
|
||||
PROFILER_ZONE("TwoMatVec.lambda");
|
||||
const size_t r0 = strip * kRowsPerStrip;
|
||||
detail::FullDotProductsForStrip<kVecIsEvenOdd, kAdd>(
|
||||
df, mat0, mat_ofs, kInner, r0, kRowsPerStrip, vec_aligned, add0,
|
||||
out0 + r0);
|
||||
detail::FullDotProductsForStrip<kVecIsEvenOdd, kAdd>(
|
||||
df, mat1, mat_ofs, kInner, r0, kRowsPerStrip, vec_aligned, add1,
|
||||
out1 + r0);
|
||||
detail::FullDotProductsForStrip<kAdd>(df, mat0, mat_ofs, kInner, r0,
|
||||
kRowsPerStrip, vec_aligned, add0,
|
||||
out0 + r0);
|
||||
detail::FullDotProductsForStrip<kAdd>(df, mat1, mat_ofs, kInner, r0,
|
||||
kRowsPerStrip, vec_aligned, add1,
|
||||
out1 + r0);
|
||||
});
|
||||
|
||||
// Remaining rows
|
||||
|
|
@ -533,9 +450,9 @@ HWY_NOINLINE void TwoMatVecAdd(
|
|||
if (r0 < kOuter) {
|
||||
PROFILER_ZONE("TwoMatVec remainder");
|
||||
const size_t num_rows = kOuter - r0;
|
||||
detail::FullDotProductsForStrip<kVecIsEvenOdd, kAdd>(
|
||||
detail::FullDotProductsForStrip<kAdd>(
|
||||
df, mat0, mat_ofs, kInner, r0, num_rows, vec_aligned, add0, out0 + r0);
|
||||
detail::FullDotProductsForStrip<kVecIsEvenOdd, kAdd>(
|
||||
detail::FullDotProductsForStrip<kAdd>(
|
||||
df, mat1, mat_ofs, kInner, r0, num_rows, vec_aligned, add1, out1 + r0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,6 +522,24 @@ void TestMatVecAdd() {
|
|||
AssertClose<kOuter>(actual_out, expected_out);
|
||||
}
|
||||
|
||||
void TestMatVecAddLoop() {
|
||||
constexpr size_t kOuter = 128 * 3;
|
||||
constexpr size_t kInner = 128 * 5;
|
||||
CompressedArray<float, kOuter * kInner> mat = GenerateMat<kOuter, kInner>(0);
|
||||
hwy::AlignedFreeUniquePtr<float[]> vec = GenerateVec<kInner>(0);
|
||||
hwy::AlignedFreeUniquePtr<float[]> add = GenerateVec<kOuter>(0);
|
||||
hwy::AlignedFreeUniquePtr<float[]> even_odd =
|
||||
hwy::AllocateAligned<float>(kInner);
|
||||
hwy::AlignedFreeUniquePtr<float[]> expected_out =
|
||||
SimpleMatVecAdd<kOuter, kInner>(mat, vec, add);
|
||||
hwy::AlignedFreeUniquePtr<float[]> actual_out =
|
||||
hwy::AllocateAligned<float>(kOuter);
|
||||
HWY_ASSERT(vec && add && even_odd && expected_out && actual_out);
|
||||
MatVecAddLoop<true, kOuter, kInner>(mat, 0, vec.get(), add.get(),
|
||||
even_odd.get(), actual_out.get());
|
||||
AssertClose<kOuter>(actual_out, expected_out);
|
||||
}
|
||||
|
||||
void TestTwoMatVecAdd() {
|
||||
hwy::ThreadPool pool(0);
|
||||
constexpr size_t kOuter = 128 * 3;
|
||||
|
|
@ -606,6 +624,7 @@ HWY_EXPORT_AND_TEST_P(OpsTest, TestAllSoftmax);
|
|||
HWY_EXPORT_AND_TEST_P(OpsTest, TestAllCreateDistribution);
|
||||
HWY_EXPORT_AND_TEST_P(OpsTest, TestMatMul);
|
||||
HWY_EXPORT_AND_TEST_P(OpsTest, TestMatVecAdd);
|
||||
HWY_EXPORT_AND_TEST_P(OpsTest, TestMatVecAddLoop);
|
||||
HWY_EXPORT_AND_TEST_P(OpsTest, TestTwoMatVecAdd);
|
||||
HWY_EXPORT_AND_TEST_P(OpsTest, TestTwoOfsMatVecAddLoop);
|
||||
HWY_EXPORT_AND_TEST_P(OpsTest, TestSigmoid);
|
||||
|
|
|
|||
Loading…
Reference in New Issue