Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
552d6857cb | ||
|
|
a37d46e917 | ||
|
|
4acdb072b6 | ||
|
|
9dbb184537 | ||
|
|
d67762a1c9 |
@@ -1,214 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
|
||||
// Abstract array data type
|
||||
|
||||
#include "array.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
#include <fstream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Print(std::ostream &os, int width) const
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
os << data[i];
|
||||
if ( !((i+1) % width) || i+1 == size )
|
||||
{
|
||||
os << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
os << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Save(std::ostream &os, int fmt) const
|
||||
{
|
||||
if (fmt == 0)
|
||||
{
|
||||
os << size << '\n';
|
||||
}
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
os << operator[](i) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Load(std::istream &in, int fmt)
|
||||
{
|
||||
if (fmt == 0)
|
||||
{
|
||||
int new_size;
|
||||
in >> new_size;
|
||||
SetSize(new_size);
|
||||
}
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
in >> operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Array<T>::Max() const
|
||||
{
|
||||
MFEM_ASSERT(size > 0, "Array is empty with size " << size);
|
||||
|
||||
T max = operator[](0);
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (max < operator[](i))
|
||||
{
|
||||
max = operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T Array<T>::Min() const
|
||||
{
|
||||
MFEM_ASSERT(size > 0, "Array is empty with size " << size);
|
||||
|
||||
T min = operator[](0);
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (operator[](i) < min)
|
||||
{
|
||||
min = operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
// Partial Sum
|
||||
template <class T>
|
||||
void Array<T>::PartialSum()
|
||||
{
|
||||
T sum = static_cast<T>(0);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
sum+=operator[](i);
|
||||
operator[](i) = sum;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Abs()
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "Use with arithmetic types!");
|
||||
const bool useDevice = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(useDevice);
|
||||
mfem::forall_switch(useDevice, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
y[i] = std::abs(y[i]);
|
||||
});
|
||||
}
|
||||
|
||||
// Sum
|
||||
template <class T>
|
||||
T Array<T>::Sum() const
|
||||
{
|
||||
T sum = static_cast<T>(0);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
sum+=operator[](i);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int Array<T>::IsSorted() const
|
||||
{
|
||||
T val_prev = operator[](0), val;
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
val=operator[](i);
|
||||
if (val < val_prev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
val_prev = val;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Array<T>::IsConstant() const
|
||||
{
|
||||
if (size < 2) { return true; }
|
||||
const T v0 = data[0];
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (data[i] != v0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array2D<T>::Load(const char *filename, int fmt)
|
||||
{
|
||||
std::ifstream in;
|
||||
in.open(filename, std::ifstream::in);
|
||||
MFEM_VERIFY(in.is_open(), "File " << filename << " does not exist.");
|
||||
Load(in, fmt);
|
||||
in.close();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array2D<T>::Print(std::ostream &os, int width_)
|
||||
{
|
||||
int height = this->NumRows();
|
||||
int width = this->NumCols();
|
||||
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
os << "[row " << i << "]\n";
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
os << (*this)(i,j);
|
||||
if ( (j+1) == width_ || (j+1) % width_ == 0 )
|
||||
{
|
||||
os << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
os << ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template class Array<char>;
|
||||
template class Array<int>;
|
||||
template class Array<long long>;
|
||||
template class Array<real_t>;
|
||||
template class Array2D<int>;
|
||||
template class Array2D<real_t>;
|
||||
|
||||
} // namespace mfem
|
||||
+213
-15
@@ -16,9 +16,13 @@
|
||||
#include "mem_manager.hpp"
|
||||
#include "device.hpp"
|
||||
#include "error.hpp"
|
||||
#include "forall.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "reducers.hpp"
|
||||
#include "scan.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
@@ -135,6 +139,8 @@ public:
|
||||
/// Return the device flag of the Memory object used by the Array
|
||||
bool UseDevice() const { return data.UseDevice(); }
|
||||
|
||||
void UseDevice(bool use_dev) { data.UseDevice(use_dev); }
|
||||
|
||||
/// Return true if the data will be deleted by the Array
|
||||
inline bool OwnsData() const { return data.OwnsHostPtr(); }
|
||||
|
||||
@@ -275,11 +281,11 @@ public:
|
||||
|
||||
/** @brief Find the maximal element in the array, using the comparison
|
||||
operator `<` for class T. */
|
||||
T Max() const;
|
||||
inline T Max() const;
|
||||
|
||||
/** @brief Find the minimal element in the array, using the comparison
|
||||
operator `<` for class T. */
|
||||
T Min() const;
|
||||
inline T Min() const;
|
||||
|
||||
/// Sorts the array in ascending order. This requires operator< to be defined for T.
|
||||
void Sort() { std::sort((T*)data, data + size); }
|
||||
@@ -297,22 +303,22 @@ public:
|
||||
}
|
||||
|
||||
/// Return 1 if the array is sorted from lowest to highest. Otherwise return 0.
|
||||
int IsSorted() const;
|
||||
inline int IsSorted() const;
|
||||
|
||||
/// Does the Array have Size zero.
|
||||
bool IsEmpty() const { return Size() == 0; }
|
||||
|
||||
/// Return true if all entries of the array are the same.
|
||||
bool IsConstant() const;
|
||||
inline bool IsConstant() const;
|
||||
|
||||
/// Fill the entries of the array with the cumulative sum of the entries.
|
||||
void PartialSum();
|
||||
inline void PartialSum();
|
||||
|
||||
/// Replace each entry of the array with its absolute value.
|
||||
void Abs();
|
||||
inline void Abs();
|
||||
|
||||
/// Return the sum of all the array entries using the '+'' operator for class 'T'.
|
||||
T Sum() const;
|
||||
inline T Sum() const;
|
||||
|
||||
/// Set all entries of the array to the provided constant.
|
||||
inline void operator=(const T &a);
|
||||
@@ -797,8 +803,14 @@ template <typename T> template <typename CT>
|
||||
inline Array<T> &Array<T>::operator=(const Array<CT> &src)
|
||||
{
|
||||
SetSize(src.Size());
|
||||
for (int i = 0; i < size; i++) { (*this)[i] = T(src[i]); }
|
||||
return *this;
|
||||
|
||||
const bool use_dev = UseDevice() || src.UseDevice();
|
||||
const auto x = src.Read(use_dev);
|
||||
auto y = Write(use_dev);
|
||||
mfem::forall_switch(use_dev, size, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
y[i] = x[i];
|
||||
});
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -1014,19 +1026,24 @@ template <class T>
|
||||
inline void Array<T>::GetSubArray(int offset, int sa_size, Array<T> &sa) const
|
||||
{
|
||||
sa.SetSize(sa_size);
|
||||
for (int i = 0; i < sa_size; i++)
|
||||
const bool use_dev = UseDevice() || sa.UseDevice();
|
||||
const auto x = Read(use_dev);
|
||||
auto y = sa.Write(use_dev);
|
||||
mfem::forall_switch(use_dev, sa_size, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
sa[i] = (*this)[offset+i];
|
||||
}
|
||||
y[i] = x[offset + i];
|
||||
});
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::operator=(const T &a)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
const bool use_dev = UseDevice();
|
||||
auto x = Write(use_dev);
|
||||
mfem::forall_switch(use_dev, size, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
data[i] = a;
|
||||
}
|
||||
x[i] = a;
|
||||
});
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -1035,6 +1052,153 @@ inline void Array<T>::Assign(const T *p)
|
||||
data.CopyFromHost(p, Size());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::Print(std::ostream &os, int width) const
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
os << data[i];
|
||||
if ( !((i+1) % width) || i+1 == size )
|
||||
{
|
||||
os << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
os << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::Save(std::ostream &os, int fmt) const
|
||||
{
|
||||
if (fmt == 0)
|
||||
{
|
||||
os << size << '\n';
|
||||
}
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
os << operator[](i) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Load(std::istream &in, int fmt)
|
||||
{
|
||||
if (fmt == 0)
|
||||
{
|
||||
int new_size;
|
||||
in >> new_size;
|
||||
SetSize(new_size);
|
||||
}
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
in >> operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T Array<T>::Max() const
|
||||
{
|
||||
MFEM_ASSERT(size > 0, "Array is empty with size " << size);
|
||||
|
||||
T max = operator[](0);
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (max < operator[](i))
|
||||
{
|
||||
max = operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T Array<T>::Min() const
|
||||
{
|
||||
MFEM_ASSERT(size > 0, "Array is empty with size " << size);
|
||||
|
||||
T min = operator[](0);
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (operator[](i) < min)
|
||||
{
|
||||
min = operator[](i);
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
// Partial Sum
|
||||
template <class T>
|
||||
inline void Array<T>::PartialSum()
|
||||
{
|
||||
auto data_ptr = ReadWrite(UseDevice());
|
||||
InclusiveScan(UseDevice(), data_ptr, data_ptr, size);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::Abs()
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "Use with arithmetic types!");
|
||||
const bool useDevice = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(useDevice);
|
||||
mfem::forall_switch(useDevice, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
y[i] = std::abs(y[i]);
|
||||
});
|
||||
}
|
||||
|
||||
// Sum
|
||||
template <class T>
|
||||
inline T Array<T>::Sum() const
|
||||
{
|
||||
T sum = static_cast<T>(0);
|
||||
if (size > 0)
|
||||
{
|
||||
const auto m_data = Read(UseDevice());
|
||||
reduce(size, sum, [=] MFEM_HOST_DEVICE(int i, T &r) { r += m_data[i]; },
|
||||
/* */ SumReducer<T> {}, UseDevice());
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline int Array<T>::IsSorted() const
|
||||
{
|
||||
T val_prev = operator[](0), val;
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
val=operator[](i);
|
||||
if (val < val_prev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
val_prev = val;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool Array<T>::IsConstant() const
|
||||
{
|
||||
if (size < 2) { return true; }
|
||||
const T v0 = data[0];
|
||||
for (int i = 1; i < size; i++)
|
||||
{
|
||||
if (data[i] != v0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
inline const T &Array2D<T>::operator()(int i, int j) const
|
||||
@@ -1074,6 +1238,40 @@ inline T *Array2D<T>::operator[](int i)
|
||||
return &array1d[i*N];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array2D<T>::Load(const char *filename, int fmt)
|
||||
{
|
||||
std::ifstream in;
|
||||
in.open(filename, std::ifstream::in);
|
||||
MFEM_VERIFY(in.is_open(), "File " << filename << " does not exist.");
|
||||
Load(in, fmt);
|
||||
in.close();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array2D<T>::Print(std::ostream &os, int width_)
|
||||
{
|
||||
int height = this->NumRows();
|
||||
int width = this->NumCols();
|
||||
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
os << "[row " << i << "]\n";
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
os << (*this)(i,j);
|
||||
if ( (j+1) == width_ || (j+1) % width_ == 0 )
|
||||
{
|
||||
os << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
os << ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
inline void Swap(Array2D<T> &a, Array2D<T> &b)
|
||||
|
||||
+29
-10
@@ -12,7 +12,6 @@
|
||||
#ifndef MFEM_REDUCERS_HPP
|
||||
#define MFEM_REDUCERS_HPP
|
||||
|
||||
#include "array.hpp"
|
||||
#include "forall.hpp"
|
||||
|
||||
#include <cmath>
|
||||
@@ -514,6 +513,33 @@ template<class B, class R> struct reduction_kernel
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ReductionWorkspace
|
||||
{
|
||||
Memory<T> workspace;
|
||||
|
||||
static ReductionWorkspace &Instance()
|
||||
{
|
||||
static ReductionWorkspace instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
~ReductionWorkspace() { workspace.Delete(); }
|
||||
|
||||
public:
|
||||
static T *Get(int num_blocks)
|
||||
{
|
||||
ReductionWorkspace &instance = Instance();
|
||||
if (instance.workspace.Capacity() < num_blocks)
|
||||
{
|
||||
instance.workspace.Delete();
|
||||
instance.workspace.New(num_blocks, MemoryType::HOST_PINNED);
|
||||
}
|
||||
return instance.workspace;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,8 +555,7 @@ template<class B, class R> struct reduction_kernel
|
||||
@tparam T value_type to operate on
|
||||
*/
|
||||
template <class T, class B, class R>
|
||||
void reduce(int N, T &res, B &&body, const R &reducer, bool use_dev,
|
||||
Array<T> &workspace)
|
||||
void reduce(int N, T &res, B &&body, const R &reducer, bool use_dev)
|
||||
{
|
||||
if (N == 0)
|
||||
{
|
||||
@@ -567,13 +592,7 @@ void reduce(int N, T &res, B &&body, const R &reducer, bool use_dev,
|
||||
|
||||
red_type red{nullptr, std::forward<B>(body), reducer, N, items_per_thread};
|
||||
// allocate res to fit block_size entries
|
||||
auto mt = workspace.GetMemory().GetMemoryType();
|
||||
if (mt != MemoryType::HOST_PINNED && mt != MemoryType::MANAGED)
|
||||
{
|
||||
mt = MemoryType::HOST_PINNED;
|
||||
}
|
||||
workspace.SetSize(nblocks, mt);
|
||||
auto work = workspace.HostWrite();
|
||||
auto work = internal::ReductionWorkspace<T>::Get(nblocks);
|
||||
red.work = work;
|
||||
forall_2D(nblocks, block_size, 1, std::move(red));
|
||||
// wait for results
|
||||
|
||||
+52
-22
@@ -28,8 +28,37 @@
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/// Equivalent to InclusiveScan(use_dev, d_in, d_out, num_items, workspace,
|
||||
/// std::plus<>{})
|
||||
|
||||
namespace internal
|
||||
{
|
||||
class ScanWorkspace
|
||||
{
|
||||
Memory<std::byte> workspace;
|
||||
static ScanWorkspace &Instance()
|
||||
{
|
||||
static ScanWorkspace instance;
|
||||
return instance;
|
||||
}
|
||||
~ScanWorkspace() { workspace.Delete(); }
|
||||
public:
|
||||
static std::byte *Get(int num_bytes)
|
||||
{
|
||||
ScanWorkspace &instance = Instance();
|
||||
if (Size() < num_bytes)
|
||||
{
|
||||
instance.workspace.Delete();
|
||||
instance.workspace.New(num_bytes);
|
||||
}
|
||||
return instance.workspace.Write(MemoryClass::DEVICE, Size());
|
||||
}
|
||||
static int Size()
|
||||
{
|
||||
return Instance().workspace.Capacity();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Equivalent to InclusiveScan(use_dev, d_in, d_out, num_items, std::plus<>{})
|
||||
template <class InputIt, class OutputIt>
|
||||
void InclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items)
|
||||
{
|
||||
@@ -37,12 +66,12 @@ void InclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items)
|
||||
#if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
|
||||
if (use_dev && mfem::Device::Allows(Backend::CUDA_MASK | Backend::HIP_MASK))
|
||||
{
|
||||
static Array<std::byte> workspace;
|
||||
size_t bytes = workspace.Size();
|
||||
if (bytes)
|
||||
using internal::ScanWorkspace;
|
||||
size_t bytes = ScanWorkspace::Size();
|
||||
if (bytes > 0)
|
||||
{
|
||||
auto err = MFEM_CUB_NAMESPACE::DeviceScan::InclusiveSum(
|
||||
workspace.Write(), bytes, d_in, d_out, num_items);
|
||||
ScanWorkspace::Get(bytes), bytes, d_in, d_out, num_items);
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
if (err == cudaSuccess)
|
||||
{
|
||||
@@ -57,11 +86,12 @@ void InclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items)
|
||||
}
|
||||
// try allocating a larger buffer
|
||||
bytes = 0;
|
||||
// get size of buffer
|
||||
MFEM_GPU_CHECK(MFEM_CUB_NAMESPACE::DeviceScan::InclusiveSum(
|
||||
nullptr, bytes, d_in, d_out, num_items));
|
||||
workspace.SetSize(bytes);
|
||||
// resize buffer (in ScanWorkspace::Get) and try again
|
||||
MFEM_GPU_CHECK(MFEM_CUB_NAMESPACE::DeviceScan::InclusiveSum(
|
||||
workspace.Write(), bytes, d_in, d_out, num_items));
|
||||
ScanWorkspace::Get(bytes), bytes, d_in, d_out, num_items));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -101,12 +131,13 @@ void InclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items,
|
||||
#if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
|
||||
if (use_dev && mfem::Device::Allows(Backend::CUDA_MASK | Backend::HIP_MASK))
|
||||
{
|
||||
static Array<std::byte> workspace;
|
||||
size_t bytes = workspace.Size();
|
||||
if (bytes)
|
||||
using internal::ScanWorkspace;
|
||||
size_t bytes = ScanWorkspace::Size();
|
||||
if (bytes > 0)
|
||||
{
|
||||
auto err = MFEM_CUB_NAMESPACE::DeviceScan::InclusiveScan(
|
||||
workspace.Write(), bytes, d_in, d_out, scan_op, num_items);
|
||||
ScanWorkspace::Get(bytes), bytes, d_in, d_out, scan_op,
|
||||
num_items);
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
if (err == cudaSuccess)
|
||||
{
|
||||
@@ -123,9 +154,9 @@ void InclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items,
|
||||
bytes = 0;
|
||||
MFEM_GPU_CHECK(MFEM_CUB_NAMESPACE::DeviceScan::InclusiveScan(
|
||||
nullptr, bytes, d_in, d_out, scan_op, num_items));
|
||||
workspace.SetSize(bytes);
|
||||
MFEM_GPU_CHECK(MFEM_CUB_NAMESPACE::DeviceScan::InclusiveScan(
|
||||
workspace.Write(), bytes, d_in, d_out, scan_op, num_items));
|
||||
ScanWorkspace::Get(bytes), bytes, d_in, d_out, scan_op,
|
||||
num_items));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -164,13 +195,13 @@ void ExclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items,
|
||||
#if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
|
||||
if (use_dev && mfem::Device::Allows(Backend::CUDA_MASK | Backend::HIP_MASK))
|
||||
{
|
||||
static Array<std::byte> workspace;
|
||||
size_t bytes = workspace.Size();
|
||||
using internal::ScanWorkspace;
|
||||
size_t bytes = ScanWorkspace::Size();
|
||||
if (bytes)
|
||||
{
|
||||
auto err = MFEM_CUB_NAMESPACE::DeviceScan::ExclusiveScan(
|
||||
workspace.Write(), bytes, d_in, d_out, scan_op, init_value,
|
||||
num_items);
|
||||
ScanWorkspace::Get(bytes), bytes, d_in, d_out, scan_op,
|
||||
init_value, num_items);
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
if (err == cudaSuccess)
|
||||
{
|
||||
@@ -187,10 +218,9 @@ void ExclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items,
|
||||
bytes = 0;
|
||||
MFEM_GPU_CHECK(MFEM_CUB_NAMESPACE::DeviceScan::ExclusiveScan(
|
||||
nullptr, bytes, d_in, d_out, scan_op, init_value, num_items));
|
||||
workspace.SetSize(bytes);
|
||||
MFEM_GPU_CHECK(MFEM_CUB_NAMESPACE::DeviceScan::ExclusiveScan(
|
||||
workspace.Write(), bytes, d_in, d_out, scan_op, init_value,
|
||||
num_items));
|
||||
ScanWorkspace::Get(bytes), bytes, d_in, d_out, scan_op,
|
||||
init_value, num_items));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -213,7 +243,7 @@ void ExclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items,
|
||||
}
|
||||
|
||||
/// Equivalent to ExclusiveScan(use_dev, d_in, d_out, num_items, init_value,
|
||||
/// workspace, std::plus<>{})
|
||||
/// std::plus<>{})
|
||||
template <class InputIt, class OutputIt, class T>
|
||||
void ExclusiveScan(bool use_dev, InputIt d_in, OutputIt d_out, size_t num_items,
|
||||
T init_value)
|
||||
|
||||
+8
-20
@@ -92,18 +92,6 @@ struct LpReducer
|
||||
}
|
||||
};
|
||||
|
||||
static Array<real_t>& vector_workspace()
|
||||
{
|
||||
static Array<real_t> instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static Array<DevicePair<real_t, real_t>> &Lpvector_workspace()
|
||||
{
|
||||
static Array<DevicePair<real_t, real_t>> instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
Vector::Vector(const Vector &v)
|
||||
{
|
||||
const int s = v.Size();
|
||||
@@ -991,7 +979,7 @@ real_t Vector::Norml2() const
|
||||
}
|
||||
}
|
||||
},
|
||||
L2Reducer{}, UseDevice(), Lpvector_workspace());
|
||||
L2Reducer{}, UseDevice());
|
||||
// final answer
|
||||
return res.second * sqrt(res.first);
|
||||
}
|
||||
@@ -1006,7 +994,7 @@ real_t Vector::Normlinf() const
|
||||
{
|
||||
r = fmax(r, fabs(m_data[i]));
|
||||
},
|
||||
MaxReducer<real_t> {}, UseDevice(), vector_workspace());
|
||||
MaxReducer<real_t> {}, UseDevice());
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1020,7 +1008,7 @@ real_t Vector::Norml1() const
|
||||
{
|
||||
r += fabs(m_data[i]);
|
||||
},
|
||||
SumReducer<real_t> {}, UseDevice(), vector_workspace());
|
||||
SumReducer<real_t> {}, UseDevice());
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1063,7 +1051,7 @@ real_t Vector::Normlp(real_t p) const
|
||||
}
|
||||
}
|
||||
},
|
||||
LpReducer{p}, UseDevice(), Lpvector_workspace());
|
||||
LpReducer{p}, UseDevice());
|
||||
// final answer
|
||||
return res.second * pow(res.first, 1.0 / p);
|
||||
} // end if p < infinity()
|
||||
@@ -1096,7 +1084,7 @@ real_t Vector::operator*(const Vector &v) const
|
||||
{
|
||||
r += m_data[i] * v_data[i];
|
||||
},
|
||||
SumReducer<real_t> {}, use_dev, vector_workspace());
|
||||
SumReducer<real_t> {}, use_dev);
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -1167,7 +1155,7 @@ real_t Vector::Min() const
|
||||
{
|
||||
r = fmin(r, m_data[i]);
|
||||
},
|
||||
MinReducer<real_t> {}, use_dev, vector_workspace());
|
||||
MinReducer<real_t> {}, use_dev);
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -1213,7 +1201,7 @@ real_t Vector::Max() const
|
||||
{
|
||||
r = fmax(r, m_data[i]);
|
||||
},
|
||||
MaxReducer<real_t> {}, use_dev, vector_workspace());
|
||||
MaxReducer<real_t> {}, use_dev);
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -1248,7 +1236,7 @@ real_t Vector::Sum() const
|
||||
{
|
||||
r += m_data[i];
|
||||
},
|
||||
SumReducer<real_t> {}, UseDevice(), vector_workspace());
|
||||
SumReducer<real_t> {}, UseDevice());
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ using namespace mfem;
|
||||
|
||||
TEST_CASE("Reduce Sum", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<int> workspace;
|
||||
Array<int> a(1000);
|
||||
a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -36,7 +35,7 @@ TEST_CASE("Reduce Sum", "[Reduction],[GPU]")
|
||||
int res = 0;
|
||||
mfem::reduce(
|
||||
a.Size(), res, [=] MFEM_HOST_DEVICE(int i, int &r) { r += dptr[i]; },
|
||||
SumReducer<int> {}, use_dev, workspace);
|
||||
SumReducer<int> {}, use_dev);
|
||||
// correct for even-length summations
|
||||
int expected = (AsConst(a)[0] + AsConst(a)[a.Size() - 1]) * a.Size() / 2;
|
||||
CAPTURE(use_dev);
|
||||
@@ -46,7 +45,6 @@ TEST_CASE("Reduce Sum", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce Mult", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<long long> workspace;
|
||||
Array<long long> a(64);
|
||||
a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -64,7 +62,7 @@ TEST_CASE("Reduce Mult", "[Reduction],[GPU]")
|
||||
mfem::reduce(
|
||||
a.Size(), res,
|
||||
[=] MFEM_HOST_DEVICE(int i, long long &r) { r *= dptr[i]; },
|
||||
MultReducer<long long> {}, use_dev, workspace);
|
||||
MultReducer<long long> {}, use_dev);
|
||||
long long expected = 0;
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res == expected);
|
||||
@@ -76,7 +74,7 @@ TEST_CASE("Reduce Mult", "[Reduction],[GPU]")
|
||||
mfem::reduce(
|
||||
a.Size(), res,
|
||||
[=] MFEM_HOST_DEVICE(int i, long long &r) { r *= dptr[i]; },
|
||||
MultReducer<long long> {}, use_dev, workspace);
|
||||
MultReducer<long long> {}, use_dev);
|
||||
long long expected = 21936950640377856;
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res == expected);
|
||||
@@ -86,7 +84,6 @@ TEST_CASE("Reduce Mult", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce BAnd", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<unsigned> workspace;
|
||||
Array<unsigned> a(10);
|
||||
SECTION("{ Bit unset }")
|
||||
{
|
||||
@@ -108,7 +105,7 @@ TEST_CASE("Reduce BAnd", "[Reduction],[GPU]")
|
||||
mfem::reduce(
|
||||
a.Size(), res,
|
||||
[=] MFEM_HOST_DEVICE(int i, unsigned &r) { r &= dptr[i]; },
|
||||
BAndReducer<unsigned> {}, use_dev, workspace);
|
||||
BAndReducer<unsigned> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res == ((~1u) & ~(1u << unset_bit)));
|
||||
REQUIRE((res & (1u << unset_bit)) == 0);
|
||||
@@ -132,7 +129,7 @@ TEST_CASE("Reduce BAnd", "[Reduction],[GPU]")
|
||||
mfem::reduce(
|
||||
a.Size(), res,
|
||||
[=] MFEM_HOST_DEVICE(int i, unsigned &r) { r &= dptr[i]; },
|
||||
BAndReducer<unsigned> {}, use_dev, workspace);
|
||||
BAndReducer<unsigned> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res == (1u << set_bit));
|
||||
}
|
||||
@@ -141,7 +138,6 @@ TEST_CASE("Reduce BAnd", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce BOr", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<unsigned> workspace;
|
||||
Array<unsigned> a(0x210);
|
||||
a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -157,7 +153,7 @@ TEST_CASE("Reduce BOr", "[Reduction],[GPU]")
|
||||
mfem::reduce(
|
||||
a.Size(), res,
|
||||
[=] MFEM_HOST_DEVICE(int i, unsigned &r) { r |= dptr[i]; },
|
||||
BOrReducer<unsigned> {}, use_dev, workspace);
|
||||
BOrReducer<unsigned> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res == 0x3ffu);
|
||||
}
|
||||
@@ -165,7 +161,6 @@ TEST_CASE("Reduce BOr", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce Min", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<int> workspace;
|
||||
Array<int> a(1000);
|
||||
auto hptr = a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -190,7 +185,7 @@ TEST_CASE("Reduce Min", "[Reduction],[GPU]")
|
||||
r = dptr[i];
|
||||
}
|
||||
},
|
||||
MinReducer<int> {}, use_dev, workspace);
|
||||
MinReducer<int> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res == -10);
|
||||
}
|
||||
@@ -198,7 +193,6 @@ TEST_CASE("Reduce Min", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce Max", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<int> workspace;
|
||||
Array<int> a(1000);
|
||||
auto hptr = a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -223,7 +217,7 @@ TEST_CASE("Reduce Max", "[Reduction],[GPU]")
|
||||
r = dptr[i];
|
||||
}
|
||||
},
|
||||
MaxReducer<int> {}, use_dev, workspace);
|
||||
MaxReducer<int> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res == 999 - 10);
|
||||
}
|
||||
@@ -231,7 +225,6 @@ TEST_CASE("Reduce Max", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce MinMax", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<DevicePair<int, int>> workspace;
|
||||
Array<int> a(1000);
|
||||
auto hptr = a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -262,7 +255,7 @@ TEST_CASE("Reduce MinMax", "[Reduction],[GPU]")
|
||||
r.second = dptr[i];
|
||||
}
|
||||
},
|
||||
MinMaxReducer<int> {}, use_dev, workspace);
|
||||
MinMaxReducer<int> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res.first == -10);
|
||||
REQUIRE(res.second == a.Size() - 11);
|
||||
@@ -271,7 +264,6 @@ TEST_CASE("Reduce MinMax", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce ArgMin", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<DevicePair<double, int>> workspace;
|
||||
Array<double> a(1000);
|
||||
auto hptr = a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -297,7 +289,7 @@ TEST_CASE("Reduce ArgMin", "[Reduction],[GPU]")
|
||||
r.second = i;
|
||||
}
|
||||
},
|
||||
ArgMinReducer<double, int> {}, use_dev, workspace);
|
||||
ArgMinReducer<double, int> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res.first == -10);
|
||||
REQUIRE(res.second >= 0);
|
||||
@@ -308,7 +300,6 @@ TEST_CASE("Reduce ArgMin", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce ArgMax", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<DevicePair<double, int>> workspace;
|
||||
Array<double> a(1000);
|
||||
|
||||
auto hptr = a.HostReadWrite();
|
||||
@@ -337,7 +328,7 @@ TEST_CASE("Reduce ArgMax", "[Reduction],[GPU]")
|
||||
r.second = i;
|
||||
}
|
||||
},
|
||||
ArgMaxReducer<double, int> {}, use_dev, workspace);
|
||||
ArgMaxReducer<double, int> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res.first == a.Size() - 11);
|
||||
REQUIRE(res.second >= 0);
|
||||
@@ -348,7 +339,6 @@ TEST_CASE("Reduce ArgMax", "[Reduction],[GPU]")
|
||||
|
||||
TEST_CASE("Reduce ArgMinMax", "[Reduction],[GPU]")
|
||||
{
|
||||
Array<MinMaxLocScalar<double, int>> workspace;
|
||||
Array<double> a(1000);
|
||||
auto hptr = a.HostReadWrite();
|
||||
for (int i = 0; i < a.Size(); ++i)
|
||||
@@ -383,7 +373,7 @@ TEST_CASE("Reduce ArgMinMax", "[Reduction],[GPU]")
|
||||
r.max_loc = i;
|
||||
}
|
||||
},
|
||||
ArgMinMaxReducer<double, int> {}, use_dev, workspace);
|
||||
ArgMinMaxReducer<double, int> {}, use_dev);
|
||||
CAPTURE(use_dev);
|
||||
REQUIRE(res.min_val == -10);
|
||||
REQUIRE(res.min_loc >= 0);
|
||||
|
||||
Reference in New Issue
Block a user