Updating threadpool, Array, StackTrace, ... classes
This commit is contained in:
626
common/Array.h
626
common/Array.h
@@ -1,34 +1,15 @@
|
||||
#ifndef included_ArrayClass
|
||||
#define included_ArrayClass
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#define ARRAY_NDIM_MAX 5 // Maximum number of dimensions supported
|
||||
|
||||
|
||||
#define GET_ARRAY_INDEX3D( N, i1, i2, i3 ) i1 + N[0] * ( i2 + N[1] * i3 )
|
||||
#define GET_ARRAY_INDEX4D( N, i1, i2, i3, i4 ) i1 + N[0] * ( i2 + N[1] * ( i3 + N[2] * i4 ) )
|
||||
#define GET_ARRAY_INDEX5D( N, i1, i2, i3, i4, i5 ) i1 + N[0] * ( i2 + N[1] * ( i3 + N[2] * ( i4 + N[3] * i5 ) ) )
|
||||
|
||||
#if defined( DEBUG ) || defined( _DEBUG )
|
||||
#define CHECK_ARRAY_INDEX3D( N, i1, i2, i3 ) \
|
||||
if ( GET_ARRAY_INDEX3D( N, i1, i2, i3 ) < 0 || GET_ARRAY_INDEX3D( N, i1, i2, i3 ) >= d_length ) \
|
||||
throw std::logic_error( "Index exceeds array bounds" );
|
||||
#define CHECK_ARRAY_INDEX4D( N, i1, i2, i3, i4 ) \
|
||||
if ( GET_ARRAY_INDEX4D( N, i1, i2, i3, i4 ) < 0 || \
|
||||
GET_ARRAY_INDEX4D( N, i1, i2, i3, i4 ) >= d_length ) \
|
||||
throw std::logic_error( "Index exceeds array bounds" );
|
||||
#else
|
||||
#define CHECK_ARRAY_INDEX3D( N, i1, i2, i3 )
|
||||
#define CHECK_ARRAY_INDEX4D( N, i1, i2, i3, i4 )
|
||||
#endif
|
||||
#include "Utilities.h"
|
||||
|
||||
|
||||
#if defined( __CUDA_ARCH__ )
|
||||
@@ -37,20 +18,244 @@
|
||||
#else
|
||||
#define HOST_DEVICE
|
||||
#endif
|
||||
#if defined( USING_GCC ) || defined( USING_CLANG )
|
||||
#define ATTRIBUTE_INLINE __attribute__( ( always_inline ) )
|
||||
#else
|
||||
#define ATTRIBUTE_INLINE
|
||||
#endif
|
||||
|
||||
|
||||
#if ( defined( DEBUG ) || defined( _DEBUG ) ) && !defined( NDEBUG )
|
||||
#define CHECK_ARRAY_LENGTH( i ) \
|
||||
do { \
|
||||
if ( i >= d_length ) \
|
||||
throw std::length_error( "Index exceeds array bounds" ); \
|
||||
} while ( 0 )
|
||||
#else
|
||||
#define CHECK_ARRAY_LENGTH( i ) \
|
||||
do { \
|
||||
} while ( 0 )
|
||||
#endif
|
||||
|
||||
|
||||
// Forward decleration
|
||||
class FunctionTable;
|
||||
|
||||
|
||||
//! Simple range class
|
||||
template<class TYPE = size_t>
|
||||
class Range final
|
||||
{
|
||||
public:
|
||||
//! Empty constructor
|
||||
Range() : i( 0 ), j( -1 ), k( 1 ) {}
|
||||
|
||||
/*!
|
||||
* Create a range i:k:j (or i:j)
|
||||
* @param i_ Starting value
|
||||
* @param j_ Ending value
|
||||
* @param k_ Increment value
|
||||
*/
|
||||
Range( TYPE i_, TYPE j_, TYPE k_ = 1 ) : i( i_ ), j( j_ ), k( k_ ) {}
|
||||
|
||||
TYPE i, j, k;
|
||||
};
|
||||
|
||||
|
||||
//! Simple class to store the array dimensions
|
||||
class ArraySize final
|
||||
{
|
||||
public:
|
||||
//! Empty constructor
|
||||
inline ArraySize();
|
||||
|
||||
/*!
|
||||
* Create the vector size
|
||||
* @param N1 Number of elements in the first dimension
|
||||
*/
|
||||
inline ArraySize( size_t N1 );
|
||||
|
||||
/*!
|
||||
* Create the vector size
|
||||
* @param N1 Number of elements in the first dimension
|
||||
* @param N2 Number of elements in the second dimension
|
||||
*/
|
||||
inline ArraySize( size_t N1, size_t N2 );
|
||||
|
||||
/*!
|
||||
* Create the vector size
|
||||
* @param N1 Number of elements in the first dimension
|
||||
* @param N2 Number of elements in the second dimension
|
||||
* @param N3 Number of elements in the third dimension
|
||||
*/
|
||||
inline ArraySize( size_t N1, size_t N2, size_t N3 );
|
||||
|
||||
/*!
|
||||
* Create the vector size
|
||||
* @param N1 Number of elements in the first dimension
|
||||
* @param N2 Number of elements in the second dimension
|
||||
* @param N3 Number of elements in the third dimension
|
||||
* @param N4 Number of elements in the fourth dimension
|
||||
*/
|
||||
inline ArraySize( size_t N1, size_t N2, size_t N3, size_t N4 );
|
||||
|
||||
/*!
|
||||
* Create the vector size
|
||||
* @param N1 Number of elements in the first dimension
|
||||
* @param N2 Number of elements in the second dimension
|
||||
* @param N3 Number of elements in the third dimension
|
||||
* @param N4 Number of elements in the fourth dimension
|
||||
* @param N5 Number of elements in the fifth dimension
|
||||
*/
|
||||
inline ArraySize( size_t N1, size_t N2, size_t N3, size_t N4, size_t N5 );
|
||||
|
||||
/*!
|
||||
* Create from initializer list
|
||||
* @param N Size of the array
|
||||
*/
|
||||
inline ArraySize( std::initializer_list<size_t> N );
|
||||
|
||||
/*!
|
||||
* Create from raw pointer
|
||||
* @param ndim Number of dimensions
|
||||
* @param ndim Dimensions
|
||||
*/
|
||||
inline ArraySize( size_t ndim, const size_t *dims );
|
||||
|
||||
/*!
|
||||
* Create from std::vector
|
||||
* @param N Size of the array
|
||||
*/
|
||||
inline ArraySize( const std::vector<size_t> &N );
|
||||
|
||||
/*!
|
||||
* Copy constructor
|
||||
* @param rhs Array to copy
|
||||
*/
|
||||
inline ArraySize( const ArraySize &rhs );
|
||||
|
||||
/*!
|
||||
* Move constructor
|
||||
* @param rhs Array to copy
|
||||
*/
|
||||
inline ArraySize( ArraySize &&rhs );
|
||||
|
||||
/*!
|
||||
* Assignment operator
|
||||
* @param rhs Array to copy
|
||||
*/
|
||||
inline ArraySize &operator=( const ArraySize &rhs );
|
||||
|
||||
/*!
|
||||
* Move assignment operator
|
||||
* @param rhs Array to copy
|
||||
*/
|
||||
inline ArraySize &operator=( ArraySize &&rhs );
|
||||
|
||||
/*!
|
||||
* Access the ith dimension
|
||||
* @param i Index to access
|
||||
*/
|
||||
inline size_t operator[]( size_t i ) const { return d_N[i]; }
|
||||
|
||||
//! Sum the elements
|
||||
inline uint8_t ndim() const ATTRIBUTE_INLINE { return d_ndim; }
|
||||
|
||||
//! Sum the elements
|
||||
inline size_t size() const ATTRIBUTE_INLINE { return d_ndim; }
|
||||
|
||||
//! Sum the elements
|
||||
inline size_t length() const ATTRIBUTE_INLINE { return d_length; }
|
||||
|
||||
//! Sum the elements
|
||||
inline void resize( uint8_t dim, size_t N );
|
||||
|
||||
//! Returns an iterator to the beginning
|
||||
inline const size_t *begin() const ATTRIBUTE_INLINE { return d_N; }
|
||||
|
||||
//! Returns an iterator to the end
|
||||
inline const size_t *end() const ATTRIBUTE_INLINE { return d_N + d_ndim; }
|
||||
|
||||
// Check if two matrices are equal
|
||||
inline bool operator==( const ArraySize &rhs ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
return d_ndim == rhs.d_ndim && memcmp( d_N, rhs.d_N, sizeof( d_N ) ) == 0;
|
||||
}
|
||||
|
||||
//! Check if two matrices are not equal
|
||||
inline bool operator!=( const ArraySize &rhs ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
return d_ndim != rhs.d_ndim || memcmp( d_N, rhs.d_N, sizeof( d_N ) ) != 0;
|
||||
}
|
||||
|
||||
//! Maximum supported dimension
|
||||
constexpr static uint8_t maxDim() ATTRIBUTE_INLINE { return 5u; }
|
||||
|
||||
//! Get the index
|
||||
inline size_t index( size_t i ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_LENGTH( i );
|
||||
return i;
|
||||
}
|
||||
|
||||
//! Get the index
|
||||
inline size_t index( size_t i1, size_t i2 ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
size_t index = i1 + i2 * d_N[0];
|
||||
CHECK_ARRAY_LENGTH( index );
|
||||
return index;
|
||||
}
|
||||
|
||||
//! Get the index
|
||||
inline size_t index( size_t i1, size_t i2, size_t i3 ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
size_t index = i1 + d_N[0] * ( i2 + d_N[1] * i3 );
|
||||
CHECK_ARRAY_LENGTH( index );
|
||||
return index;
|
||||
}
|
||||
|
||||
//! Get the index
|
||||
inline size_t index( size_t i1, size_t i2, size_t i3, size_t i4 ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
size_t index = i1 + d_N[0] * ( i2 + d_N[1] * ( i3 + d_N[2] * i4 ) );
|
||||
CHECK_ARRAY_LENGTH( index );
|
||||
return index;
|
||||
}
|
||||
|
||||
//! Get the index
|
||||
inline size_t index(
|
||||
size_t i1, size_t i2, size_t i3, size_t i4, size_t i5 ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
size_t index = i1 + d_N[0] * ( i2 + d_N[1] * ( i3 + d_N[2] * ( i4 + d_N[3] * i5 ) ) );
|
||||
CHECK_ARRAY_LENGTH( index );
|
||||
return index;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t d_ndim;
|
||||
size_t d_length;
|
||||
size_t d_N[5];
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* Class Array is a multi-dimensional array class written by Mark Berrill
|
||||
*/
|
||||
template <class TYPE>
|
||||
class Array
|
||||
template<class TYPE, class FUN = FunctionTable>
|
||||
class Array final
|
||||
{
|
||||
public:
|
||||
public: // Constructors / assignment operators
|
||||
/*!
|
||||
* Create a new empty Array
|
||||
*/
|
||||
Array();
|
||||
|
||||
/*!
|
||||
* Create an Array with the given size
|
||||
* @param N Size of the array
|
||||
*/
|
||||
explicit Array( const ArraySize &N );
|
||||
|
||||
/*!
|
||||
* Create a new 1D Array with the given number of elements
|
||||
* @param N Number of elements in the array
|
||||
@@ -72,6 +277,25 @@ public:
|
||||
*/
|
||||
explicit Array( size_t N1, size_t N2, size_t N3 );
|
||||
|
||||
/*!
|
||||
* Create a new 4D Array with the given number of rows and columns
|
||||
* @param N1 Number of elements in the first dimension
|
||||
* @param N2 Number of elements in the second dimension
|
||||
* @param N3 Number of elements in the third dimension
|
||||
* @param N4 Number of elements in the fourth dimension
|
||||
*/
|
||||
explicit Array( size_t N1, size_t N2, size_t N3, size_t N4 );
|
||||
|
||||
/*!
|
||||
* Create a new 4D Array with the given number of rows and columns
|
||||
* @param N1 Number of elements in the first dimension
|
||||
* @param N2 Number of elements in the second dimension
|
||||
* @param N3 Number of elements in the third dimension
|
||||
* @param N4 Number of elements in the fourth dimension
|
||||
* @param N5 Number of elements in the fifth dimension
|
||||
*/
|
||||
explicit Array( size_t N1, size_t N2, size_t N3, size_t N4, size_t N5 );
|
||||
|
||||
/*!
|
||||
* Create a multi-dimensional Array with the given number of elements
|
||||
* @param N Number of elements in each dimension
|
||||
@@ -79,6 +303,19 @@ public:
|
||||
*/
|
||||
explicit Array( const std::vector<size_t> &N, const TYPE *data = NULL );
|
||||
|
||||
/*!
|
||||
* Create a 1D Array with the range
|
||||
* @param range Range of the data
|
||||
*/
|
||||
explicit Array( const Range<TYPE> &range );
|
||||
|
||||
/*!
|
||||
* Create a 1D Array with the given initializer list
|
||||
* @param data Input data
|
||||
*/
|
||||
Array( std::initializer_list<TYPE> data );
|
||||
|
||||
|
||||
/*!
|
||||
* Copy constructor
|
||||
* @param rhs Array to copy
|
||||
@@ -109,7 +346,7 @@ public:
|
||||
*/
|
||||
Array &operator=( const std::vector<TYPE> &rhs );
|
||||
|
||||
|
||||
public: // Views/copies/subset
|
||||
/*!
|
||||
* Create a 1D Array view to a raw block of data
|
||||
* @param N Number of elements in the array
|
||||
@@ -141,8 +378,7 @@ public:
|
||||
* @param N Number of elements in each dimension
|
||||
* @param data Pointer to the data
|
||||
*/
|
||||
static std::shared_ptr<Array> view(
|
||||
const std::vector<size_t> &N, std::shared_ptr<TYPE> const &data );
|
||||
static std::shared_ptr<Array> view( const ArraySize &N, std::shared_ptr<TYPE> const &data );
|
||||
|
||||
|
||||
/*!
|
||||
@@ -178,7 +414,7 @@ public:
|
||||
* @param data Pointer to the data
|
||||
*/
|
||||
static std::shared_ptr<const Array> constView(
|
||||
const std::vector<size_t> &N, std::shared_ptr<const TYPE> const &data );
|
||||
const ArraySize &N, std::shared_ptr<const TYPE> const &data );
|
||||
|
||||
|
||||
/*!
|
||||
@@ -192,7 +428,20 @@ public:
|
||||
* @param N Number of elements in each dimension
|
||||
* @param data Pointer to the data
|
||||
*/
|
||||
void view2( const std::vector<size_t> &N, std::shared_ptr<TYPE> const &data );
|
||||
void view2( const ArraySize &N, std::shared_ptr<TYPE> const &data );
|
||||
|
||||
/*!
|
||||
* Make this object a view of the raw data (expert use only).
|
||||
* Use view2( N, std::shared_ptr(data,[](TYPE*){}) ) instead.
|
||||
* Note: this interface is not recommended as it does not protect from
|
||||
* the src data being deleted while still being used by the Array.
|
||||
* Additionally for maximum performance it does not set the internal shared_ptr
|
||||
* so functions like getPtr and resize will not work correctly.
|
||||
* @param ndim Number of dimensions
|
||||
* @param dims Number of elements in each dimension
|
||||
* @param data Pointer to the data
|
||||
*/
|
||||
void viewRaw( int ndim, const size_t *dims, TYPE *data );
|
||||
|
||||
/*!
|
||||
* Make this object a view of the raw data (expert use only).
|
||||
@@ -204,26 +453,14 @@ public:
|
||||
* @param N Number of elements in each dimension
|
||||
* @param data Pointer to the data
|
||||
*/
|
||||
void viewRaw( const std::initializer_list<size_t> &N, TYPE *data );
|
||||
|
||||
/*!
|
||||
* Make this object a view of the raw data (expert use only).
|
||||
* Use view2( N, std::shared_ptr(data,[](TYPE*){}) ) instead.
|
||||
* Note: this interface is not recommended as it does not protect from
|
||||
* the src data being deleted while still being used by the Array.
|
||||
* Additionally for maximum performance it does not set the internal shared_ptr
|
||||
* so functions like getPtr and resize will not work correctly.
|
||||
* @param N Number of elements in each dimension
|
||||
* @param data Pointer to the data
|
||||
*/
|
||||
void viewRaw( const std::vector<size_t> &N, TYPE *data );
|
||||
void viewRaw( const ArraySize &N, TYPE *data );
|
||||
|
||||
/*!
|
||||
* Convert an array of one type to another. This may or may not allocate new memory.
|
||||
* @param array Input array
|
||||
*/
|
||||
template<class TYPE2>
|
||||
static std::shared_ptr<Array<TYPE2>> convert( std::shared_ptr<Array<TYPE>> array );
|
||||
static std::shared_ptr<Array<TYPE2>> convert( std::shared_ptr<Array<TYPE, FUN>> array );
|
||||
|
||||
|
||||
/*!
|
||||
@@ -231,7 +468,8 @@ public:
|
||||
* @param array Input array
|
||||
*/
|
||||
template<class TYPE2>
|
||||
static std::shared_ptr<const Array<TYPE2>> convert( std::shared_ptr<const Array<TYPE>> array );
|
||||
static std::shared_ptr<const Array<TYPE2>> convert(
|
||||
std::shared_ptr<const Array<TYPE, FUN>> array );
|
||||
|
||||
|
||||
/*!
|
||||
@@ -256,6 +494,13 @@ public:
|
||||
template<class TYPE2>
|
||||
void copyTo( TYPE2 *array ) const;
|
||||
|
||||
/*!
|
||||
* Copy and convert data from this array to a raw vector.
|
||||
* @param array Source array
|
||||
*/
|
||||
template<class TYPE2>
|
||||
Array<TYPE2, FUN> cloneTo() const;
|
||||
|
||||
|
||||
/*!
|
||||
* Fill the array with the given value
|
||||
@@ -274,7 +519,7 @@ public:
|
||||
* @param base Base array
|
||||
* @param exp Exponent value
|
||||
*/
|
||||
void pow( const Array<TYPE> &baseArray, const TYPE &exp );
|
||||
void pow( const Array<TYPE, FUN> &base, const TYPE &exp );
|
||||
|
||||
//! Destructor
|
||||
~Array();
|
||||
@@ -285,23 +530,27 @@ public:
|
||||
|
||||
|
||||
//! Return the size of the Array
|
||||
inline int ndim() const { return d_ndim; }
|
||||
inline int ndim() const { return d_size.ndim(); }
|
||||
|
||||
|
||||
//! Return the size of the Array
|
||||
inline std::vector<size_t> size() const { return std::vector<size_t>( d_N, d_N + d_ndim ); }
|
||||
inline ArraySize &size() { return d_size; }
|
||||
|
||||
|
||||
//! Return the size of the Array
|
||||
inline size_t size( int d ) const { return d_N[d]; }
|
||||
inline ArraySize size() const { return d_size; }
|
||||
|
||||
|
||||
//! Return the size of the Array
|
||||
inline size_t length() const { return d_length; }
|
||||
inline size_t size( int d ) const { return d_size[d]; }
|
||||
|
||||
|
||||
//! Return the size of the Array
|
||||
inline size_t length() const { return d_size.length(); }
|
||||
|
||||
|
||||
//! Return true if the Array is empty
|
||||
inline bool empty() const { return d_length == 0; }
|
||||
inline bool empty() const { return d_size.length() == 0; }
|
||||
|
||||
|
||||
/*!
|
||||
@@ -329,7 +578,8 @@ public:
|
||||
* Resize the Array
|
||||
* @param N Number of elements in each dimension
|
||||
*/
|
||||
void resize( const std::vector<size_t> &N );
|
||||
void resize( const ArraySize &N );
|
||||
|
||||
|
||||
/*!
|
||||
* Resize the given dimension of the array
|
||||
@@ -344,7 +594,7 @@ public:
|
||||
* Reshape the Array (total size of array will not change)
|
||||
* @param N Number of elements in each dimension
|
||||
*/
|
||||
void reshape( const std::vector<size_t> &N );
|
||||
void reshape( const ArraySize &N );
|
||||
|
||||
|
||||
/*!
|
||||
@@ -352,7 +602,16 @@ public:
|
||||
* @param index Index to subset (imin,imax,jmin,jmax,kmin,kmax,...)
|
||||
*/
|
||||
template<class TYPE2 = TYPE>
|
||||
Array<TYPE2> subset( const std::vector<size_t> &index ) const;
|
||||
Array<TYPE2, FUN> subset( const std::vector<size_t> &index ) const;
|
||||
|
||||
|
||||
/*!
|
||||
* Subset the Array (total size of array will not change)
|
||||
* @param index Index to subset (ix:kx:jx,iy:ky:jy,...)
|
||||
*/
|
||||
template<class TYPE2 = TYPE>
|
||||
Array<TYPE2, FUN> subset( const std::vector<Range<size_t>> &index ) const;
|
||||
|
||||
|
||||
/*!
|
||||
* Copy data from an array into a subset of this array
|
||||
@@ -360,32 +619,48 @@ public:
|
||||
* @param subset The subset array to copy from
|
||||
*/
|
||||
template<class TYPE2>
|
||||
void copySubset( const std::vector<size_t> &index, const Array<TYPE2> &subset );
|
||||
void copySubset( const std::vector<size_t> &index, const Array<TYPE2, FUN> &subset );
|
||||
|
||||
/*!
|
||||
* Copy data from an array into a subset of this array
|
||||
* @param index Index of the subset
|
||||
* @param subset The subset array to copy from
|
||||
*/
|
||||
template<class TYPE2>
|
||||
void copySubset( const std::vector<Range<size_t>> &index, const Array<TYPE2, FUN> &subset );
|
||||
|
||||
/*!
|
||||
* Add data from an array into a subset of this array
|
||||
* @param index Index of the subset (imin,imax,jmin,jmax,kmin,kmax,...)
|
||||
* @param subset The subset array to add from
|
||||
*/
|
||||
void addSubset( const std::vector<size_t> &index, const Array<TYPE> &subset );
|
||||
void addSubset( const std::vector<size_t> &index, const Array<TYPE, FUN> &subset );
|
||||
|
||||
/*!
|
||||
* Add data from an array into a subset of this array
|
||||
* @param index Index of the subset
|
||||
* @param subset The subset array to add from
|
||||
*/
|
||||
void addSubset( const std::vector<Range<size_t>> &index, const Array<TYPE, FUN> &subset );
|
||||
|
||||
|
||||
public: // Accessors
|
||||
/*!
|
||||
* Access the desired element
|
||||
* @param i The row index
|
||||
*/
|
||||
HOST_DEVICE inline TYPE &operator()( size_t i )
|
||||
HOST_DEVICE inline TYPE &operator()( size_t i ) ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX3D( d_N, i, 0, 0 ) return d_data[i];
|
||||
return d_data[d_size.index( i )];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Access the desired element
|
||||
* @param i The row index
|
||||
*/
|
||||
HOST_DEVICE inline const TYPE &operator()( size_t i ) const
|
||||
HOST_DEVICE inline const TYPE &operator()( size_t i ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX3D( d_N, i, 0, 0 ) return d_data[i];
|
||||
return d_data[d_size.index( i )];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -393,9 +668,9 @@ public:
|
||||
* @param i The row index
|
||||
* @param j The column index
|
||||
*/
|
||||
HOST_DEVICE inline TYPE &operator()( size_t i, size_t j )
|
||||
HOST_DEVICE inline TYPE &operator()( size_t i, size_t j ) ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX3D( d_N, i, j, 0 ) return d_data[i + j * d_N[0]];
|
||||
return d_data[d_size.index( i, j )];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -403,9 +678,9 @@ public:
|
||||
* @param i The row index
|
||||
* @param j The column index
|
||||
*/
|
||||
HOST_DEVICE inline const TYPE &operator()( size_t i, size_t j ) const
|
||||
HOST_DEVICE inline const TYPE &operator()( size_t i, size_t j ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX3D( d_N, i, j, 0 ) return d_data[i + j * d_N[0]];
|
||||
return d_data[d_size.index( i, j )];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -414,9 +689,9 @@ public:
|
||||
* @param j The column index
|
||||
* @param k The third index
|
||||
*/
|
||||
HOST_DEVICE inline TYPE &operator()( size_t i, size_t j, size_t k )
|
||||
HOST_DEVICE inline TYPE &operator()( size_t i, size_t j, size_t k ) ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX3D( d_N, i, j, k ) return d_data[GET_ARRAY_INDEX3D( d_N, i, j, k )];
|
||||
return d_data[d_size.index( i, j, k )];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -425,35 +700,109 @@ public:
|
||||
* @param j The column index
|
||||
* @param k The third index
|
||||
*/
|
||||
HOST_DEVICE inline const TYPE &operator()( size_t i, size_t j, size_t k ) const
|
||||
HOST_DEVICE inline const TYPE &operator()( size_t i, size_t j, size_t k ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX3D( d_N, i, j, k ) return d_data[GET_ARRAY_INDEX3D( d_N, i, j, k )];
|
||||
return d_data[d_size.index( i, j, k )];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Access the desired element
|
||||
* @param i The row index
|
||||
* @param j The column index
|
||||
* @param k The third index
|
||||
* @param l The fourth index
|
||||
* @param i1 The first index
|
||||
* @param i2 The second index
|
||||
* @param i3 The third index
|
||||
* @param i4 The fourth index
|
||||
*/
|
||||
HOST_DEVICE inline TYPE &operator()( size_t i, size_t j, size_t k, size_t l )
|
||||
HOST_DEVICE inline TYPE &operator()(
|
||||
size_t i1, size_t i2, size_t i3, size_t i4 ) ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX4D( d_N, i, j, k, l ) return d_data[GET_ARRAY_INDEX4D( d_N, i, j, k, l )];
|
||||
return d_data[d_size.index( i1, i2, i3, i4 )];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Access the desired element
|
||||
* @param i The row index
|
||||
* @param j The column index
|
||||
* @param k The third index
|
||||
* @param l The fourth index
|
||||
* @param i1 The first index
|
||||
* @param i2 The second index
|
||||
* @param i3 The third index
|
||||
* @param i4 The fourth index
|
||||
*/
|
||||
HOST_DEVICE inline const TYPE &operator()( size_t i, size_t j, size_t k, size_t l ) const
|
||||
HOST_DEVICE inline const TYPE &operator()(
|
||||
size_t i1, size_t i2, size_t i3, size_t i4 ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
CHECK_ARRAY_INDEX4D( d_N, i, j, k, l ) return d_data[GET_ARRAY_INDEX4D( d_N, i, j, k, l )];
|
||||
return d_data[d_size.index( i1, i2, i3, i4 )];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Access the desired element
|
||||
* @param i1 The first index
|
||||
* @param i2 The second index
|
||||
* @param i3 The third index
|
||||
* @param i4 The fourth index
|
||||
* @param i5 The fifth index
|
||||
*/
|
||||
HOST_DEVICE inline TYPE &operator()(
|
||||
size_t i1, size_t i2, size_t i3, size_t i4, size_t i5 ) ATTRIBUTE_INLINE
|
||||
{
|
||||
return d_data[d_size.index( i1, i2, i3, i4, i5 )];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Access the desired element
|
||||
* @param i1 The first index
|
||||
* @param i2 The second index
|
||||
* @param i3 The third index
|
||||
* @param i4 The fourth index
|
||||
* @param i5 The fifth index
|
||||
*/
|
||||
HOST_DEVICE inline const TYPE &operator()(
|
||||
size_t i1, size_t i2, size_t i3, size_t i4, size_t i5 ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
return d_data[d_size.index( i1, i2, i3, i4, i5 )];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Access the desired element as a raw pointer
|
||||
* @param i The global index
|
||||
*/
|
||||
HOST_DEVICE inline TYPE *ptr( size_t i ) ATTRIBUTE_INLINE
|
||||
{
|
||||
return i >= d_size.length() ? nullptr : &d_data[i];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Access the desired element as a raw pointer
|
||||
* @param i The global index
|
||||
*/
|
||||
HOST_DEVICE inline const TYPE *ptr( size_t i ) const ATTRIBUTE_INLINE
|
||||
{
|
||||
return i >= d_size.length() ? nullptr : &d_data[i];
|
||||
}
|
||||
|
||||
//! Get iterator to beginning of data
|
||||
inline TYPE *begin() ATTRIBUTE_INLINE { return d_data; }
|
||||
|
||||
//! Get iterator to beginning of data
|
||||
inline const TYPE *begin() const ATTRIBUTE_INLINE { return d_data; }
|
||||
|
||||
//! Get iterator to beginning of data
|
||||
inline TYPE *end() ATTRIBUTE_INLINE { return d_data + d_size.length(); }
|
||||
|
||||
//! Get iterator to beginning of data
|
||||
inline const TYPE *end() const ATTRIBUTE_INLINE { return d_data + d_size.length(); }
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
inline std::shared_ptr<TYPE> getPtr() ATTRIBUTE_INLINE { return d_ptr; }
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
inline std::shared_ptr<const TYPE> getPtr() const ATTRIBUTE_INLINE { return d_ptr; }
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
HOST_DEVICE inline TYPE *data() ATTRIBUTE_INLINE { return d_data; }
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
HOST_DEVICE inline const TYPE *data() const ATTRIBUTE_INLINE { return d_data; }
|
||||
|
||||
|
||||
public: // Operator overloading
|
||||
//! Check if two matrices are equal
|
||||
// Equality means the dimensions and data have to be identical
|
||||
bool operator==( const Array &rhs ) const;
|
||||
@@ -461,19 +810,28 @@ public:
|
||||
//! Check if two matrices are not equal
|
||||
inline bool operator!=( const Array &rhs ) const { return !this->operator==( rhs ); }
|
||||
|
||||
//! Add another array
|
||||
Array &operator+=( const Array &rhs );
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
inline std::shared_ptr<TYPE> getPtr() { return d_ptr; }
|
||||
//! Subtract another array
|
||||
Array &operator-=( const Array &rhs );
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
inline std::shared_ptr<const TYPE> getPtr() const { return d_ptr; }
|
||||
//! Add a scalar
|
||||
Array &operator+=( const TYPE &rhs );
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
HOST_DEVICE inline TYPE *data() { return d_data; }
|
||||
//! Subtract a scalar
|
||||
Array &operator-=( const TYPE &rhs );
|
||||
|
||||
//! Return the pointer to the raw data
|
||||
HOST_DEVICE inline const TYPE *data() const { return d_data; }
|
||||
|
||||
public: // Math operations
|
||||
//! Concatenates the arrays along the dimension dim.
|
||||
static Array cat( const std::vector<Array> &x, int dim = 0 );
|
||||
|
||||
//! Concatenates a given array with the current array
|
||||
void cat( const Array &x, int dim = 0 );
|
||||
|
||||
//! Initialize the array with random values (defined from the function table)
|
||||
void rand();
|
||||
|
||||
//! Return true if NaNs are present
|
||||
inline bool NaNs() const;
|
||||
@@ -491,13 +849,13 @@ public:
|
||||
inline TYPE mean() const;
|
||||
|
||||
//! Return the min of all elements in a given direction
|
||||
Array<TYPE> min( int dir ) const;
|
||||
Array<TYPE, FUN> min( int dir ) const;
|
||||
|
||||
//! Return the max of all elements in a given direction
|
||||
Array<TYPE> max( int dir ) const;
|
||||
Array<TYPE, FUN> max( int dir ) const;
|
||||
|
||||
//! Return the sum of all elements in a given direction
|
||||
Array<TYPE> sum( int dir ) const;
|
||||
Array<TYPE, FUN> sum( int dir ) const;
|
||||
|
||||
//! Return the smallest value
|
||||
inline TYPE min( const std::vector<size_t> &index ) const;
|
||||
@@ -511,52 +869,86 @@ public:
|
||||
//! Return the mean of all elements
|
||||
inline TYPE mean( const std::vector<size_t> &index ) const;
|
||||
|
||||
//! Return the smallest value
|
||||
inline TYPE min( const std::vector<Range<size_t>> &index ) const;
|
||||
|
||||
//! Return the largest value
|
||||
inline TYPE max( const std::vector<Range<size_t>> &index ) const;
|
||||
|
||||
//! Return the sum of all elements
|
||||
inline TYPE sum( const std::vector<Range<size_t>> &index ) const;
|
||||
|
||||
//! Return the mean of all elements
|
||||
inline TYPE mean( const std::vector<Range<size_t>> &index ) const;
|
||||
|
||||
//! Find all elements that match the operator
|
||||
std::vector<size_t> find(
|
||||
const TYPE &value, std::function<bool( const TYPE &, const TYPE & )> compare ) const;
|
||||
|
||||
//! Add another array
|
||||
Array &operator+=( const Array &rhs );
|
||||
|
||||
//! Subtract another array
|
||||
Array &operator-=( const Array &rhs );
|
||||
|
||||
//! Add a scalar
|
||||
Array &operator+=( const TYPE &rhs );
|
||||
|
||||
//! Subtract a scalar
|
||||
Array &operator-=( const TYPE &rhs );
|
||||
|
||||
//! Print an array
|
||||
void print( std::ostream& os, const std::string& name="A", const std::string& prefix="" ) const;
|
||||
void print(
|
||||
std::ostream &os, const std::string &name = "A", const std::string &prefix = "" ) const;
|
||||
|
||||
//! Multiply two arrays
|
||||
static Array multiply( const Array &a, const Array &b );
|
||||
|
||||
//! Transpose an array
|
||||
Array<TYPE> reverseDim( ) const;
|
||||
Array<TYPE, FUN> reverseDim() const;
|
||||
|
||||
//! Replicate an array a given number of times in each direction
|
||||
Array<TYPE, FUN> repmat( const std::vector<size_t> &N ) const;
|
||||
|
||||
//! Coarsen an array using the given filter
|
||||
Array<TYPE> coarsen( const Array<TYPE>& filter ) const;
|
||||
Array<TYPE, FUN> coarsen( const Array<TYPE, FUN> &filter ) const;
|
||||
|
||||
//! Coarsen an array using the given filter
|
||||
Array<TYPE> coarsen( const std::vector<size_t>& ratio, std::function<TYPE(const Array<TYPE>&)> filter ) const;
|
||||
Array<TYPE, FUN> coarsen( const std::vector<size_t> &ratio,
|
||||
std::function<TYPE( const Array<TYPE, FUN> & )> filter ) const;
|
||||
|
||||
/*!
|
||||
* Perform a element-wise operation y = f(x)
|
||||
* @param[in] fun The function operation
|
||||
* @param[in] x The input array
|
||||
*/
|
||||
static Array transform( std::function<TYPE( const TYPE & )> fun, const Array &x );
|
||||
|
||||
/*!
|
||||
* Perform a element-wise operation z = f(x,y)
|
||||
* @param[in] fun The function operation
|
||||
* @param[in] x The first array
|
||||
* @param[in] y The second array
|
||||
*/
|
||||
static Array transform(
|
||||
std::function<TYPE( const TYPE &, const TYPE & )> fun, const Array &x, const Array &y );
|
||||
|
||||
/*!
|
||||
* axpby operation: this = alpha*x + beta*this
|
||||
* @param[in] alpha alpha
|
||||
* @param[in] x x
|
||||
* @param[in] beta beta
|
||||
*/
|
||||
void axpby( const TYPE &alpha, const Array<TYPE, FUN> &x, const TYPE &beta );
|
||||
|
||||
private:
|
||||
int d_ndim; // Number of dimensions in array
|
||||
size_t d_N[ARRAY_NDIM_MAX]; // Size of each dimension
|
||||
size_t d_length; // Total length of array
|
||||
ArraySize d_size; // Size of each dimension
|
||||
TYPE *d_data; // Raw pointer to data in array
|
||||
std::shared_ptr<TYPE> d_ptr; // Shared pointer to data in array
|
||||
void allocate( const std::vector<size_t> &N );
|
||||
void allocate( const ArraySize &N );
|
||||
|
||||
public:
|
||||
template<class TYPE2, class FUN2>
|
||||
inline bool sizeMatch( const Array<TYPE2, FUN2> &rhs ) const
|
||||
{
|
||||
return d_size == rhs.d_size;
|
||||
}
|
||||
|
||||
private:
|
||||
template<class TYPE2>
|
||||
inline bool sizeMatch( const Array<TYPE2>& rhs ) const;
|
||||
inline void checkSubsetIndex( const std::vector<size_t> &index ) const;
|
||||
inline std::array<size_t, 5> getDimArray() const;
|
||||
static inline void getSubsetArrays( const std::vector<size_t> &index,
|
||||
std::array<size_t, 5> &first, std::array<size_t, 5> &last, std::array<size_t, 5> &N );
|
||||
inline void checkSubsetIndex( const std::vector<Range<size_t>> &range ) const;
|
||||
inline std::vector<Range<size_t>> convert( const std::vector<size_t> &index ) const;
|
||||
static inline void getSubsetArrays( const std::vector<Range<size_t>> &range,
|
||||
std::array<size_t, 5> &first, std::array<size_t, 5> &last, std::array<size_t, 5> &inc,
|
||||
std::array<size_t, 5> &N );
|
||||
};
|
||||
|
||||
|
||||
|
||||
1450
common/Array.hpp
1450
common/Array.hpp
File diff suppressed because it is too large
Load Diff
81
common/FunctionTable.h
Normal file
81
common/FunctionTable.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef included_FunctionTable
|
||||
#define included_FunctionTable
|
||||
|
||||
|
||||
#include "common/Array.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
/*!
|
||||
* Class FunctionTable is a serial function table class that defines
|
||||
* a series of operations that can be performed on the Array class.
|
||||
* Users can impliment additional versions of the function table that match
|
||||
* the interface to change the behavior of the array class.
|
||||
*/
|
||||
class FunctionTable final
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Initialize the array with random values
|
||||
* @param[in] x The array to operate on
|
||||
*/
|
||||
template<class TYPE, class FUN>
|
||||
static void rand( Array<TYPE, FUN> &x );
|
||||
|
||||
/*!
|
||||
* Perform a reduce operator y = f(x)
|
||||
* @param[in] op The function operation
|
||||
* Note: the operator is a template parameter
|
||||
* (compared to a std::function to improve performance)
|
||||
* @param[in] A The array to operate on
|
||||
* @return The reduction
|
||||
*/
|
||||
template<class TYPE, class FUN, typename LAMBDA>
|
||||
static inline TYPE reduce( LAMBDA &op, const Array<TYPE, FUN> &A );
|
||||
|
||||
/*!
|
||||
* Perform a element-wise operation y = f(x)
|
||||
* @param[in] fun The function operation
|
||||
* Note: the operator is a template parameter
|
||||
* (compared to a std::function to improve performance)
|
||||
* @param[in] x The input array to operate on
|
||||
* @param[out] y The output array
|
||||
*/
|
||||
template<class TYPE, class FUN, typename LAMBDA>
|
||||
static inline void transform( LAMBDA &fun, const Array<TYPE, FUN> &x, Array<TYPE, FUN> &y );
|
||||
|
||||
/*!
|
||||
* Perform a element-wise operation z = f(x,y)
|
||||
* @param[in] fun The function operation
|
||||
* Note: the operator is a template parameter
|
||||
* (compared to a std::function to improve performance)
|
||||
* @param[in] x The first array
|
||||
* @param[in] y The second array
|
||||
* @param[out] z The result
|
||||
*/
|
||||
template<class TYPE, class FUN, typename LAMBDA>
|
||||
static inline void transform(
|
||||
LAMBDA &fun, const Array<TYPE, FUN> &x, const Array<TYPE, FUN> &y, Array<TYPE, FUN> &z );
|
||||
|
||||
/*!
|
||||
* Multiply two arrays
|
||||
* @param[in] a The first array
|
||||
* @param[in] b The second array
|
||||
* @param[out] c The output array
|
||||
*/
|
||||
template<class TYPE, class FUN>
|
||||
static void multiply(
|
||||
const Array<TYPE, FUN> &a, const Array<TYPE, FUN> &b, Array<TYPE, FUN> &c );
|
||||
|
||||
|
||||
private:
|
||||
FunctionTable();
|
||||
|
||||
template<class T>
|
||||
static inline void rand( size_t N, T *x );
|
||||
};
|
||||
|
||||
#include "common/FunctionTable.hpp"
|
||||
|
||||
#endif
|
||||
116
common/FunctionTable.hpp
Normal file
116
common/FunctionTable.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#ifndef included_FunctionTable_hpp
|
||||
#define included_FunctionTable_hpp
|
||||
|
||||
#include "common/FunctionTable.h"
|
||||
#include "common/Utilities.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
|
||||
/********************************************************
|
||||
* Random number initialization *
|
||||
********************************************************/
|
||||
template<class TYPE, class FUN>
|
||||
void FunctionTable::rand( Array<TYPE, FUN> &x )
|
||||
{
|
||||
FunctionTable::rand<TYPE>( x.length(), x.data() );
|
||||
}
|
||||
template<>
|
||||
inline void FunctionTable::rand<double>( size_t N, double *x )
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 gen( rd() );
|
||||
std::uniform_real_distribution<> dis( 0, 1 );
|
||||
for ( size_t i = 0; i < N; i++ )
|
||||
x[i] = dis( gen );
|
||||
}
|
||||
template<>
|
||||
inline void FunctionTable::rand<float>( size_t N, float *x )
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 gen( rd() );
|
||||
std::uniform_real_distribution<> dis( 0, 1 );
|
||||
for ( size_t i = 0; i < N; i++ )
|
||||
x[i] = dis( gen );
|
||||
}
|
||||
template<>
|
||||
inline void FunctionTable::rand<int>( size_t N, int *x )
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 gen( rd() );
|
||||
std::uniform_int_distribution<> dis;
|
||||
for ( size_t i = 0; i < N; i++ )
|
||||
x[i] = dis( gen );
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* Reduction *
|
||||
********************************************************/
|
||||
template<class TYPE, class FUN, typename LAMBDA>
|
||||
inline TYPE FunctionTable::reduce( LAMBDA &op, const Array<TYPE, FUN> &A )
|
||||
{
|
||||
if ( A.length() == 0 )
|
||||
return TYPE();
|
||||
const TYPE *x = A.data();
|
||||
TYPE y = x[0];
|
||||
const size_t N = A.length();
|
||||
for ( size_t i = 1; i < N; i++ )
|
||||
y = op( x[i], y );
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* Unary transformation *
|
||||
********************************************************/
|
||||
template<class TYPE, class FUN, typename LAMBDA>
|
||||
inline void FunctionTable::transform( LAMBDA &fun, const Array<TYPE, FUN> &x, Array<TYPE, FUN> &y )
|
||||
{
|
||||
y.resize( x.size() );
|
||||
const size_t N = x.length();
|
||||
for ( size_t i = 0; i < N; i++ )
|
||||
y( i ) = fun( x( i ) );
|
||||
}
|
||||
template<class TYPE, class FUN, typename LAMBDA>
|
||||
inline void FunctionTable::transform(
|
||||
LAMBDA &fun, const Array<TYPE, FUN> &x, const Array<TYPE, FUN> &y, Array<TYPE, FUN> &z )
|
||||
{
|
||||
if ( !x.sizeMatch( y ) )
|
||||
throw std::logic_error( "Sizes of x and y do not match" );
|
||||
z.resize( x.size() );
|
||||
const size_t N = x.length();
|
||||
for ( size_t i = 0; i < N; i++ )
|
||||
z( i ) = fun( x( i ), y( i ) );
|
||||
}
|
||||
|
||||
|
||||
/********************************************************
|
||||
* Multiply two arrays *
|
||||
********************************************************/
|
||||
template<class TYPE, class FUN>
|
||||
void FunctionTable::multiply(
|
||||
const Array<TYPE, FUN> &a, const Array<TYPE, FUN> &b, Array<TYPE, FUN> &c )
|
||||
{
|
||||
if ( a.ndim() <= 2 && b.ndim() <= 2 ) {
|
||||
if ( a.size( 1 ) != b.size( 0 ) )
|
||||
throw std::logic_error( "Inner dimensions must match" );
|
||||
c.resize( a.size( 0 ), b.size( 1 ) );
|
||||
c.fill( 0 );
|
||||
for ( size_t k = 0; k < b.size( 1 ); k++ ) {
|
||||
for ( size_t j = 0; j < a.size( 1 ); j++ ) {
|
||||
for ( size_t i = 0; i < a.size( 0 ); i++ ) {
|
||||
c( i, k ) += a( i, j ) * b( j, k );
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw std::logic_error( "Not finished yet" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,11 @@
|
||||
#ifndef included_AtomicStackTrace
|
||||
#define included_AtomicStackTrace
|
||||
#ifndef included_StackTrace
|
||||
#define included_StackTrace
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
|
||||
// Check for and include MPI
|
||||
@@ -39,12 +36,16 @@ struct stack_info {
|
||||
int line;
|
||||
//! Default constructor
|
||||
stack_info() : address( nullptr ), address2( nullptr ), line( 0 ) {}
|
||||
//! Reset the stack
|
||||
void clear();
|
||||
//! Operator==
|
||||
bool operator==( const stack_info &rhs ) const;
|
||||
//! Operator!=
|
||||
bool operator!=( const stack_info &rhs ) const;
|
||||
//! Get the minimum width to print the addresses
|
||||
int getAddressWidth() const;
|
||||
//! Print the stack info
|
||||
std::string print() const;
|
||||
std::string print( int widthAddress = 16, int widthObject = 20, int widthFunction = 32 ) const;
|
||||
//! Compute the number of bytes needed to store the object
|
||||
size_t size() const;
|
||||
//! Pack the data to a byte array, returning a pointer to the end of the data
|
||||
@@ -59,15 +60,27 @@ struct stack_info {
|
||||
|
||||
|
||||
struct multi_stack_info {
|
||||
int N;
|
||||
stack_info stack;
|
||||
std::vector<multi_stack_info> children;
|
||||
int N; // Number of threads/processes
|
||||
stack_info stack; // Current stack item
|
||||
std::vector<multi_stack_info> children; // Children
|
||||
//! Default constructor
|
||||
multi_stack_info() : N( 0 ) {}
|
||||
//! Construct from a simple call stack
|
||||
explicit multi_stack_info( const std::vector<stack_info> & );
|
||||
//! Copy constructor from a simple call stack
|
||||
multi_stack_info &operator=( const std::vector<stack_info> & );
|
||||
//! Reset the stack
|
||||
void clear();
|
||||
//! Add the given stack to the multistack
|
||||
void add( size_t N, const stack_info *stack );
|
||||
void add( size_t len, const stack_info *stack );
|
||||
//! Print the stack info
|
||||
std::vector<std::string> print( const std::string &prefix = std::string() ) const;
|
||||
|
||||
private:
|
||||
void print2( const std::string &prefix, int w[3], std::vector<std::string> &text ) const;
|
||||
int getAddressWidth() const;
|
||||
int getObjectWidth() const;
|
||||
int getFunctionWidth() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -110,6 +123,16 @@ multi_stack_info getAllCallStacks( );
|
||||
multi_stack_info getGlobalCallStacks();
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Clean up the stack trace
|
||||
* @details This function modifies the stack trace to remove entries
|
||||
* related to acquiring the stack trace in an attempt to make it
|
||||
* more useful for display/users.
|
||||
* @param[in,out] stack The stack trace to modify
|
||||
*/
|
||||
void cleanupStackTrace( multi_stack_info &stack );
|
||||
|
||||
|
||||
//! Function to return the current call stack for the current thread
|
||||
std::vector<void *> backtrace();
|
||||
|
||||
@@ -136,8 +159,9 @@ std::string signalName( int signal );
|
||||
* Return the symbols from the current executable (not availible for all platforms)
|
||||
* @return Returns 0 if sucessful
|
||||
*/
|
||||
int getSymbols(
|
||||
std::vector<void *> &address, std::vector<char> &type, std::vector<std::string> &obj );
|
||||
int getSymbols( std::vector<void *> &address,
|
||||
std::vector<char> &type,
|
||||
std::vector<std::string> &obj );
|
||||
|
||||
|
||||
/*!
|
||||
@@ -159,14 +183,15 @@ enum class terminateType { signal, exception };
|
||||
|
||||
/*!
|
||||
* Set the error handlers
|
||||
* @param[in] Function to terminate the program: abort(msg,type)
|
||||
* @param[in] abort Function to terminate the program: abort(msg,type)
|
||||
*/
|
||||
void setErrorHandlers( std::function<void( std::string, terminateType )> abort );
|
||||
|
||||
|
||||
/*!
|
||||
* Set the given signals to the handler
|
||||
* @param[in] Function to terminate the program: abort(msg,type)
|
||||
* @param[in] signals Signals to handle
|
||||
* @param[in] handler Function to terminate the program: abort(msg,type)
|
||||
*/
|
||||
void setSignals( const std::vector<int> &signals, void ( *handler )( int ) );
|
||||
|
||||
@@ -213,4 +238,5 @@ std::string exec( const std::string& cmd, int& exit_code );
|
||||
|
||||
} // namespace StackTrace
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,37 +1,99 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "common/UnitTest.h"
|
||||
#include "common/Utilities.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||
// Windows
|
||||
// Sleep is defined in milliseconds
|
||||
#else
|
||||
// Linux
|
||||
// usleep is defined in microseconds, create a Sleep command
|
||||
#define Sleep(x) usleep(x*1000)
|
||||
#endif
|
||||
|
||||
#define pout std::cout
|
||||
#define printp printf
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Empty Constructor *
|
||||
* Constructor/Destructor *
|
||||
********************************************************************/
|
||||
UnitTest::UnitTest() {
|
||||
UnitTest::UnitTest()
|
||||
{
|
||||
#ifdef USE_MPI
|
||||
comm = MPI_COMM_WORLD;
|
||||
#endif
|
||||
}
|
||||
UnitTest::~UnitTest() { reset(); }
|
||||
void UnitTest::reset()
|
||||
{
|
||||
mutex.lock();
|
||||
// Clear the data forcing a reallocation
|
||||
std::vector<std::string>().swap( pass_messages );
|
||||
std::vector<std::string>().swap( fail_messages );
|
||||
std::vector<std::string>().swap( expected_fail_messages );
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Add a pass, fail, expected failure message in a thread-safe way *
|
||||
********************************************************************/
|
||||
void UnitTest::passes( const std::string &in )
|
||||
{
|
||||
mutex.lock();
|
||||
pass_messages.push_back( in );
|
||||
mutex.unlock();
|
||||
}
|
||||
void UnitTest::failure( const std::string &in )
|
||||
{
|
||||
mutex.lock();
|
||||
fail_messages.push_back( in );
|
||||
mutex.unlock();
|
||||
}
|
||||
void UnitTest::expected_failure( const std::string &in )
|
||||
{
|
||||
mutex.lock();
|
||||
expected_fail_messages.push_back( in );
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Print a global report *
|
||||
* Note: only rank 0 will print, all messages will be aggregated *
|
||||
********************************************************************/
|
||||
void UnitTest::report(const int level0) {
|
||||
inline std::vector<int> UnitTest::allGather( int value ) const
|
||||
{
|
||||
int size = getSize();
|
||||
std::vector<int> data( size, value );
|
||||
#ifdef USE_MPI
|
||||
if ( size > 1 )
|
||||
MPI_Allgather( &value, 1, MPI_INT, data.data(), 1, MPI_INT, comm );
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
inline void UnitTest::barrier() const
|
||||
{
|
||||
#ifdef USE_MPI
|
||||
if ( getSize() > 1 )
|
||||
MPI_Barrier( comm );
|
||||
#endif
|
||||
}
|
||||
static inline void print_messages( const std::vector<std::vector<std::string>> &messages )
|
||||
{
|
||||
if ( messages.size() > 1 ) {
|
||||
for ( size_t i = 0; i < messages.size(); i++ ) {
|
||||
if ( !messages[i].empty() ) {
|
||||
printp( " Proccessor %i:\n", static_cast<int>( i ) );
|
||||
for ( const auto &j : messages[i] )
|
||||
pout << " " << j << std::endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ( const auto &j : messages[0] )
|
||||
pout << " " << j << std::endl;
|
||||
}
|
||||
}
|
||||
void UnitTest::report( const int level0 ) const
|
||||
{
|
||||
mutex.lock();
|
||||
int size = getSize();
|
||||
int rank = getRank();
|
||||
// Broadcast the print level from rank 0
|
||||
@@ -43,27 +105,15 @@ void UnitTest::report(const int level0) {
|
||||
if ( level < 0 || level > 2 )
|
||||
ERROR( "Invalid print level" );
|
||||
// Perform a global all gather to get the number of failures per processor
|
||||
std::vector<int> N_pass(size,0);
|
||||
std::vector<int> N_fail(size,0);
|
||||
std::vector<int> N_expected_fail(size,0);
|
||||
int local_pass_size = (int) pass_messages.size();
|
||||
int local_fail_size = (int) fail_messages.size();
|
||||
int local_expected_fail_size = (int) expected_fail_messages.size();
|
||||
if ( getSize() > 1 ) {
|
||||
#ifdef USE_MPI
|
||||
MPI_Allgather( &local_pass_size, 1, MPI_INT, &N_pass[0], 1, MPI_INT, comm);
|
||||
MPI_Allgather( &local_fail_size, 1, MPI_INT, &N_fail[0], 1, MPI_INT, comm);
|
||||
MPI_Allgather( &local_expected_fail_size, 1, MPI_INT, &N_expected_fail[0], 1, MPI_INT, comm);
|
||||
#endif
|
||||
} else {
|
||||
N_pass[0] = local_pass_size;
|
||||
N_fail[0] = local_fail_size;
|
||||
N_expected_fail[0] = local_expected_fail_size;
|
||||
}
|
||||
auto N_pass = allGather( pass_messages.size() );
|
||||
auto N_fail = allGather( fail_messages.size() );
|
||||
auto N_expected_fail = allGather( expected_fail_messages.size() );
|
||||
int N_pass_tot = 0;
|
||||
int N_fail_tot = 0;
|
||||
int N_expected_fail_tot = 0;
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
N_pass_tot += N_pass[i];
|
||||
N_fail_tot += N_fail[i];
|
||||
N_expected_fail_tot += N_expected_fail[i];
|
||||
}
|
||||
// Send all messages to rank 0 (if needed)
|
||||
@@ -71,158 +121,134 @@ void UnitTest::report(const int level0) {
|
||||
std::vector<std::vector<std::string>> fail_messages_rank( size );
|
||||
std::vector<std::vector<std::string>> expected_fail_rank( size );
|
||||
// Get the pass messages
|
||||
if ( ( level==1 && N_pass_tot<=20 ) || level==2 ) {
|
||||
if ( rank==0 ) {
|
||||
// Rank 0 should receive all messages
|
||||
for (int i=0; i<size; i++) {
|
||||
if ( i==0 )
|
||||
pass_messages_rank[i] = pass_messages;
|
||||
else if ( N_pass[i]>0 )
|
||||
pass_messages_rank[i] = unpack_message_stream(i,1);
|
||||
}
|
||||
} else if ( pass_messages.size() ) {
|
||||
// All other ranks send their message (use non-blocking communication)
|
||||
pack_message_stream(pass_messages,0,1);
|
||||
}
|
||||
}
|
||||
if ( ( level == 1 && N_pass_tot <= 20 ) || level == 2 )
|
||||
pass_messages_rank = UnitTest::gatherMessages( pass_messages, 1 );
|
||||
// Get the fail messages
|
||||
if ( level==1 || level==2 ) {
|
||||
if ( rank==0 ) {
|
||||
// Rank 0 should receive all messages
|
||||
for (int i=0; i<size; i++) {
|
||||
if ( i==0 )
|
||||
fail_messages_rank[i] = fail_messages;
|
||||
else if ( N_fail[i]>0 )
|
||||
fail_messages_rank[i] = unpack_message_stream(i,2);
|
||||
}
|
||||
} else if ( !fail_messages.empty() ){
|
||||
// All other ranks send their message (use non-blocking communication)
|
||||
pack_message_stream(fail_messages,0,2);
|
||||
}
|
||||
}
|
||||
if ( level == 1 || level == 2 )
|
||||
fail_messages_rank = UnitTest::gatherMessages( fail_messages, 2 );
|
||||
// Get the expected_fail messages
|
||||
if ( ( level==1 && N_expected_fail_tot<=50 ) || level==2 ) {
|
||||
if ( rank==0 ) {
|
||||
// Rank 0 should receive all messages
|
||||
for (int i=0; i<size; i++) {
|
||||
if ( i==0 )
|
||||
expected_fail_rank[i] = expected_fail_messages;
|
||||
else if ( N_expected_fail[i]>0 )
|
||||
expected_fail_rank[i] = unpack_message_stream(i,3);
|
||||
}
|
||||
} else if ( !expected_fail_messages.empty() ){
|
||||
// All other ranks send their message (use non-blocking communication)
|
||||
pack_message_stream(expected_fail_messages,0,3);
|
||||
}
|
||||
}
|
||||
if ( ( level == 1 && N_expected_fail_tot <= 50 ) || level == 2 )
|
||||
expected_fail_rank = UnitTest::gatherMessages( expected_fail_messages, 2 );
|
||||
// Print the results of all messages (only rank 0 will print)
|
||||
if ( rank == 0 ) {
|
||||
std::cout << std::endl;
|
||||
pout << std::endl;
|
||||
// Print the passed tests
|
||||
std::cout << "Tests passed" << std::endl;
|
||||
pout << "Tests passed" << std::endl;
|
||||
if ( level == 0 || ( level == 1 && N_pass_tot > 20 ) ) {
|
||||
// We want to print a summary
|
||||
if ( size > 8 ) {
|
||||
// Print 1 summary for all processors
|
||||
std::cout << " " << N_pass_tot << " tests passed (use report level 2 for more detail)" << std::endl;
|
||||
printp( " %i tests passed (use report level 2 for more detail)\n", N_pass_tot );
|
||||
} else {
|
||||
// Print a summary for each processor
|
||||
for ( int i = 0; i < size; i++ )
|
||||
std::cout << " " << N_pass[i] << " tests passed (proc " << i << ") (use report level 2 for more detail)" << std::endl;
|
||||
printp( " %i tests passed (proc %i) (use report level 2 for more detail)\n",
|
||||
N_pass[i], i );
|
||||
}
|
||||
} else {
|
||||
// We want to print all messages
|
||||
for (int i=0; i<size; i++) {
|
||||
for ( int i = 0; i < size; i++ )
|
||||
ASSERT( (int) pass_messages_rank[i].size() == N_pass[i] );
|
||||
if ( N_pass[i] > 0 ) {
|
||||
std::cout << " Proccessor " << i << ":" << std::endl;
|
||||
for (unsigned int j=0; j<pass_messages_rank[i].size(); j++)
|
||||
std::cout << " " << pass_messages_rank[i][j] << std::endl;
|
||||
print_messages( pass_messages_rank );
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
pout << std::endl;
|
||||
// Print the tests that failed
|
||||
std::cout << "Tests failed" << std::endl;
|
||||
pout << "Tests failed" << std::endl;
|
||||
if ( level == 0 ) {
|
||||
// We want to print a summary
|
||||
if ( size > 8 ) {
|
||||
// Print 1 summary for all processors
|
||||
std::cout << " " << N_pass_tot << " tests failed (use report level 2 for more detail)" << std::endl;
|
||||
printp( " %i tests failed (use report level 2 for more detail)\n", N_fail_tot );
|
||||
} else {
|
||||
// Print a summary for each processor
|
||||
for ( int i = 0; i < size; i++ )
|
||||
std::cout << " " << N_fail[i] << " tests failed (proc " << i << ") (use report level 1 or 2 for more detail)" << std::endl;
|
||||
printp( " %i tests failed (proc %i) (use report level 2 for more detail)\n",
|
||||
N_fail[i], i );
|
||||
}
|
||||
} else {
|
||||
// We want to print all messages
|
||||
for (int i=0; i<size; i++) {
|
||||
for ( int i = 0; i < size; i++ )
|
||||
ASSERT( (int) fail_messages_rank[i].size() == N_fail[i] );
|
||||
if ( N_fail[i] > 0 ) {
|
||||
std::cout << " Processor " << i << ":" << std::endl;
|
||||
for (unsigned int j=0; j<fail_messages_rank[i].size(); j++)
|
||||
std::cout << " " << fail_messages_rank[i][j] << std::endl;
|
||||
print_messages( fail_messages_rank );
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
pout << std::endl;
|
||||
// Print the tests that expected failed
|
||||
std::cout << "Tests expected failed" << std::endl;
|
||||
pout << "Tests expected failed" << std::endl;
|
||||
if ( level == 0 || ( level == 1 && N_expected_fail_tot > 50 ) ) {
|
||||
// We want to print a summary
|
||||
if ( size > 8 ) {
|
||||
// Print 1 summary for all processors
|
||||
std::cout << " " << N_expected_fail_tot << " tests expected failed (use report level 2 for more detail)" << std::endl;
|
||||
printp( " %i tests expected failed (use report level 2 for more detail)\n",
|
||||
N_expected_fail_tot );
|
||||
} else {
|
||||
// Print a summary for each processor
|
||||
for ( int i = 0; i < size; i++ )
|
||||
std::cout << " " << N_expected_fail[i] << " tests expected failed (proc " << i << ") (use report level 1 or 2 for more detail)" << std::endl;
|
||||
printp( " %i tests expected failed (proc %i) (use report level 2 for more "
|
||||
"detail)\n",
|
||||
N_expected_fail[i], i );
|
||||
}
|
||||
} else {
|
||||
// We want to print all messages
|
||||
for (int i=0; i<size; i++) {
|
||||
for ( int i = 0; i < size; i++ )
|
||||
ASSERT( (int) expected_fail_rank[i].size() == N_expected_fail[i] );
|
||||
if ( N_expected_fail[i] > 0 ) {
|
||||
std::cout << " Processor " << i << ":" << std::endl;
|
||||
for (unsigned int j=0; j<expected_fail_rank[i].size(); j++)
|
||||
std::cout << " " << expected_fail_rank[i][j] << std::endl;
|
||||
print_messages( expected_fail_rank );
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
pout << std::endl;
|
||||
}
|
||||
// Add a barrier to synchronize all processors (rank 0 is much slower)
|
||||
#ifdef USE_MPI
|
||||
if ( getSize() > 1 )
|
||||
MPI_Barrier(comm);
|
||||
#endif
|
||||
barrier();
|
||||
Utilities::sleep_ms( 10 ); // Need a brief pause to allow any printing to finish
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Gather the messages to rank 0 *
|
||||
********************************************************************/
|
||||
std::vector<std::vector<std::string>> UnitTest::gatherMessages(
|
||||
const std::vector<std::string> &local_messages, int tag ) const
|
||||
{
|
||||
const int rank = getRank();
|
||||
const int size = getSize();
|
||||
std::vector<std::vector<std::string>> messages( size );
|
||||
if ( rank == 0 ) {
|
||||
// Rank 0 should receive all messages
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
if ( i == 0 )
|
||||
messages[i] = local_messages;
|
||||
else
|
||||
messages[i] = unpack_message_stream( i, tag );
|
||||
}
|
||||
} else {
|
||||
// All other ranks send their message (use non-blocking communication)
|
||||
pack_message_stream( local_messages, 0, tag );
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Pack and send the given messages *
|
||||
********************************************************************/
|
||||
void UnitTest::pack_message_stream(const std::vector<std::string>& messages, const int rank, const int tag)
|
||||
void UnitTest::pack_message_stream(
|
||||
const std::vector<std::string> &messages, const int rank, const int tag ) const
|
||||
{
|
||||
#ifdef USE_MPI
|
||||
// Get the size of the messages
|
||||
int N_messages = (int) messages.size();
|
||||
int *msg_size = new int[N_messages];
|
||||
auto N_messages = (int) messages.size();
|
||||
auto *msg_size = new int[N_messages];
|
||||
int msg_size_tot = 0;
|
||||
for ( int i = 0; i < N_messages; i++ ) {
|
||||
msg_size[i] = (int) messages[i].size();
|
||||
msg_size_tot += msg_size[i];
|
||||
}
|
||||
// Allocate space for the message stream
|
||||
int size_data = (N_messages+1)*sizeof(int)+msg_size_tot;
|
||||
char *data = new char[size_data];
|
||||
size_t size_data = ( N_messages + 1 ) * sizeof( int ) + msg_size_tot;
|
||||
auto *data = new char[size_data];
|
||||
// Pack the message stream
|
||||
int *tmp = (int*) data;
|
||||
tmp[0] = N_messages;
|
||||
for (int i=0; i<N_messages; i++)
|
||||
tmp[i+1] = msg_size[i];
|
||||
int k = (N_messages+1)*sizeof(int);
|
||||
memcpy( data, &N_messages, sizeof( int ) );
|
||||
memcpy( &data[sizeof( int )], msg_size, N_messages * sizeof( int ) );
|
||||
size_t k = ( N_messages + 1 ) * sizeof( int );
|
||||
for ( int i = 0; i < N_messages; i++ ) {
|
||||
messages[i].copy( &data[k], msg_size[i] );
|
||||
k += msg_size[i];
|
||||
@@ -235,14 +261,18 @@ void UnitTest::pack_message_stream(const std::vector<std::string>& messages, con
|
||||
MPI_Wait( &request, &status );
|
||||
delete[] data;
|
||||
delete[] msg_size;
|
||||
#else
|
||||
NULL_USE( messages );
|
||||
NULL_USE( rank );
|
||||
NULL_USE( tag );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* receive and unpack a message stream *
|
||||
* Receive and unpack a message stream *
|
||||
********************************************************************/
|
||||
std::vector<std::string> UnitTest::unpack_message_stream(const int rank, const int tag)
|
||||
std::vector<std::string> UnitTest::unpack_message_stream( const int rank, const int tag ) const
|
||||
{
|
||||
#ifdef USE_MPI
|
||||
// Probe the message to get the message size
|
||||
@@ -252,26 +282,32 @@ std::vector<std::string> UnitTest::unpack_message_stream(const int rank, const i
|
||||
MPI_Get_count( &status, MPI_BYTE, &size_data );
|
||||
ASSERT( size_data >= 0 );
|
||||
// Allocate memory to receive the data
|
||||
char *data = new char[size_data];
|
||||
auto *data = new char[size_data];
|
||||
// receive the data (using a non-blocking receive)
|
||||
MPI_Request request;
|
||||
MPI_Irecv( data, size_data, MPI_CHAR, rank, tag, comm, &request );
|
||||
// Wait for the communication to be received
|
||||
MPI_Wait( &request, &status );
|
||||
// Unpack the message stream
|
||||
int *tmp = (int*) data;
|
||||
int N_messages = tmp[0];
|
||||
int *msg_size = &tmp[1];
|
||||
int N_messages = 0;
|
||||
memcpy( &N_messages, data, sizeof( int ) );
|
||||
if ( N_messages == 0 ) {
|
||||
delete[] data;
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
std::vector<int> msg_size( N_messages );
|
||||
std::vector<std::string> messages( N_messages );
|
||||
memcpy( msg_size.data(), &data[sizeof( int )], N_messages * sizeof( int ) );
|
||||
int k = ( N_messages + 1 ) * sizeof( int );
|
||||
for ( int i = 0; i < N_messages; i++ ) {
|
||||
messages[i] = std::string( &data[k], msg_size[i] );
|
||||
k += msg_size[i];
|
||||
}
|
||||
// Delete the temporary memory
|
||||
delete[] data;
|
||||
return messages;
|
||||
#else
|
||||
NULL_USE( rank );
|
||||
NULL_USE( tag );
|
||||
return std::vector<std::string>();
|
||||
#endif
|
||||
}
|
||||
@@ -280,7 +316,7 @@ std::vector<std::string> UnitTest::unpack_message_stream(const int rank, const i
|
||||
/********************************************************************
|
||||
* Other functions *
|
||||
********************************************************************/
|
||||
int UnitTest::getRank()
|
||||
int UnitTest::getRank() const
|
||||
{
|
||||
int rank = 0;
|
||||
#ifdef USE_MPI
|
||||
@@ -291,7 +327,7 @@ int UnitTest::getRank()
|
||||
#endif
|
||||
return rank;
|
||||
}
|
||||
int UnitTest::getSize()
|
||||
int UnitTest::getSize() const
|
||||
{
|
||||
int size = 1;
|
||||
#ifdef USE_MPI
|
||||
@@ -302,12 +338,12 @@ int UnitTest::getSize()
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
size_t UnitTest::NumPassGlobal()
|
||||
size_t UnitTest::NumPassGlobal() const
|
||||
{
|
||||
size_t num = pass_messages.size();
|
||||
#ifdef USE_MPI
|
||||
if ( getSize() > 1 ) {
|
||||
int send = static_cast<int>(num);
|
||||
auto send = static_cast<int>( num );
|
||||
int sum = 0;
|
||||
MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm );
|
||||
num = static_cast<size_t>( sum );
|
||||
@@ -315,12 +351,12 @@ size_t UnitTest::NumPassGlobal()
|
||||
#endif
|
||||
return num;
|
||||
}
|
||||
size_t UnitTest::NumFailGlobal()
|
||||
size_t UnitTest::NumFailGlobal() const
|
||||
{
|
||||
size_t num = fail_messages.size();
|
||||
#ifdef USE_MPI
|
||||
if ( getSize() > 1 ) {
|
||||
int send = static_cast<int>(num);
|
||||
auto send = static_cast<int>( num );
|
||||
int sum = 0;
|
||||
MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm );
|
||||
num = static_cast<size_t>( sum );
|
||||
@@ -328,12 +364,12 @@ size_t UnitTest::NumFailGlobal()
|
||||
#endif
|
||||
return num;
|
||||
}
|
||||
size_t UnitTest::NumExpectedFailGlobal()
|
||||
size_t UnitTest::NumExpectedFailGlobal() const
|
||||
{
|
||||
size_t num = expected_fail_messages.size();
|
||||
#ifdef USE_MPI
|
||||
if ( getSize() > 1 ) {
|
||||
int send = static_cast<int>(num);
|
||||
auto send = static_cast<int>( num );
|
||||
int sum = 0;
|
||||
MPI_Allreduce( &send, &sum, 1, MPI_INT, MPI_SUM, comm );
|
||||
num = static_cast<size_t>( sum );
|
||||
@@ -341,5 +377,3 @@ size_t UnitTest::NumExpectedFailGlobal()
|
||||
#endif
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#ifndef included_UnitTest
|
||||
#define included_UnitTest
|
||||
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#ifdef USE_MPI
|
||||
#include "mpi.h"
|
||||
#endif
|
||||
@@ -27,78 +28,92 @@
|
||||
* \endcode
|
||||
|
||||
*/
|
||||
class UnitTest {
|
||||
class UnitTest
|
||||
{
|
||||
public:
|
||||
|
||||
//! Constructor
|
||||
UnitTest();
|
||||
|
||||
//! Indicate a passed test
|
||||
virtual void passes (const std::string &in) { pass_messages.push_back(in); }
|
||||
//! Destructor
|
||||
virtual ~UnitTest();
|
||||
|
||||
//! Indicate a failed test
|
||||
virtual void failure (const std::string &in) { fail_messages.push_back(in); }
|
||||
//! Indicate a passed test (thread-safe)
|
||||
virtual void passes( const std::string &in );
|
||||
|
||||
//! Indicate an expected failed test
|
||||
virtual void expected_failure (const std::string &in) { expected_fail_messages.push_back(in); }
|
||||
//! Indicate a failed test (thread-safe)
|
||||
virtual void failure( const std::string &in );
|
||||
|
||||
//! Indicate an expected failed test (thread-safe)
|
||||
virtual void expected_failure( const std::string &in );
|
||||
|
||||
//! Return the number of passed tests locally
|
||||
virtual size_t NumPassLocal () { return pass_messages.size(); }
|
||||
virtual size_t NumPassLocal() const { return pass_messages.size(); }
|
||||
|
||||
//! Return the number of failed tests locally
|
||||
virtual size_t NumFailLocal () { return fail_messages.size(); }
|
||||
virtual size_t NumFailLocal() const { return fail_messages.size(); }
|
||||
|
||||
//! Return the number of expected failed tests locally
|
||||
virtual size_t NumExpectedFailLocal () { return expected_fail_messages.size(); }
|
||||
virtual size_t NumExpectedFailLocal() const { return expected_fail_messages.size(); }
|
||||
|
||||
//! Return the number of passed tests locally
|
||||
virtual size_t NumPassGlobal ();
|
||||
virtual size_t NumPassGlobal() const;
|
||||
|
||||
//! Return the number of failed tests locally
|
||||
virtual size_t NumFailGlobal ();
|
||||
virtual size_t NumFailGlobal() const;
|
||||
|
||||
//! Return the number of expected failed tests locally
|
||||
virtual size_t NumExpectedFailGlobal ();
|
||||
virtual size_t NumExpectedFailGlobal() const;
|
||||
|
||||
//! Return the rank of the current processor
|
||||
int getRank ();
|
||||
int getRank() const;
|
||||
|
||||
//! Return the number of processors
|
||||
int getSize ();
|
||||
int getSize() const;
|
||||
|
||||
/*!
|
||||
* Print a report of the passed and failed tests.
|
||||
* Note: This is a blocking call that all processors must execute together.
|
||||
* Note: Only rank 0 will print the messages (this is necessary as other ranks may not be able to print correctly).
|
||||
* Note: Only rank 0 will print the messages (this is necessary as other ranks may not be able
|
||||
* to print correctly).
|
||||
* @param level Optional integer specifying the level of reporting (default: 1)
|
||||
* 0: Report the number of tests passed, failed, and expected failures.
|
||||
* 1: Report the number of passed tests (if <=20) or the number passed otherwise,
|
||||
* report all failures,
|
||||
* report the number of expected failed tests (if <=50) or the number passed otherwise.
|
||||
* 1: Report the number of passed tests (if <=20) or the number passed
|
||||
* otherwise, report all failures, report the number of expected
|
||||
* failed tests (if <=50) or the number passed otherwise.
|
||||
* 2: Report all passed, failed, and expected failed tests.
|
||||
*/
|
||||
virtual void report(const int level=1);
|
||||
virtual void report( const int level = 1 ) const;
|
||||
|
||||
//! Clear the messages
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
std::vector<std::string> pass_messages;
|
||||
std::vector<std::string> fail_messages;
|
||||
std::vector<std::string> expected_fail_messages;
|
||||
mutable std::mutex mutex;
|
||||
#ifdef USE_MPI
|
||||
MPI_Comm comm;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Make the copy constructor private
|
||||
UnitTest(const UnitTest& p) {}
|
||||
UnitTest( const UnitTest & ) {}
|
||||
|
||||
// Function to pack the messages into a single data stream and send to the given processor
|
||||
// Note: This function does not return until the message stream has been sent
|
||||
void pack_message_stream(const std::vector<std::string>& messages, const int rank, const int tag);
|
||||
void pack_message_stream(
|
||||
const std::vector<std::string> &messages, const int rank, const int tag ) const;
|
||||
|
||||
// Function to unpack the messages from a single data stream
|
||||
// Note: This function does not return until the message stream has been received
|
||||
std::vector<std::string> unpack_message_stream(const int rank, const int tag);
|
||||
std::vector<std::string> unpack_message_stream( const int rank, const int tag ) const;
|
||||
|
||||
// Helper functions
|
||||
inline void barrier() const;
|
||||
inline std::vector<int> allGather( int value ) const;
|
||||
inline std::vector<std::vector<std::string>> gatherMessages(
|
||||
const std::vector<std::string> &local_messages, int tag ) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#ifndef included_Utilities
|
||||
#define included_Utilities
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Utilities {
|
||||
|
||||
/*!
|
||||
* Utilities is a Singleton class containing basic routines for error
|
||||
* reporting, file manipulations, etc. Included are a set of \ref Macros "macros" that are commonly used.
|
||||
*/
|
||||
namespace Utilities
|
||||
{
|
||||
|
||||
/*!
|
||||
* Aborts the run after printing an error message with file and
|
||||
@@ -33,6 +33,7 @@ namespace Utilities
|
||||
//! Function to set the error handlers
|
||||
void setErrorHandlers();
|
||||
|
||||
|
||||
/*!
|
||||
* Function to get the memory availible.
|
||||
* This function will return the total memory availible
|
||||
@@ -42,6 +43,7 @@ namespace Utilities
|
||||
*/
|
||||
size_t getSystemMemory();
|
||||
|
||||
|
||||
/*!
|
||||
* Function to get the memory usage.
|
||||
* This function will return the total memory used by the application.
|
||||
@@ -55,9 +57,29 @@ namespace Utilities
|
||||
//! Function to get an arbitrary point in time
|
||||
double time();
|
||||
|
||||
|
||||
//! Function to get the resolution of time
|
||||
double tick();
|
||||
|
||||
|
||||
//! std::string version of sprintf
|
||||
inline std::string stringf( const char *format, ... );
|
||||
|
||||
|
||||
/*!
|
||||
* Sleep for X ms
|
||||
* @param N Time to sleep (ms)
|
||||
*/
|
||||
inline void sleep_ms( int N ) { std::this_thread::sleep_for( std::chrono::milliseconds( N ) ); }
|
||||
|
||||
|
||||
/*!
|
||||
* Sleep for X s
|
||||
* @param N Time to sleep (s)
|
||||
*/
|
||||
inline void sleep_s( int N ) { std::this_thread::sleep_for( std::chrono::seconds( N ) ); }
|
||||
|
||||
|
||||
//! Factor a number into it's prime factors
|
||||
std::vector<int> factor(size_t number);
|
||||
|
||||
@@ -69,6 +91,17 @@ namespace Utilities
|
||||
|
||||
#include "common/UtilityMacros.h"
|
||||
|
||||
|
||||
// stringf
|
||||
inline std::string Utilities::stringf( const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
va_start( ap, format );
|
||||
char tmp[4096];
|
||||
vsprintf( tmp, format, ap );
|
||||
va_end( ap );
|
||||
return std::string( tmp );
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -22,11 +22,17 @@
|
||||
* \details A statement that does nothing, for insure++ make it something
|
||||
* more complex than a simple C null statement to avoid a warning.
|
||||
*/
|
||||
#ifndef NULL_STATEMENT
|
||||
#ifdef __INSURE__
|
||||
#define NULL_STATEMENT do{if(0) int nullstatement=0 }}while(0)
|
||||
#define NULL_STATEMENT \
|
||||
do { \
|
||||
if ( 0 ) \
|
||||
int nullstatement = 0 \
|
||||
} while ( 0 )
|
||||
#else
|
||||
#define NULL_STATEMENT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*! \def NULL_USE(variable)
|
||||
@@ -34,9 +40,15 @@
|
||||
* \details A null use of a variable, use to avoid GNU compiler warnings about unused variables.
|
||||
* \param variable Variable to pretend to use
|
||||
*/
|
||||
#define NULL_USE(variable) do { \
|
||||
if(0) {char *temp = (char *)&variable; temp++;} \
|
||||
#ifndef NULL_USE
|
||||
#define NULL_USE( variable ) \
|
||||
do { \
|
||||
if ( 0 ) { \
|
||||
auto temp = (char *) &variable; \
|
||||
temp++; \
|
||||
} \
|
||||
} while ( 0 )
|
||||
#endif
|
||||
|
||||
|
||||
/*! \def ERROR(MSG)
|
||||
@@ -46,7 +58,8 @@
|
||||
* line number of the abort are also printed.
|
||||
* \param MSG Error message to print
|
||||
*/
|
||||
#define ERROR(MSG) do { \
|
||||
#define ERROR(MSG) \
|
||||
do { \
|
||||
::Utilities::abort( MSG, __FILE__, __LINE__ ); \
|
||||
} while ( 0 )
|
||||
|
||||
@@ -56,10 +69,12 @@
|
||||
* \details Print a warning without exit. Print file and line number of the warning.
|
||||
* \param MSG Warning message to print
|
||||
*/
|
||||
#define WARNING(MSG) do { \
|
||||
#define WARNING(MSG) \
|
||||
do { \
|
||||
std::stringstream tboxos; \
|
||||
tboxos << MSG << std::ends; \
|
||||
printf("WARNING: %s\n Warning called in %s on line %i\n",tboxos.str().c_str(),__FILE__,__LINE__); \
|
||||
printf("WARNING: %s\n Warning called in %s on line %i\n", \
|
||||
tboxos.str().c_str(),__FILE__,__LINE__); \
|
||||
}while(0)
|
||||
|
||||
|
||||
@@ -71,7 +86,8 @@
|
||||
* The file and line number of the abort are printed along with the stack trace (if availible).
|
||||
* \param EXP Expression to evaluate
|
||||
*/
|
||||
#define ASSERT(EXP) do { \
|
||||
#define ASSERT(EXP) \
|
||||
do { \
|
||||
if ( !(EXP) ) { \
|
||||
std::stringstream tboxos; \
|
||||
tboxos << "Failed assertion: " << #EXP << std::ends; \
|
||||
@@ -99,7 +115,6 @@
|
||||
}while(0)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Macro for use when assertions are to be included
|
||||
* only when debugging.
|
||||
@@ -118,6 +133,49 @@
|
||||
#endif
|
||||
|
||||
|
||||
/*! \def DISABLE_WARNINGS
|
||||
* \brief Reenable warnings
|
||||
* \details This will re-enable warnings after a call to DIASABLE_WARNINGS
|
||||
*/
|
||||
/*! \def ENABLE_WARNINGS
|
||||
* \brief Supress all warnings
|
||||
* \details This will start to supress all compile warnings.
|
||||
* Be sure to follow with ENABLE_WARNINGS
|
||||
*/
|
||||
// clang-format off
|
||||
#ifdef DISABLE_WARNINGS
|
||||
// Macros previously defined
|
||||
#elif defined( USING_MSVC )
|
||||
#define DISABLE_WARNINGS __pragma( warning( push, 0 ) )
|
||||
#define ENABLE_WARNINGS __pragma( warning( pop ) )
|
||||
#elif defined( USING_CLANG )
|
||||
#define DISABLE_WARNINGS \
|
||||
_Pragma( "clang diagnostic push" ) _Pragma( "clang diagnostic ignored \"-Wall\"" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wextra\"" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wunused-private-field\"" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wmismatched-new-delete\"" )
|
||||
#define ENABLE_WARNINGS _Pragma( "clang diagnostic pop" )
|
||||
#elif defined( USING_GCC )
|
||||
// Note: We cannot disable the -Wliteral-suffix message with this macro because the
|
||||
// pragma command cannot suppress warnings from the C++ preprocessor. See gcc bug #53431.
|
||||
#define DISABLE_WARNINGS \
|
||||
_Pragma( "GCC diagnostic push" ) _Pragma( "GCC diagnostic ignored \"-Wall\"" ) \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wextra\"" ) \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wpragmas\"" ) \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wunused-local-typedefs\"" ) \
|
||||
_Pragma( "GCC diagnostic ignored \"-Woverloaded-virtual\"" ) \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wunused-parameter\"" ) \
|
||||
_Pragma( "GCC diagnostic ignored \"-Warray-bounds\"" ) \
|
||||
_Pragma( "GCC diagnostic ignored \"-Wterminate\"" )
|
||||
#define ENABLE_WARNINGS _Pragma( "GCC diagnostic pop" )
|
||||
#else
|
||||
#define DISABLE_WARNINGS
|
||||
#define ENABLE_WARNINGS
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
|
||||
|
||||
/*! @} */
|
||||
|
||||
|
||||
|
||||
@@ -9,9 +9,24 @@
|
||||
#define ANALYSIS_INTERVAL 1000
|
||||
#define BLOBID_INTERVAL 1000
|
||||
|
||||
enum AnalysisType{ AnalyzeNone=0, IdentifyBlobs=0x01, CopyPhaseIndicator=0x02,
|
||||
|
||||
enum class AnalysisType : uint64_t { AnalyzeNone=0, IdentifyBlobs=0x01, CopyPhaseIndicator=0x02,
|
||||
CopySimState=0x04, ComputeAverages=0x08, CreateRestart=0x10, WriteVis=0x20 };
|
||||
|
||||
AnalysisType& operator |=(AnalysisType &lhs, AnalysisType rhs)
|
||||
{
|
||||
lhs = static_cast<AnalysisType> (
|
||||
static_cast<std::underlying_type<AnalysisType>::type>(lhs) |
|
||||
static_cast<std::underlying_type<AnalysisType>::type>(rhs)
|
||||
);
|
||||
return lhs;
|
||||
}
|
||||
bool matches( AnalysisType x, AnalysisType y )
|
||||
{
|
||||
return static_cast<std::underlying_type<AnalysisType>::type>(x) &
|
||||
static_cast<std::underlying_type<AnalysisType>::type>(y) != 0;
|
||||
}
|
||||
|
||||
|
||||
template<class TYPE>
|
||||
void DeleteArray( const TYPE *p )
|
||||
@@ -30,7 +45,7 @@ struct AnalysisWaitIdStruct {
|
||||
|
||||
|
||||
// Helper class to write the restart file from a seperate thread
|
||||
class WriteRestartWorkItem: public ThreadPool::WorkItem
|
||||
class WriteRestartWorkItem: public ThreadPool::WorkItemRet<void>
|
||||
{
|
||||
public:
|
||||
WriteRestartWorkItem( const char* filename_, std::shared_ptr<double> cDen_,
|
||||
@@ -41,7 +56,6 @@ public:
|
||||
WriteCheckpoint(filename,cDen.get(),cfq.get(),N);
|
||||
PROFILE_STOP("Save Checkpoint",1);
|
||||
};
|
||||
virtual bool has_result() const { return false; }
|
||||
private:
|
||||
WriteRestartWorkItem();
|
||||
const char* filename;
|
||||
@@ -54,7 +68,7 @@ private:
|
||||
static const std::string id_map_filename = "lbpm_id_map.txt";
|
||||
typedef std::shared_ptr<std::pair<int,IntArray> > BlobIDstruct;
|
||||
typedef std::shared_ptr<std::vector<BlobIDType> > BlobIDList;
|
||||
class BlobIdentificationWorkItem1: public ThreadPool::WorkItem
|
||||
class BlobIdentificationWorkItem1: public ThreadPool::WorkItemRet<void>
|
||||
{
|
||||
public:
|
||||
BlobIdentificationWorkItem1( int timestep_, int Nx_, int Ny_, int Nz_, const RankInfoStruct& rank_info_,
|
||||
@@ -75,7 +89,6 @@ public:
|
||||
new_index->first = ComputeGlobalBlobIDs(Nx-2,Ny-2,Nz-2,rank_info,*phase,dist,vF,vS,ids,newcomm);
|
||||
PROFILE_STOP("Identify blobs",1);
|
||||
}
|
||||
virtual bool has_result() const { return false; }
|
||||
private:
|
||||
BlobIdentificationWorkItem1();
|
||||
int timestep;
|
||||
@@ -87,7 +100,7 @@ private:
|
||||
BlobIDList new_list;
|
||||
MPI_Comm newcomm;
|
||||
};
|
||||
class BlobIdentificationWorkItem2: public ThreadPool::WorkItem
|
||||
class BlobIdentificationWorkItem2: public ThreadPool::WorkItemRet<void>
|
||||
{
|
||||
public:
|
||||
BlobIdentificationWorkItem2( int timestep_, int Nx_, int Ny_, int Nz_, const RankInfoStruct& rank_info_,
|
||||
@@ -122,7 +135,6 @@ public:
|
||||
}
|
||||
PROFILE_STOP("Identify blobs maps",1);
|
||||
}
|
||||
virtual bool has_result() const { return false; }
|
||||
private:
|
||||
BlobIdentificationWorkItem2();
|
||||
int timestep;
|
||||
@@ -137,7 +149,7 @@ private:
|
||||
|
||||
|
||||
// Helper class to write the vis file from a thread
|
||||
class WriteVisWorkItem: public ThreadPool::WorkItem
|
||||
class WriteVisWorkItem: public ThreadPool::WorkItemRet<void>
|
||||
{
|
||||
public:
|
||||
WriteVisWorkItem( int timestep_, std::vector<IO::MeshDataStruct>& visData_,
|
||||
@@ -164,7 +176,6 @@ public:
|
||||
IO::writeData( timestep, visData, newcomm );
|
||||
PROFILE_STOP("Save Vis",1);
|
||||
};
|
||||
virtual bool has_result() const { return false; }
|
||||
private:
|
||||
WriteVisWorkItem();
|
||||
int timestep;
|
||||
@@ -177,7 +188,7 @@ private:
|
||||
|
||||
// Helper class to run the analysis from within a thread
|
||||
// Note: Averages will be modified after the constructor is called
|
||||
class AnalysisWorkItem: public ThreadPool::WorkItem
|
||||
class AnalysisWorkItem: public ThreadPool::WorkItemRet<void>
|
||||
{
|
||||
public:
|
||||
AnalysisWorkItem( AnalysisType type_, int timestep_, TwoPhase& Averages_,
|
||||
@@ -191,10 +202,10 @@ public:
|
||||
Averages.Label_NWP_map = *id_list;
|
||||
Averages.NumberComponents_WP = 1;
|
||||
Averages.Label_WP.fill(0.0);
|
||||
if ( (type&CopyPhaseIndicator) != 0 ) {
|
||||
if ( matches(type,AnalysisType::CopyPhaseIndicator) ) {
|
||||
// Averages.ColorToSignedDistance(beta,Averages.Phase,Averages.Phase_tplus);
|
||||
}
|
||||
if ( (type&ComputeAverages) != 0 ) {
|
||||
if ( matches(type,AnalysisType::ComputeAverages) ) {
|
||||
PROFILE_START("Compute dist",1);
|
||||
Averages.Initialize();
|
||||
Averages.ComputeDelPhi();
|
||||
@@ -212,7 +223,6 @@ public:
|
||||
PROFILE_STOP("Compute dist",1);
|
||||
}
|
||||
}
|
||||
virtual bool has_result() const { return false; }
|
||||
private:
|
||||
AnalysisWorkItem();
|
||||
AnalysisType type;
|
||||
@@ -223,6 +233,7 @@ private:
|
||||
double beta;
|
||||
};
|
||||
|
||||
|
||||
// Function to start the analysis
|
||||
void run_analysis( int timestep, int restart_interval,
|
||||
const RankInfoStruct& rank_info, ScaLBL_Communicator &ScaLBL_Comm, TwoPhase& Averages,
|
||||
@@ -236,46 +247,45 @@ void run_analysis( int timestep, int restart_interval,
|
||||
int N = Nx*Ny*Nz;
|
||||
|
||||
// Determin the analysis we want to perform
|
||||
AnalysisType type = AnalyzeNone;
|
||||
AnalysisType type = AnalysisType::AnalyzeNone;
|
||||
if ( timestep%ANALYSIS_INTERVAL + 5 == ANALYSIS_INTERVAL ) {
|
||||
// Copy the phase indicator field for the earlier timestep
|
||||
type = static_cast<AnalysisType>( type | CopyPhaseIndicator );
|
||||
type |= AnalysisType::CopyPhaseIndicator;
|
||||
}
|
||||
if ( timestep%BLOBID_INTERVAL == 0 ) {
|
||||
// Identify blobs and update global ids in time
|
||||
type = static_cast<AnalysisType>( type | IdentifyBlobs );
|
||||
type |= AnalysisType::IdentifyBlobs;
|
||||
}
|
||||
/*#ifdef USE_CUDA
|
||||
if ( tpool.getQueueSize()<=3 && tpool.getNumThreads()>0 && timestep%50==0 ) {
|
||||
// Keep a few blob identifications queued up to keep the processors busy,
|
||||
// allowing us to track the blobs as fast as possible
|
||||
// Add more detailed estimates of the update frequency required to track blobs
|
||||
type = static_cast<AnalysisType>( type | IdentifyBlobs );
|
||||
type |= AnalysisType::IdentifyBlobs;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
#endif */
|
||||
if ( timestep%ANALYSIS_INTERVAL == 0 ) {
|
||||
// Copy the averages to the CPU (and identify blobs)
|
||||
type = static_cast<AnalysisType>( type | CopySimState );
|
||||
type = static_cast<AnalysisType>( type | IdentifyBlobs );
|
||||
type |= AnalysisType::CopySimState;
|
||||
type |= AnalysisType::IdentifyBlobs;
|
||||
}
|
||||
if ( timestep%ANALYSIS_INTERVAL == 5 ) {
|
||||
// Run the analysis
|
||||
type = static_cast<AnalysisType>( type | ComputeAverages );
|
||||
type |= AnalysisType::ComputeAverages;
|
||||
}
|
||||
if (timestep%restart_interval == 0) {
|
||||
// Write the restart file
|
||||
type = static_cast<AnalysisType>( type | CreateRestart );
|
||||
type |= AnalysisType::CreateRestart;
|
||||
}
|
||||
if (timestep%restart_interval == 0) {
|
||||
// Write the visualization data
|
||||
type = static_cast<AnalysisType>( type | WriteVis );
|
||||
type = static_cast<AnalysisType>( type | CopySimState );
|
||||
type = static_cast<AnalysisType>( type | IdentifyBlobs );
|
||||
type |= AnalysisType::WriteVis;
|
||||
type |= AnalysisType::CopySimState;
|
||||
type |= AnalysisType::IdentifyBlobs;
|
||||
}
|
||||
|
||||
// Return if we are not doing anything
|
||||
if ( type == AnalyzeNone )
|
||||
if ( type == AnalysisType::AnalyzeNone )
|
||||
return;
|
||||
|
||||
PROFILE_START("start_analysis");
|
||||
@@ -284,21 +294,23 @@ void run_analysis( int timestep, int restart_interval,
|
||||
ScaLBL_DeviceBarrier();
|
||||
PROFILE_START("Copy data to host",1);
|
||||
std::shared_ptr<DoubleArray> phase;
|
||||
if ( (type&CopyPhaseIndicator)!=0 || (type&ComputeAverages)!=0 ||
|
||||
(type&CopySimState)!=0 || (type&IdentifyBlobs)!=0 )
|
||||
if ( matches(type,AnalysisType::CopyPhaseIndicator) ||
|
||||
matches(type,AnalysisType::ComputeAverages) ||
|
||||
matches(type,AnalysisType::CopySimState) ||
|
||||
matches(type,AnalysisType::IdentifyBlobs) )
|
||||
{
|
||||
phase = std::shared_ptr<DoubleArray>(new DoubleArray(Nx,Ny,Nz));
|
||||
ScaLBL_CopyToHost(phase->data(),Phi,N*sizeof(double));
|
||||
}
|
||||
if ( (type&CopyPhaseIndicator)!=0 ) {
|
||||
if ( matches(type,AnalysisType::CopyPhaseIndicator) ) {
|
||||
memcpy(Averages.Phase_tplus.data(),phase->data(),N*sizeof(double));
|
||||
//Averages.ColorToSignedDistance(beta,Averages.Phase,Averages.Phase_tplus);
|
||||
}
|
||||
if ( (type&ComputeAverages)!=0 ) {
|
||||
if ( matches(type,AnalysisType::ComputeAverages) ) {
|
||||
memcpy(Averages.Phase_tminus.data(),phase->data(),N*sizeof(double));
|
||||
//Averages.ColorToSignedDistance(beta,Averages.Phase,Averages.Phase_tminus);
|
||||
}
|
||||
if ( (type&CopySimState) != 0 ) {
|
||||
if ( matches(type,AnalysisType::CopySimState) ) {
|
||||
// Copy the members of Averages to the cpu (phase was copied above)
|
||||
// Wait
|
||||
PROFILE_START("Copy-Pressure",1);
|
||||
@@ -319,7 +331,7 @@ void run_analysis( int timestep, int restart_interval,
|
||||
PROFILE_STOP("Copy-State",1);
|
||||
}
|
||||
std::shared_ptr<double> cDen, cfq;
|
||||
if ( (type&CreateRestart) != 0 ) {
|
||||
if ( matches(type,AnalysisType::CreateRestart) ) {
|
||||
// Copy restart data to the CPU
|
||||
cDen = std::shared_ptr<double>(new double[2*Np],DeleteArray<double>);
|
||||
cfq = std::shared_ptr<double>(new double[19*Np],DeleteArray<double>);
|
||||
@@ -329,14 +341,14 @@ void run_analysis( int timestep, int restart_interval,
|
||||
PROFILE_STOP("Copy data to host",1);
|
||||
|
||||
// Spawn threads to do blob identification work
|
||||
if ( (type&IdentifyBlobs)!=0 ) {
|
||||
if ( matches(type,AnalysisType::IdentifyBlobs) ) {
|
||||
BlobIDstruct new_index(new std::pair<int,IntArray>(0,IntArray()));
|
||||
BlobIDstruct new_ids(new std::pair<int,IntArray>(0,IntArray()));
|
||||
BlobIDList new_list(new std::vector<BlobIDType>());
|
||||
ThreadPool::WorkItem *work1 = new BlobIdentificationWorkItem1(timestep,
|
||||
Nx,Ny,Nz,rank_info,phase,Averages.SDs,last_ids,new_index,new_ids,new_list);
|
||||
ThreadPool::WorkItem *work2 = new BlobIdentificationWorkItem2(timestep,
|
||||
Nx,Ny,Nz,rank_info,phase,Averages.SDs,last_ids,new_index,new_ids,new_list);
|
||||
auto work1 = new BlobIdentificationWorkItem1(timestep,Nx,Ny,Nz,rank_info,
|
||||
phase,Averages.SDs,last_ids,new_index,new_ids,new_list);
|
||||
auto work2 = new BlobIdentificationWorkItem2(timestep,Nx,Ny,Nz,rank_info,
|
||||
phase,Averages.SDs,last_ids,new_index,new_ids,new_list);
|
||||
work1->add_dependency(wait.blobID);
|
||||
work2->add_dependency(tpool.add_work(work1));
|
||||
wait.blobID = tpool.add_work(work2);
|
||||
@@ -346,9 +358,8 @@ void run_analysis( int timestep, int restart_interval,
|
||||
}
|
||||
|
||||
// Spawn threads to do the analysis work
|
||||
if ( (type&ComputeAverages) != 0 ) {
|
||||
ThreadPool::WorkItem *work = new AnalysisWorkItem(
|
||||
type,timestep,Averages,last_index,last_id_map,beta);
|
||||
if ( matches(type,AnalysisType::ComputeAverages) ) {
|
||||
auto work = new AnalysisWorkItem(type,timestep,Averages,last_index,last_id_map,beta);
|
||||
work->add_dependency(wait.blobID);
|
||||
work->add_dependency(wait.analysis);
|
||||
work->add_dependency(wait.vis); // Make sure we are done using analysis before modifying
|
||||
@@ -356,15 +367,15 @@ void run_analysis( int timestep, int restart_interval,
|
||||
}
|
||||
|
||||
// Spawn a thread to write the restart file
|
||||
if ( (type&CreateRestart) != 0 ) {
|
||||
if ( matches(type,AnalysisType::CreateRestart) ) {
|
||||
int rank = MPI_WORLD_RANK();
|
||||
//if (pBC) {
|
||||
//err = fabs(sat_w - sat_w_previous);
|
||||
//sat_w_previous = sat_w;
|
||||
//if (rank==0){
|
||||
// printf("Timestep %i: change in saturation since last checkpoint is %f \n",timestep,err);
|
||||
// }
|
||||
// }
|
||||
/* if (pBC) {
|
||||
err = fabs(sat_w - sat_w_previous);
|
||||
sat_w_previous = sat_w;
|
||||
if (rank==0){
|
||||
printf("Timestep %i: change in saturation since last checkpoint is %f \n",timestep,err);
|
||||
}
|
||||
} */
|
||||
// Wait for previous restart files to finish writing (not necessary, but helps to ensure memory usage is limited)
|
||||
tpool.wait(wait.restart);
|
||||
// Retain the timestep associated with the restart files
|
||||
@@ -374,17 +385,17 @@ void run_analysis( int timestep, int restart_interval,
|
||||
fclose(Rst);
|
||||
}
|
||||
// Write the restart file (using a seperate thread)
|
||||
WriteRestartWorkItem *work = new WriteRestartWorkItem(LocalRestartFile,cDen,cfq,Np);
|
||||
auto work = new WriteRestartWorkItem(LocalRestartFile,cDen,cfq,Np);
|
||||
work->add_dependency(wait.restart);
|
||||
wait.restart = tpool.add_work(work);
|
||||
}
|
||||
|
||||
// Save the results for visualization
|
||||
if ( (type&CreateRestart) != 0 ) {
|
||||
if ( matches(type,AnalysisType::CreateRestart) ) {
|
||||
// Wait for previous restart files to finish writing (not necessary, but helps to ensure memory usage is limited)
|
||||
tpool.wait(wait.vis);
|
||||
// Write the vis files
|
||||
ThreadPool::WorkItem *work = new WriteVisWorkItem( timestep, visData, Averages, fillData );
|
||||
auto work = new WriteVisWorkItem( timestep, visData, Averages, fillData );
|
||||
work->add_dependency(wait.blobID);
|
||||
work->add_dependency(wait.analysis);
|
||||
work->add_dependency(wait.vis);
|
||||
|
||||
@@ -27,4 +27,3 @@ int atomic_pthread_lock_initialized = create_atomic_pthread_lock();
|
||||
|
||||
} // AtomicOperations namespace
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <typeinfo>
|
||||
#include <stdexcept>
|
||||
|
||||
// Choose the OS
|
||||
#if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 )
|
||||
@@ -89,6 +88,16 @@ inline int32_atomic atomic_get( const int32_atomic volatile *x );
|
||||
*/
|
||||
inline int64_atomic atomic_get( const int64_atomic volatile *x );
|
||||
|
||||
|
||||
/**
|
||||
* \brief Get the value
|
||||
* \details Read the data in x
|
||||
* \param[in] x The pointer to the value to get
|
||||
*/
|
||||
template<class TYPE>
|
||||
inline TYPE *atomic_get( volatile TYPE **x );
|
||||
|
||||
|
||||
/**
|
||||
* \brief Set the value
|
||||
* \details Set the data in x to y (*x=y)
|
||||
@@ -185,9 +194,8 @@ inline bool atomic_compare_and_swap( void *volatile *v, void *x, void *y );
|
||||
* \brief Fetch the current value and "and" with given value
|
||||
* \details Perform *v = (*v) & x, returning the previous value
|
||||
* \return Returns the previous value before the "and" operation
|
||||
* \param[in] v The pointer to the value to check and swap
|
||||
* \param[in] x The value to compare
|
||||
* \param[in] y The value to swap iff *v==x
|
||||
* \param[in] v The pointer to the value to check and and
|
||||
* \param[in] x The value to and
|
||||
*/
|
||||
inline int32_atomic atomic_fetch_and_and( int32_atomic volatile *v, int32_atomic x );
|
||||
|
||||
@@ -195,9 +203,8 @@ inline int32_atomic atomic_fetch_and_and( int32_atomic volatile *v, int32_atomic
|
||||
* \brief Fetch the current value and "and" with given value
|
||||
* \details Perform *v = (*v) & x, returning the previous value
|
||||
* \return Returns the previous value before the "and" operation
|
||||
* \param[in] v The pointer to the value to check and swap
|
||||
* \param[in] x The value to compare
|
||||
* \param[in] y The value to swap iff *v==x
|
||||
* \param[in] v The pointer to the value to check and and
|
||||
* \param[in] x The value to and
|
||||
*/
|
||||
inline int64_atomic atomic_fetch_and_and( int64_atomic volatile *v, int64_atomic x );
|
||||
|
||||
@@ -205,9 +212,8 @@ inline int64_atomic atomic_fetch_and_and( int64_atomic volatile *v, int64_atomic
|
||||
* \brief Fetch the current value and "or" with given value
|
||||
* \details Perform *v = (*v) | x, returning the previous value
|
||||
* \return Returns the previous value before the "and" operation
|
||||
* \param[in] v The pointer to the value to check and swap
|
||||
* \param[in] x The value to compare
|
||||
* \param[in] y The value to swap iff *v==x
|
||||
* \param[in] v The pointer to the value to check and or
|
||||
* \param[in] x The value to or
|
||||
*/
|
||||
inline int32_atomic atomic_fetch_and_or( int32_atomic volatile *v, int32_atomic x );
|
||||
|
||||
@@ -216,13 +222,12 @@ inline int32_atomic atomic_fetch_and_or( int32_atomic volatile *v, int32_atomic
|
||||
* \details Perform *v = (*v) | x, returning the previous value
|
||||
* \return Returns the previous value before the "and" operation
|
||||
* \param[in] v The pointer to the value to check and swap
|
||||
* \param[in] x The value to compare
|
||||
* \param[in] y The value to swap iff *v==x
|
||||
* \param[in] v The pointer to the value to check and or
|
||||
* \param[in] x The value to or
|
||||
*/
|
||||
inline int64_atomic atomic_fetch_and_or( int64_atomic volatile *v, int64_atomic x );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Class to store a pool of objects
|
||||
* \details This class stores a pool of objects that can be added/removed in a thread-safe way
|
||||
@@ -261,6 +266,7 @@ class pool
|
||||
while ( !atomic_compare_and_swap( (void *volatile *) &d_data[i], nullptr, ptr ) )
|
||||
i = ( i + 1 ) % N_MAX;
|
||||
}
|
||||
|
||||
private:
|
||||
volatile TYPE **d_data;
|
||||
pool( const pool &rhs );
|
||||
@@ -323,10 +329,24 @@ inline int64_atomic atomic_decrement( int64_atomic volatile *x )
|
||||
{
|
||||
return OSAtomicDecrement64Barrier( x );
|
||||
}
|
||||
int32_atomic atomic_fetch_and_or( int32_atomic volatile *v, int32_atomic x ) { return OSAtomicOr32Orig( x, (volatile uint32_t *) v ); }
|
||||
int32_atomic atomic_fetch_and_and( int32_atomic volatile *v, int32_atomic x ) { return OSAtomicAnd32Orig( x, (volatile uint32_t *) v); }
|
||||
int64_atomic atomic_fetch_and_or( int64_atomic volatile *v, int64_atomic x ) { throw std::logic_error("Not availible for this OS"); return 0; }
|
||||
int64_atomic atomic_fetch_and_and( int64_atomic volatile *v, int64_atomic x ) { throw std::logic_error("Not availible for this OS"); return 0; }
|
||||
int32_atomic atomic_fetch_and_or( int32_atomic volatile *v, int32_atomic x )
|
||||
{
|
||||
return OSAtomicOr32Orig( x, (volatile uint32_t *) v );
|
||||
}
|
||||
int32_atomic atomic_fetch_and_and( int32_atomic volatile *v, int32_atomic x )
|
||||
{
|
||||
return OSAtomicAnd32Orig( x, (volatile uint32_t *) v );
|
||||
}
|
||||
int64_atomic atomic_fetch_and_or( int64_atomic volatile *v, int64_atomic x )
|
||||
{
|
||||
throw std::logic_error( "Not availible for this OS" );
|
||||
return 0;
|
||||
}
|
||||
int64_atomic atomic_fetch_and_and( int64_atomic volatile *v, int64_atomic x )
|
||||
{
|
||||
throw std::logic_error( "Not availible for this OS" );
|
||||
return 0;
|
||||
}
|
||||
inline int32_atomic atomic_add( int32_atomic volatile *x, int32_atomic y )
|
||||
{
|
||||
return OSAtomicAdd32Barrier( y, x );
|
||||
@@ -352,10 +372,22 @@ int32_atomic atomic_increment( int32_atomic volatile *x ) { return __sync_add_an
|
||||
int64_atomic atomic_increment( int64_atomic volatile *x ) { return __sync_add_and_fetch( x, 1 ); }
|
||||
int32_atomic atomic_decrement( int32_atomic volatile *x ) { return __sync_sub_and_fetch( x, 1 ); }
|
||||
int64_atomic atomic_decrement( int64_atomic volatile *x ) { return __sync_sub_and_fetch( x, 1 ); }
|
||||
int32_atomic atomic_fetch_and_or( int32_atomic volatile *v, int32_atomic x ) { return __sync_fetch_and_or( v, x ); }
|
||||
int64_atomic atomic_fetch_and_or( int64_atomic volatile *v, int64_atomic x ) { return __sync_fetch_and_or( v, x ); }
|
||||
int32_atomic atomic_fetch_and_and( int32_atomic volatile *v, int32_atomic x ) { return __sync_fetch_and_and( v, x ); }
|
||||
int64_atomic atomic_fetch_and_and( int64_atomic volatile *v, int64_atomic x ) { return __sync_fetch_and_and( v, x ); }
|
||||
int32_atomic atomic_fetch_and_or( int32_atomic volatile *v, int32_atomic x )
|
||||
{
|
||||
return __sync_fetch_and_or( v, x );
|
||||
}
|
||||
int64_atomic atomic_fetch_and_or( int64_atomic volatile *v, int64_atomic x )
|
||||
{
|
||||
return __sync_fetch_and_or( v, x );
|
||||
}
|
||||
int32_atomic atomic_fetch_and_and( int32_atomic volatile *v, int32_atomic x )
|
||||
{
|
||||
return __sync_fetch_and_and( v, x );
|
||||
}
|
||||
int64_atomic atomic_fetch_and_and( int64_atomic volatile *v, int64_atomic x )
|
||||
{
|
||||
return __sync_fetch_and_and( v, x );
|
||||
}
|
||||
inline int32_atomic atomic_add( int32_atomic volatile *x, int32_atomic y )
|
||||
{
|
||||
return __sync_add_and_fetch( x, y );
|
||||
@@ -459,31 +491,44 @@ inline int64_atomic atomic_get( const int64_atomic volatile *x )
|
||||
{
|
||||
return atomic_add( const_cast<int64_atomic volatile *>( x ), 0 );
|
||||
}
|
||||
template<class TYPE>
|
||||
inline TYPE *atomic_get( volatile TYPE **x )
|
||||
{
|
||||
return reinterpret_cast<TYPE *>(
|
||||
atomic_add( reinterpret_cast<int64_atomic volatile *>( x ), 0 ) );
|
||||
}
|
||||
inline void atomic_set( int32_atomic volatile *x, int32_atomic y )
|
||||
{
|
||||
int32_atomic tmp = *x;
|
||||
while ( !atomic_compare_and_swap( x, tmp, y ) ) { tmp = *x; }
|
||||
while ( !atomic_compare_and_swap( x, tmp, y ) ) {
|
||||
tmp = *x;
|
||||
}
|
||||
}
|
||||
inline void atomic_set( int64_atomic volatile *x, int64_atomic y )
|
||||
{
|
||||
int64_atomic tmp = *x;
|
||||
while ( !atomic_compare_and_swap( x, tmp, y ) ) { tmp = *x; }
|
||||
while ( !atomic_compare_and_swap( x, tmp, y ) ) {
|
||||
tmp = *x;
|
||||
}
|
||||
}
|
||||
inline void atomic_swap( int32_atomic volatile *x, int32_atomic *y )
|
||||
{
|
||||
int32_atomic tmp = *x;
|
||||
while ( !atomic_compare_and_swap( x, tmp, *y ) ) { tmp = *x; }
|
||||
while ( !atomic_compare_and_swap( x, tmp, *y ) ) {
|
||||
tmp = *x;
|
||||
}
|
||||
*y = tmp;
|
||||
}
|
||||
inline void atomic_swap( int64_atomic volatile *x, int64_atomic *y )
|
||||
{
|
||||
int64_atomic tmp = *x;
|
||||
while ( !atomic_compare_and_swap( x, tmp, *y ) ) { tmp = *x; }
|
||||
while ( !atomic_compare_and_swap( x, tmp, *y ) ) {
|
||||
tmp = *x;
|
||||
}
|
||||
*y = tmp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Define an atomic counter
|
||||
struct counter_t {
|
||||
public:
|
||||
@@ -499,6 +544,7 @@ public:
|
||||
inline void setCount( int val ) { count = val; }
|
||||
// Get the current value of the count
|
||||
inline int getCount() const { return count; }
|
||||
|
||||
private:
|
||||
counter_t( const counter_t & );
|
||||
counter_t &operator=( const counter_t & );
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#ifndef included_AtomicModelAtomicList
|
||||
#define included_AtomicModelAtomicList
|
||||
|
||||
#include <functional>
|
||||
#include <csignal>
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <functional>
|
||||
|
||||
#include "threadpool/atomic_helpers.h"
|
||||
|
||||
|
||||
|
||||
/** \class AtomicList
|
||||
*
|
||||
* \brief Maintain a sorted list of entries
|
||||
@@ -25,12 +24,14 @@ public:
|
||||
/*!
|
||||
* \brief Remove an item from the list
|
||||
* \details Find and remove first entry that meets the given criteria
|
||||
* @return Return the item that matches the criteria, or the default item if no item matches
|
||||
* @param comp Comparison function object (i.e. an object that satisfies
|
||||
* @return Return the item that matches the criteria,
|
||||
* or the default item if no item matches
|
||||
* @param compare Comparison function object (i.e. an object that satisfies
|
||||
* the requirements of Compare) which returns true if the
|
||||
* given value meets the selection criteria.
|
||||
* The signature of the comparison function should be equivalent to:
|
||||
* bool cmp( const TYPE& value, ... );
|
||||
* @param args Additional arguments for the comparison
|
||||
*/
|
||||
template<class Compare, class... Args>
|
||||
inline TYPE remove( Compare compare, Args... args );
|
||||
@@ -42,11 +43,6 @@ public:
|
||||
* \brief Insert an item
|
||||
* \details Insert an item into the list
|
||||
* @param x Item to insert
|
||||
* @param comp Comparison function object (i.e. an object that satisfies
|
||||
* the requirements of Compare) which returns true if the
|
||||
* first argument is less than (i.e. is ordered before) the second.
|
||||
* The signature of the comparison function should be equivalent to:
|
||||
* bool cmp(const TYPE &a, const TYPE &b);
|
||||
*/
|
||||
inline void insert( TYPE x );
|
||||
|
||||
@@ -177,8 +173,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#include "threadpool/atomic_list.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
#define included_AtomicList_hpp
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* Constructor *
|
||||
******************************************************************/
|
||||
template<class TYPE, int MAX_SIZE, class COMPARE>
|
||||
AtomicList<TYPE,MAX_SIZE,COMPARE>::AtomicList( const TYPE& default_value, const COMPARE& comp ):
|
||||
d_compare(comp),
|
||||
d_default(default_value)
|
||||
AtomicList<TYPE, MAX_SIZE, COMPARE>::AtomicList( const TYPE &default_value, const COMPARE &comp )
|
||||
: d_compare( comp ), d_default( default_value )
|
||||
{
|
||||
d_N = 0;
|
||||
d_next[0] = -1;
|
||||
@@ -52,7 +50,8 @@ inline TYPE AtomicList<TYPE,MAX_SIZE,COMPARE>::remove( Compare compare, Args...
|
||||
// Test to see if the object passes compare
|
||||
bool test = compare( const_cast<TYPE &>( d_objects[next - 1] ), args... );
|
||||
if ( test ) {
|
||||
// We want to return this object, update next to point to another entry and remove the entry
|
||||
// We want to return this object, update next to point to another entry and remove the
|
||||
// entry
|
||||
unlock( next, -3 );
|
||||
unlock( pos, next2 );
|
||||
pos = next;
|
||||
@@ -187,7 +186,6 @@ inline bool AtomicList<TYPE,MAX_SIZE,COMPARE>::check( )
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* MemoryPool *
|
||||
******************************************************************/
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#include "threadpool/atomic_helpers.h"
|
||||
#include "common/UnitTest.h"
|
||||
#include "common/Utilities.h"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#define perr std::cerr
|
||||
@@ -108,7 +108,8 @@ int main( int, char *[] )
|
||||
for ( int i = 0; i < N_threads; i++ )
|
||||
threads[i].join();
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
double time_inc_parallel = std::chrono::duration<double>(stop-start).count() / ( N_count * N_threads );
|
||||
double time_inc_parallel =
|
||||
std::chrono::duration<double>( stop - start ).count() / ( N_count * N_threads );
|
||||
val = count.getCount();
|
||||
if ( val != N_count * N_threads ) {
|
||||
char tmp[100];
|
||||
@@ -124,7 +125,8 @@ int main( int, char *[] )
|
||||
for ( int i = 0; i < N_threads; i++ )
|
||||
threads[i].join();
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
double time_dec_parallel = std::chrono::duration<double>(stop-start).count() / ( N_count * N_threads );
|
||||
double time_dec_parallel =
|
||||
std::chrono::duration<double>( stop - start ).count() / ( N_count * N_threads );
|
||||
val = count.getCount();
|
||||
if ( val != 0 ) {
|
||||
char tmp[100];
|
||||
@@ -147,6 +149,6 @@ int main( int, char *[] )
|
||||
|
||||
// Finished
|
||||
ut.report();
|
||||
int N_errors = static_cast<int>( ut.NumFailGlobal() );
|
||||
auto N_errors = static_cast<int>( ut.NumFailGlobal() );
|
||||
return N_errors;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#include "threadpool/atomic_list.h"
|
||||
#include "common/UnitTest.h"
|
||||
#include "common/Utilities.h"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
@@ -23,11 +23,21 @@ static void modify_list( AtomicList<int,1024>& list )
|
||||
auto v3 = list.remove( []( int v ) { return v >= ( rand() / 8 ); } );
|
||||
auto v4 = list.remove( []( int v ) { return v >= ( rand() / 4 ); } );
|
||||
auto v5 = list.remove( []( int v ) { return v >= ( rand() / 2 ); } );
|
||||
if ( v1 !=-1 ) { list.insert( v1 ); }
|
||||
if ( v2 !=-1 ) { list.insert( v2 ); }
|
||||
if ( v3 !=-1 ) { list.insert( v3 ); }
|
||||
if ( v4 !=-1 ) { list.insert( v4 ); }
|
||||
if ( v5 !=-1 ) { list.insert( v5 ); }
|
||||
if ( v1 != -1 ) {
|
||||
list.insert( v1 );
|
||||
}
|
||||
if ( v2 != -1 ) {
|
||||
list.insert( v2 );
|
||||
}
|
||||
if ( v3 != -1 ) {
|
||||
list.insert( v3 );
|
||||
}
|
||||
if ( v4 != -1 ) {
|
||||
list.insert( v4 );
|
||||
}
|
||||
if ( v5 != -1 ) {
|
||||
list.insert( v5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,14 +47,14 @@ static bool check_list( const std::vector<int>& x, AtomicList<int,1024>& list )
|
||||
bool pass = list.check();
|
||||
pass = pass && (int) x.size() == list.size();
|
||||
if ( pass ) {
|
||||
for (size_t i=0; i<x.size(); i++)
|
||||
pass = pass && x[i] == list.remove( [](int) { return true; } );
|
||||
for ( int i : x )
|
||||
pass = pass && i == list.remove( []( int ) { return true; } );
|
||||
}
|
||||
// Restore the list
|
||||
for ( int i = 0; i < list.size(); i++ )
|
||||
list.remove_first();
|
||||
for (size_t i=0; i<x.size(); i++)
|
||||
list.insert( x[i] );
|
||||
for ( int i : x )
|
||||
list.insert( i );
|
||||
return pass;
|
||||
}
|
||||
|
||||
@@ -56,7 +66,6 @@ static inline void clear_list(AtomicList<int,1024>& list )
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* The main program *
|
||||
******************************************************************/
|
||||
@@ -90,13 +99,14 @@ int main( int, char *[] )
|
||||
ut.failure( "Basic sanity test" );
|
||||
|
||||
// Clear the list
|
||||
while ( list.remove( [](int) { return true; } ) != -1 ) {}
|
||||
while ( list.remove( []( int ) { return true; } ) != -1 ) {
|
||||
}
|
||||
|
||||
// Create a list of known values
|
||||
// std::vector<int> data0(512);
|
||||
std::vector<int> data0( 5 * N_threads );
|
||||
for (size_t i=0; i<data0.size(); i++)
|
||||
data0[i] = rand();
|
||||
for ( int &i : data0 )
|
||||
i = rand();
|
||||
auto data = data0;
|
||||
std::sort( data.begin(), data.end() );
|
||||
|
||||
@@ -110,8 +120,8 @@ int main( int, char *[] )
|
||||
for ( int it = 0; it < N_it; it++ ) {
|
||||
clear_list( list );
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
for (size_t i=0; i<data0.size(); i++)
|
||||
list.insert( data0[i] );
|
||||
for ( int i : data0 )
|
||||
list.insert( i );
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
time += ( stop - start );
|
||||
}
|
||||
@@ -139,21 +149,22 @@ int main( int, char *[] )
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
time += ( stop - start );
|
||||
}
|
||||
printf("remove (ordered) time/item = %0.0f ns\n",1e9*time.count()/(N_it*data0.size()));
|
||||
printf(
|
||||
"remove (ordered) time/item = %0.0f ns\n", 1e9 * time.count() / ( N_it * data0.size() ) );
|
||||
|
||||
// Test the cost to remove (out order)
|
||||
time = time.zero();
|
||||
for ( int it = 0; it < N_it; it++ ) {
|
||||
check_list( data, list );
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
for (size_t i=0; i<data0.size(); i++) {
|
||||
int tmp = data0[i];
|
||||
for ( int tmp : data0 ) {
|
||||
list.remove( [tmp]( int v ) { return v == tmp; } );
|
||||
}
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
time += ( stop - start );
|
||||
}
|
||||
printf("remove (unordered) time/item = %0.0f ns\n",1e9*time.count()/(N_it*data0.size()));
|
||||
printf(
|
||||
"remove (unordered) time/item = %0.0f ns\n", 1e9 * time.count() / ( N_it * data0.size() ) );
|
||||
|
||||
// Read/write to the list and check the results
|
||||
int64_t N0 = list.N_remove();
|
||||
@@ -205,6 +216,6 @@ int main( int, char *[] )
|
||||
|
||||
// Finished
|
||||
ut.report();
|
||||
int N_errors = static_cast<int>( ut.NumFailGlobal() );
|
||||
auto N_errors = static_cast<int>( ut.NumFailGlobal() );
|
||||
return N_errors;
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
#include "threadpool/thread_pool.h"
|
||||
#include "common/UnitTest.h"
|
||||
#include "common/Utilities.h"
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
#define MAX( x, y ) ( ( x ) > ( y ) ? ( x ) : ( y ) )
|
||||
@@ -82,7 +82,8 @@ void waste_cpu( int N )
|
||||
// Sleep for the given time
|
||||
// Note: since we may encounter interrupts, we may not sleep for the desired time
|
||||
// so we need to perform the sleep in a loop
|
||||
void sleep_ms( int64_t N ) {
|
||||
void sleep_ms( int64_t N )
|
||||
{
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
auto t2 = std::chrono::high_resolution_clock::now();
|
||||
while ( to_ms( t2 - t1 ) < N ) {
|
||||
@@ -91,9 +92,7 @@ void sleep_ms( int64_t N ) {
|
||||
t2 = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
void sleep_s( int N ) {
|
||||
sleep_ms(1000*N);
|
||||
}
|
||||
void sleep_s( int N ) { sleep_ms( 1000 * N ); }
|
||||
|
||||
|
||||
// Function to sleep for N seconds then increment a global count
|
||||
@@ -135,7 +134,7 @@ void print_processor( ThreadPool *tpool )
|
||||
sprintf( tmp, "%i: Thread,proc = %i,%i\n", rank, thread, processor );
|
||||
sleep_ms( 10 * rank );
|
||||
print_processor_mutex.lock();
|
||||
std::cout << tmp;
|
||||
pout << tmp;
|
||||
print_processor_mutex.unlock();
|
||||
sleep_ms( 100 );
|
||||
}
|
||||
@@ -161,7 +160,9 @@ int test_member_thread( ThreadPool *tpool )
|
||||
}
|
||||
|
||||
|
||||
// Functions to test the templates
|
||||
/******************************************************************
|
||||
* Test the TPOOL_ADD_WORK macro with variable number of arguments *
|
||||
******************************************************************/
|
||||
static int myfun0() { return 0; }
|
||||
static int myfun1( int ) { return 1; }
|
||||
static int myfun2( int, float ) { return 2; }
|
||||
@@ -170,60 +171,6 @@ static int myfun4( int, float, double, char ) { return 4; }
|
||||
static int myfun5( int, float, double, char, std::string ) { return 5; }
|
||||
static int myfun6( int, float, double, char, std::string, int ) { return 6; }
|
||||
static int myfun7( int, float, double, char, std::string, int, int ) { return 7; }
|
||||
|
||||
|
||||
// Function to test instantiation of functions with different number of arguments
|
||||
// clang-format off
|
||||
static void vfunarg00() {}
|
||||
static void vfunarg01( int ) {}
|
||||
static void vfunarg02( int, char ) {}
|
||||
static void vfunarg03( int, char, double ) {}
|
||||
static void vfunarg04( int, char, double, int ) {}
|
||||
static void vfunarg05( int, char, double, int, char ) {}
|
||||
static void vfunarg06( int, char, double, int, char, double ) {}
|
||||
static void vfunarg07( int, char, double, int, char, double, int ) {}
|
||||
static void vfunarg08( int, char, double, int, char, double, int, char ) {}
|
||||
static void vfunarg09( int, char, double, int, char, double, int, char, double ) {}
|
||||
static void vfunarg10( int, char, double, int, char, double, int, char, double, int ) {}
|
||||
static void vfunarg11( int, char, double, int, char, double, int, char, double, int, char ) {}
|
||||
static void vfunarg12( int, char, double, int, char, double, int, char, double, int, char, double ) {}
|
||||
static void vfunarg13( int, char, double, int, char, double, int, char, double, int, char, double, int ) {}
|
||||
static void vfunarg14( int, char, double, int, char, double, int, char, double, int, char, double, int, char ) {}
|
||||
static void vfunarg15( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) {}
|
||||
static void vfunarg16( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int ) {}
|
||||
static void vfunarg17( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char ) {}
|
||||
static void vfunarg18( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) {}
|
||||
static void vfunarg19( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int ) {}
|
||||
static void vfunarg20( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char ) {}
|
||||
static void vfunarg21( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) {}
|
||||
static void vfunarg22( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int ) {}
|
||||
static void vfunarg23( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char ) {}
|
||||
static void vfunarg24( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) {}
|
||||
static int funarg00() { return 0; }
|
||||
static int funarg01( int ) { return 1; }
|
||||
static int funarg02( int, char ) { return 2; }
|
||||
static int funarg03( int, char, double ) { return 3; }
|
||||
static int funarg04( int, char, double, int ) { return 4; }
|
||||
static int funarg05( int, char, double, int, char ) { return 5; }
|
||||
static int funarg06( int, char, double, int, char, double ) { return 6; }
|
||||
static int funarg07( int, char, double, int, char, double, int ) { return 7; }
|
||||
static int funarg08( int, char, double, int, char, double, int, char ) { return 8; }
|
||||
static int funarg09( int, char, double, int, char, double, int, char, double ) { return 9; }
|
||||
static int funarg10( int, char, double, int, char, double, int, char, double, int ) { return 10; }
|
||||
static int funarg11( int, char, double, int, char, double, int, char, double, int, char ) { return 11; }
|
||||
static int funarg12( int, char, double, int, char, double, int, char, double, int, char, double ) { return 12; }
|
||||
static int funarg13( int, char, double, int, char, double, int, char, double, int, char, double, int ) { return 13; }
|
||||
static int funarg14( int, char, double, int, char, double, int, char, double, int, char, double, int, char ) { return 14; }
|
||||
static int funarg15( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) { return 15; }
|
||||
static int funarg16( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int ) { return 16; }
|
||||
static int funarg17( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char ) { return 17; }
|
||||
static int funarg18( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) { return 18; }
|
||||
static int funarg19( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int ) { return 19; }
|
||||
static int funarg20( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char ) { return 20; }
|
||||
static int funarg21( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) { return 21; }
|
||||
static int funarg22( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int ) { return 22; }
|
||||
static int funarg23( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char ) { return 23; }
|
||||
static int funarg24( int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double, int, char, double ) { return 24; }
|
||||
static int test_function_arguements( ThreadPool *tpool )
|
||||
{
|
||||
int N_errors = 0;
|
||||
@@ -231,83 +178,51 @@ static int test_function_arguements( ThreadPool *tpool )
|
||||
ThreadPool::thread_id_t id0 = TPOOL_ADD_WORK( tpool, myfun0, ( nullptr ) );
|
||||
ThreadPool::thread_id_t id1 = TPOOL_ADD_WORK( tpool, myfun1, ( (int) 1 ) );
|
||||
ThreadPool::thread_id_t id2 = TPOOL_ADD_WORK( tpool, myfun2, ( (int) 1, (float) 2 ) );
|
||||
ThreadPool::thread_id_t id3 = TPOOL_ADD_WORK( tpool, myfun3, ( (int) 1, (float) 2, (double) 3 ) );
|
||||
ThreadPool::thread_id_t id4 = TPOOL_ADD_WORK( tpool, myfun4, ( (int) 1, (float) 2, (double) 3, (char) 4 ) );
|
||||
ThreadPool::thread_id_t id5 = TPOOL_ADD_WORK( tpool, myfun5, ( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ) ) );
|
||||
ThreadPool::thread_id_t id52= TPOOL_ADD_WORK( tpool, myfun5, ( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ) ), -1 );
|
||||
ThreadPool::thread_id_t id6 = TPOOL_ADD_WORK( tpool, myfun6, ( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ), (int) 1 ) );
|
||||
ThreadPool::thread_id_t id7 = TPOOL_ADD_WORK( tpool, myfun7, ( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ), (int) 1, (int) 1 ) );
|
||||
ThreadPool::thread_id_t id3 =
|
||||
TPOOL_ADD_WORK( tpool, myfun3, ( (int) 1, (float) 2, (double) 3 ) );
|
||||
ThreadPool::thread_id_t id4 =
|
||||
TPOOL_ADD_WORK( tpool, myfun4, ( (int) 1, (float) 2, (double) 3, (char) 4 ) );
|
||||
ThreadPool::thread_id_t id5 = TPOOL_ADD_WORK(
|
||||
tpool, myfun5, ( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ) ) );
|
||||
ThreadPool::thread_id_t id52 = TPOOL_ADD_WORK(
|
||||
tpool, myfun5, ( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ) ), -1 );
|
||||
ThreadPool::thread_id_t id6 = TPOOL_ADD_WORK( tpool, myfun6,
|
||||
( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ), (int) 1 ) );
|
||||
ThreadPool::thread_id_t id7 = TPOOL_ADD_WORK( tpool, myfun7,
|
||||
( (int) 1, (float) 2, (double) 3, (char) 4, std::string( "test" ), (int) 1, (int) 1 ) );
|
||||
tpool->wait_pool_finished();
|
||||
if ( !tpool->isFinished( id0 ) ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id0 ) != 0 ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id1 ) != 1 ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id2 ) != 2 ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id3 ) != 3 ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id4 ) != 4 ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id5 ) != 5 ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id52 ) != 5 ){ N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id6 ) != 6 ) { N_errors++; }
|
||||
if ( tpool->getFunctionRet<int>( id7 ) != 7 ) { N_errors++; }
|
||||
// Test all the different numbers of arguments allowed
|
||||
TPOOL_ADD_WORK( tpool, vfunarg00, ( nullptr ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg01, ( 1 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg02, ( 1, 'a' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg03, ( 1, 'a', 3.0 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg04, ( 1, 'a', 3.0, 4 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg05, ( 1, 'a', 3.0, 4, 'e' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg06, ( 1, 'a', 3.0, 4, 'e', 6.0 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg07, ( 1, 'a', 3.0, 4, 'e', 6.0, 7 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg08, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg09, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg10, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg11, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg12, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg13, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg14, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg15, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg16, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg17, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg18, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg19, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg20, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg21, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg22, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0, 22 ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg23, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0, 22, 'w' ) );
|
||||
TPOOL_ADD_WORK( tpool, vfunarg24, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0, 22, 'w', 24.0 ) );
|
||||
std::vector<ThreadPool::thread_id_t> ids( 25 );
|
||||
ids[0] = TPOOL_ADD_WORK( tpool, funarg00, ( nullptr ) );
|
||||
ids[1] = TPOOL_ADD_WORK( tpool, funarg01, ( 1 ) );
|
||||
ids[2] = TPOOL_ADD_WORK( tpool, funarg02, ( 1, 'a' ) );
|
||||
ids[3] = TPOOL_ADD_WORK( tpool, funarg03, ( 1, 'a', 3.0 ) );
|
||||
ids[4] = TPOOL_ADD_WORK( tpool, funarg04, ( 1, 'a', 3.0, 4 ) );
|
||||
ids[5] = TPOOL_ADD_WORK( tpool, funarg05, ( 1, 'a', 3.0, 4, 'e' ) );
|
||||
ids[6] = TPOOL_ADD_WORK( tpool, funarg06, ( 1, 'a', 3.0, 4, 'e', 6.0 ) );
|
||||
ids[7] = TPOOL_ADD_WORK( tpool, funarg07, ( 1, 'a', 3.0, 4, 'e', 6.0, 7 ) );
|
||||
ids[8] = TPOOL_ADD_WORK( tpool, funarg08, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h' ) );
|
||||
ids[9] = TPOOL_ADD_WORK( tpool, funarg09, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0 ) );
|
||||
ids[10] = TPOOL_ADD_WORK( tpool, funarg10, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10 ) );
|
||||
ids[11] = TPOOL_ADD_WORK( tpool, funarg11, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k' ) );
|
||||
ids[12] = TPOOL_ADD_WORK( tpool, funarg12, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0 ) );
|
||||
ids[13] = TPOOL_ADD_WORK( tpool, funarg13, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13 ) );
|
||||
ids[14] = TPOOL_ADD_WORK( tpool, funarg14, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'h' ) );
|
||||
ids[15] = TPOOL_ADD_WORK( tpool, funarg15, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'h', 15.0 ) );
|
||||
ids[16] = TPOOL_ADD_WORK( tpool, funarg16, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16 ) );
|
||||
ids[17] = TPOOL_ADD_WORK( tpool, funarg17, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q' ) );
|
||||
ids[18] = TPOOL_ADD_WORK( tpool, funarg18, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0 ) );
|
||||
ids[19] = TPOOL_ADD_WORK( tpool, funarg19, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19 ) );
|
||||
ids[20] = TPOOL_ADD_WORK( tpool, funarg20, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't' ) );
|
||||
ids[21] = TPOOL_ADD_WORK( tpool, funarg21, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0 ) );
|
||||
ids[22] = TPOOL_ADD_WORK( tpool, funarg22, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0, 22 ) );
|
||||
ids[23] = TPOOL_ADD_WORK( tpool, funarg23, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0, 22, 'w' ) );
|
||||
ids[24] = TPOOL_ADD_WORK( tpool, funarg24, ( 1, 'a', 3.0, 4, 'e', 6.0, 7, 'h', 9.0, 10, 'k', 12.0, 13, 'n', 15.0, 16, 'q', 18.0, 19, 't', 21.0, 22, 'w', 24.0 ) );
|
||||
tpool->wait_all( ids );
|
||||
for ( size_t i = 0; i < ids.size(); i++ ) {
|
||||
if ( tpool->getFunctionRet<int>( ids[i] ) != static_cast<int>( i ) )
|
||||
if ( !tpool->isFinished( id0 ) ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id0 ) != 0 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id1 ) != 1 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id2 ) != 2 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id3 ) != 3 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id4 ) != 4 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id5 ) != 5 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id52 ) != 5 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id6 ) != 6 ) {
|
||||
N_errors++;
|
||||
}
|
||||
if ( tpool->getFunctionRet<int>( id7 ) != 7 ) {
|
||||
N_errors++;
|
||||
}
|
||||
return N_errors;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
|
||||
/******************************************************************
|
||||
@@ -323,15 +238,15 @@ public:
|
||||
NULL_USE( dummy );
|
||||
}
|
||||
// User defined run (can do anything)
|
||||
virtual void run() override
|
||||
void run() override
|
||||
{
|
||||
// Perform the tasks
|
||||
printf( "Hello work from UserWorkItem (void)" );
|
||||
}
|
||||
// Will the routine return a result
|
||||
virtual bool has_result() const override { return false; }
|
||||
bool has_result() const override { return false; }
|
||||
// User defined destructor
|
||||
virtual ~UserWorkItemVoid() {}
|
||||
~UserWorkItemVoid() override = default;
|
||||
};
|
||||
class UserWorkItemInt : public ThreadPool::WorkItemRet<int>
|
||||
{
|
||||
@@ -343,36 +258,29 @@ public:
|
||||
NULL_USE( dummy );
|
||||
}
|
||||
// User defined run (can do anything)
|
||||
virtual void run() override
|
||||
void run() override
|
||||
{
|
||||
// Perform the tasks
|
||||
printf( "Hello work from UserWorkItem (int)" );
|
||||
// Store the results (it's type will match the template)
|
||||
ThreadPool::WorkItemRet<int>::d_result = 1;
|
||||
}
|
||||
// Will the routine return a result
|
||||
virtual bool has_result() const override { return false; }
|
||||
// User defined destructor
|
||||
virtual ~UserWorkItemInt() {}
|
||||
~UserWorkItemInt() override = default;
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* test the time to run N tasks in parallel *
|
||||
******************************************************************/
|
||||
inline double run_parallel( ThreadPool *tpool, int N_tasks, int N_work )
|
||||
template<class Ret, class... Args>
|
||||
inline double launchAndTime( ThreadPool &tpool, int N, Ret ( *routine )( Args... ), Args... args )
|
||||
{
|
||||
// Make sure the thread pool is empty
|
||||
tpool->wait_pool_finished();
|
||||
// Add the work
|
||||
std::vector<ThreadPool::thread_id_t> ids;
|
||||
ids.reserve( N_tasks );
|
||||
tpool.wait_pool_finished();
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
for ( int i = 0; i < N_tasks; i++ )
|
||||
ids.push_back( TPOOL_ADD_WORK( tpool, waste_cpu, ( N_work ) ) );
|
||||
// Wait for the thread pool to finish
|
||||
tpool->wait_pool_finished();
|
||||
// Compute the time spent running the tasks
|
||||
for ( int i = 0; i < N; i++ )
|
||||
ThreadPool_add_work( &tpool, 0, routine, args... );
|
||||
tpool.wait_pool_finished();
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
return std::chrono::duration<double>( stop - start ).count();
|
||||
}
|
||||
@@ -417,17 +325,19 @@ void test_FIFO( UnitTest& ut, ThreadPool& tpool )
|
||||
{
|
||||
int rank = getRank();
|
||||
int size = getSize();
|
||||
const int N = 4000;
|
||||
for ( int r = 0; r < size; r++ ) {
|
||||
barrier();
|
||||
if ( r != rank )
|
||||
continue;
|
||||
std::vector<ThreadPool::thread_id_t> ids;
|
||||
for (size_t i=0; i<4000; i++)
|
||||
ids.push_back( TPOOL_ADD_WORK( &tpool, sleep_inc2, ( 0.001 ) ) );
|
||||
ids.reserve( N );
|
||||
for ( size_t i = 0; i < N; i++ )
|
||||
ids.emplace_back( TPOOL_ADD_WORK( &tpool, sleep_inc2, ( 0.001 ) ) );
|
||||
bool pass = true;
|
||||
while ( tpool.N_queued() > 0 ) {
|
||||
int i1 = -1, i2 = ids.size();
|
||||
for (size_t i=0; i<ids.size(); i++) {
|
||||
for ( int i = N - 1; i >= 0; i-- ) {
|
||||
bool started = ids[i].started();
|
||||
if ( started )
|
||||
i1 = std::max<int>( i1, i ); // Last index to processing item
|
||||
@@ -510,11 +420,7 @@ int main( int argc, char *argv[] )
|
||||
|
||||
// Get the number of processors availible
|
||||
barrier();
|
||||
int N_procs = 0;
|
||||
try {
|
||||
N_procs = ThreadPool::getNumberOfProcessors();
|
||||
} catch ( ... ) {
|
||||
}
|
||||
int N_procs = ThreadPool::getNumberOfProcessors();
|
||||
if ( N_procs > 0 )
|
||||
ut.passes( "getNumberOfProcessors" );
|
||||
else
|
||||
@@ -524,15 +430,11 @@ int main( int argc, char *argv[] )
|
||||
|
||||
// Get the processor affinities for the process
|
||||
barrier();
|
||||
std::vector<int> cpus;
|
||||
try {
|
||||
cpus = ThreadPool::getProcessAffinity();
|
||||
std::vector<int> cpus = ThreadPool::getProcessAffinity();
|
||||
printp( "%i cpus for current process: ", (int) cpus.size() );
|
||||
for ( size_t i = 0; i < cpus.size(); i++ )
|
||||
printp( "%i ", cpus[i] );
|
||||
for ( int cpu : cpus )
|
||||
printp( "%i ", cpu );
|
||||
printp( "\n" );
|
||||
} catch ( ... ) {
|
||||
}
|
||||
if ( !cpus.empty() ) {
|
||||
ut.passes( "getProcessAffinity" );
|
||||
} else {
|
||||
@@ -559,8 +461,8 @@ int main( int argc, char *argv[] )
|
||||
cpus = ThreadPool::getProcessAffinity();
|
||||
std::vector<int> cpus = ThreadPool::getProcessAffinity();
|
||||
printp( "%i cpus for current process (updated): ", (int) cpus.size() );
|
||||
for ( size_t i = 0; i < cpus.size(); i++ )
|
||||
printp( "%i ", cpus[i] );
|
||||
for ( int cpu : cpus )
|
||||
printp( "%i ", cpu );
|
||||
printp( "\n" );
|
||||
pass = cpus.size() > 1;
|
||||
} else {
|
||||
@@ -630,8 +532,8 @@ int main( int argc, char *argv[] )
|
||||
std::vector<int> procs_thread = tpool.getThreadAffinity( i );
|
||||
if ( procs_thread != procs ) {
|
||||
printp( "%i: Initial thread affinity: ", rank );
|
||||
for ( size_t i = 0; i < procs_thread.size(); i++ )
|
||||
printp( "%i ", procs_thread[i] );
|
||||
for ( int i : procs_thread )
|
||||
printp( "%i ", i );
|
||||
printp( "\n" );
|
||||
pass = false;
|
||||
}
|
||||
@@ -653,8 +555,8 @@ int main( int argc, char *argv[] )
|
||||
std::vector<int> procs_thread2 = tpool.getThreadAffinity( i );
|
||||
if ( procs_thread2 != procs_thread ) {
|
||||
printp( "%i: Final thread affinity: ", rank );
|
||||
for ( size_t i = 0; i < procs_thread.size(); i++ )
|
||||
printp( "%i ", procs_thread[i] );
|
||||
for ( int i : procs_thread )
|
||||
printp( "%i ", i );
|
||||
printp( "\n" );
|
||||
pass = false;
|
||||
}
|
||||
@@ -674,8 +576,8 @@ int main( int argc, char *argv[] )
|
||||
for ( int i = 0; i < N_threads; i++ ) {
|
||||
std::vector<int> procs_thread = tpool.getThreadAffinity( i );
|
||||
printp( "Thread affinity: " );
|
||||
for ( size_t i = 0; i < procs_thread.size(); i++ )
|
||||
printp( "%i ", procs_thread[i] );
|
||||
for ( int i : procs_thread )
|
||||
printp( "%i ", i );
|
||||
printp( "\n" );
|
||||
}
|
||||
|
||||
@@ -683,9 +585,7 @@ int main( int argc, char *argv[] )
|
||||
barrier();
|
||||
ThreadPool::set_OS_warnings( 1 );
|
||||
print_processor( &tpool );
|
||||
for ( int i = 0; i < N_threads; i++ )
|
||||
TPOOL_ADD_WORK( &tpool, print_processor, ( &tpool ) );
|
||||
tpool.wait_pool_finished();
|
||||
launchAndTime( tpool, N_threads, print_processor, &tpool );
|
||||
|
||||
// Run some basic tests
|
||||
barrier();
|
||||
@@ -730,18 +630,12 @@ int main( int argc, char *argv[] )
|
||||
sleep_inc( 1 );
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
double sleep_serial = std::chrono::duration<double>( stop - start ).count();
|
||||
ids2.clear();
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
for ( int i = 0; i < N_threads; i++ )
|
||||
ids2.push_back( TPOOL_ADD_WORK( &tpool, sleep_inc, ( 1 ) ) );
|
||||
tpool.wait_all( N_procs_used, &ids2[0] );
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
ids2.clear();
|
||||
double sleep_parallel = std::chrono::duration<double>(stop-start).count();
|
||||
double sleep_parallel = launchAndTime( tpool, N_threads, sleep_inc, 1 );
|
||||
double sleep_speedup = N_procs_used * sleep_serial / sleep_parallel;
|
||||
printf( "%i: Speedup on %i sleeping threads: %0.3f\n", rank, N_procs_used, sleep_speedup );
|
||||
printf( "%i: ts = %0.3f, tp = %0.3f\n", rank, sleep_serial, sleep_parallel );
|
||||
if ( fabs( sleep_serial - 1.0 ) < 0.05 && fabs( sleep_parallel - 1.0 ) < 0.25 && sleep_speedup>3 )
|
||||
if ( fabs( sleep_serial - 1.0 ) < 0.05 && fabs( sleep_parallel - 1.0 ) < 0.25 &&
|
||||
sleep_speedup > 3 )
|
||||
ut.passes( "Passed thread sleep" );
|
||||
else
|
||||
ut.failure( "Failed thread sleep" );
|
||||
@@ -773,8 +667,8 @@ int main( int argc, char *argv[] )
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
double time_serial = std::chrono::duration<double>( stop - start ).count();
|
||||
// Run in parallel
|
||||
double time_parallel2 = run_parallel( &tpool, N_procs_used, N / 1000 );
|
||||
double time_parallel = run_parallel( &tpool, N_procs_used, N );
|
||||
double time_parallel = launchAndTime( tpool, N_procs_used, waste_cpu, N );
|
||||
double time_parallel2 = launchAndTime( tpool, N_procs_used, waste_cpu, N / 1000 );
|
||||
double speedup = N_procs_used * time_serial / time_parallel;
|
||||
printf( "%i: Speedup on %i procs: %0.3f\n", rank, N_procs_used, speedup );
|
||||
printf( "%i: ts = %0.3f, tp = %0.3f, tp2 = %0.3f\n", rank, time_serial, time_parallel,
|
||||
@@ -849,8 +743,8 @@ int main( int argc, char *argv[] )
|
||||
id = tpool.add_work( wait3, 50 );
|
||||
tpool.wait( id );
|
||||
bool pass = true;
|
||||
for (size_t i=0; i<ids.size(); i++)
|
||||
pass = pass && ids[i].finished();
|
||||
for ( auto &id : ids )
|
||||
pass = pass && id.finished();
|
||||
ids.clear();
|
||||
if ( pass )
|
||||
ut.passes( "Dependencies2" );
|
||||
@@ -908,9 +802,9 @@ int main( int argc, char *argv[] )
|
||||
printp( " time = %0.0f ms\n", 1e3 * time );
|
||||
printp( " time / cycle = %0.0f us\n", 1e6 * time / N_it );
|
||||
printp( " average time / item = %0.0f ns\n", 1e9 * time / ( N_it * N_work ) );
|
||||
printp( " create = %i ns\n", static_cast<int>( time_create / ( N_it * N_work ) ) );
|
||||
printp( " run = %i ns\n", static_cast<int>( time_run / ( N_it * N_work ) ) );
|
||||
printp( " delete = %i us\n", static_cast<int>( time_delete / ( N_it * N_work ) ) );
|
||||
printp( " create = %i ns\n", time_create / ( N_it * N_work ) );
|
||||
printp( " run = %i ns\n", time_run / ( N_it * N_work ) );
|
||||
printp( " delete = %i us\n", time_delete / ( N_it * N_work ) );
|
||||
}
|
||||
|
||||
// Test the timing adding a single item
|
||||
@@ -950,8 +844,8 @@ int main( int argc, char *argv[] )
|
||||
printp( " time = %0.0f ms\n", 1e3 * time );
|
||||
printp( " time / cycle = %0.0f us\n", 1e6 * time / N_it );
|
||||
printp( " average time / item = %0.0f ns\n", 1e9 * time / ( N_it * N_work ) );
|
||||
printp( " create and add = %i ns\n", static_cast<int>( time_add / ( N_it * N_work ) ) );
|
||||
printp( " wait = %i us\n", static_cast<int>( time_wait / ( N_it * N_work ) ) );
|
||||
printp( " create and add = %i ns\n", time_add / ( N_it * N_work ) );
|
||||
printp( " wait = %i us\n", time_wait / ( N_it * N_work ) );
|
||||
}
|
||||
|
||||
// Test the timing pre-creating the work items and adding multiple at a time
|
||||
@@ -995,9 +889,9 @@ int main( int argc, char *argv[] )
|
||||
printp( " time = %0.0f ms\n", 1e3 * time );
|
||||
printp( " time / cycle = %0.0f us\n", 1e6 * time / N_it );
|
||||
printp( " average time / item = %0.0f ns\n", 1e9 * time / ( N_it * N_work ) );
|
||||
printp( " create = %i ns\n", static_cast<int>( time_create_work / ( N_it * N_work ) ) );
|
||||
printp( " add = %i ns\n", static_cast<int>( time_add_work / ( N_it * N_work ) ) );
|
||||
printp( " wait = %i ns\n", static_cast<int>( time_wait_work / ( N_it * N_work ) ) );
|
||||
printp( " create = %i ns\n", time_create_work / ( N_it * N_work ) );
|
||||
printp( " add = %i ns\n", time_add_work / ( N_it * N_work ) );
|
||||
printp( " wait = %i ns\n", time_wait_work / ( N_it * N_work ) );
|
||||
}
|
||||
|
||||
// Run a dependency test that tests a simple case that should keep the thread pool busy
|
||||
@@ -1035,8 +929,8 @@ int main( int argc, char *argv[] )
|
||||
barrier();
|
||||
pass = true;
|
||||
try {
|
||||
ThreadPool *tpool = new ThreadPool( MAX_NUM_THREADS - 1 );
|
||||
if ( tpool->getNumThreads() != MAX_NUM_THREADS - 1 )
|
||||
ThreadPool *tpool = new ThreadPool( ThreadPool::MAX_NUM_THREADS - 1 );
|
||||
if ( tpool->getNumThreads() != ThreadPool::MAX_NUM_THREADS - 1 )
|
||||
pass = false;
|
||||
if ( !ThreadPool::is_valid( tpool ) )
|
||||
pass = false;
|
||||
@@ -1056,14 +950,14 @@ int main( int argc, char *argv[] )
|
||||
// Print the test results
|
||||
barrier();
|
||||
ut.report();
|
||||
int N_errors = static_cast<int>( ut.NumFailGlobal() );
|
||||
auto N_errors = static_cast<int>( ut.NumFailGlobal() );
|
||||
|
||||
// Shudown MPI
|
||||
pout << "Shutting down\n";
|
||||
barrier();
|
||||
#ifdef USE_TIMER
|
||||
if ( rank == 0 )
|
||||
MemoryApp::print( std::cout );
|
||||
MemoryApp::print( pout );
|
||||
#endif
|
||||
#ifdef USE_MPI
|
||||
MPI_Finalize();
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
#include "ProfilerApp.h"
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <typeinfo>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <typeinfo>
|
||||
|
||||
|
||||
#define perr std::cerr
|
||||
@@ -22,6 +22,15 @@
|
||||
|
||||
// OS specific includes / definitions
|
||||
// clang-format off
|
||||
#if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 )
|
||||
#define USE_WINDOWS
|
||||
#elif defined( __APPLE__ )
|
||||
#define USE_MAC
|
||||
#elif defined( __linux ) || defined( __unix ) || defined( __posix )
|
||||
#define USE_LINUX
|
||||
#else
|
||||
#error Unknown OS
|
||||
#endif
|
||||
#if defined( USE_WINDOWS )
|
||||
#include <process.h>
|
||||
#include <windows.h>
|
||||
@@ -73,8 +82,9 @@
|
||||
} while ( 0 )
|
||||
#endif
|
||||
#if MONITOR_THREADPOOL_PERFORMANCE == 1
|
||||
#define accumulate( x, t1, t2 ) AtomicOperations::atomic_add( &x, \
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(t2-t1).count() );
|
||||
#define accumulate( x, t1, t2 ) \
|
||||
AtomicOperations::atomic_add( \
|
||||
&x, std::chrono::duration_cast<std::chrono::nanoseconds>( t2 - t1 ).count() );
|
||||
#endif
|
||||
|
||||
|
||||
@@ -87,7 +97,10 @@
|
||||
template<class T>
|
||||
void quicksort( int N, T *data );
|
||||
template<class T>
|
||||
inline void quicksort( std::vector<T> &x ) { quicksort((int)x.size(),x.data()); }
|
||||
inline void quicksort( std::vector<T> &x )
|
||||
{
|
||||
quicksort( (int) x.size(), x.data() );
|
||||
}
|
||||
static inline int find_id( int, const ThreadPool::thread_id_t *, const ThreadPool::thread_id_t & );
|
||||
|
||||
|
||||
@@ -130,6 +143,10 @@ static size_t rand_size_t()
|
||||
// We store the indicies to the queue list as short ints
|
||||
#error MAX_QUEUED must < 65535
|
||||
#endif
|
||||
// Check the c++ std
|
||||
#if CXX_STD == 98
|
||||
#error Thread pool class requires c++11 or newer
|
||||
#endif
|
||||
|
||||
|
||||
/******************************************************************
|
||||
@@ -163,7 +180,8 @@ static inline bool get_bit( const volatile AtomicOperations::int64_atomic *x, si
|
||||
{
|
||||
uint64_t mask = 0x01;
|
||||
mask <<= index % 64;
|
||||
AtomicOperations::int64_atomic y = x[index / 64]; // This is thread-safe since we only care about a single bit
|
||||
// This is thread-safe since we only care about a single bit
|
||||
AtomicOperations::int64_atomic y = x[index / 64];
|
||||
return ( y & mask ) != 0;
|
||||
}
|
||||
|
||||
@@ -194,7 +212,17 @@ static inline int count_bits( int_type x )
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* Set the bahvior of OS warnings *
|
||||
* Set the global constants *
|
||||
******************************************************************/
|
||||
constexpr int ThreadPool::MAX_NUM_THREADS;
|
||||
constexpr int ThreadPool::MAX_QUEUED;
|
||||
constexpr int ThreadPool::MAX_WAIT;
|
||||
constexpr bool ThreadPool::PROFILE_THREADPOOL_PERFORMANCE;
|
||||
constexpr bool ThreadPool::MONITOR_THREADPOOL_PERFORMANCE;
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* Set the behavior of OS warnings *
|
||||
******************************************************************/
|
||||
static int global_OS_behavior = 0;
|
||||
std::mutex OS_warning_mutex;
|
||||
@@ -213,7 +241,10 @@ static void OS_warning( const std::string &message )
|
||||
}
|
||||
OS_warning_mutex.unlock();
|
||||
}
|
||||
|
||||
void ThreadPool::setErrorHandler( std::function<void( const std::string & )> fun )
|
||||
{
|
||||
d_errorHandler = fun;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* Function to return the number of processors availible *
|
||||
@@ -517,7 +548,7 @@ void ThreadPool::check_startup( size_t size0 )
|
||||
ThreadPool::thread_id_t id;
|
||||
if ( id.getPriority() != -128 )
|
||||
pass = false;
|
||||
id.reset( 3, 564, NULL );
|
||||
id.reset( 3, 564, nullptr );
|
||||
if ( id.getPriority() != 3 || id.getLocalID() != 564 )
|
||||
pass = false;
|
||||
if ( count_bits( 0x0 ) != 0 || count_bits( 0x03 ) != 2 )
|
||||
@@ -530,8 +561,10 @@ void ThreadPool::check_startup( size_t size0 )
|
||||
if ( is_odd8( ~( (size_t) 0 ) ) || !is_odd8( thread_id_t::maxThreadID ) )
|
||||
pass = false;
|
||||
for ( size_t i = 0; i < 1024; i++ ) {
|
||||
if ( ( count_bits( thread_id_t::maxThreadID - i ) % 2 == 1 ) != is_odd8( thread_id_t::maxThreadID - i ) ) {
|
||||
printp( "%i %i %s\n", count_bits( thread_id_t::maxThreadID - i ), is_odd8( thread_id_t::maxThreadID - i ) ? 1 : 0,
|
||||
if ( ( count_bits( thread_id_t::maxThreadID - i ) % 2 == 1 ) !=
|
||||
is_odd8( thread_id_t::maxThreadID - i ) ) {
|
||||
printp( "%i %i %s\n", count_bits( thread_id_t::maxThreadID - i ),
|
||||
is_odd8( thread_id_t::maxThreadID - i ) ? 1 : 0,
|
||||
std::bitset<64>( thread_id_t::maxThreadID - i ).to_string().c_str() );
|
||||
pass = false;
|
||||
}
|
||||
@@ -566,11 +599,12 @@ void ThreadPool::initialize( const int N, const char *affinity, int N_procs, con
|
||||
d_N_added = 0;
|
||||
d_N_started = 0;
|
||||
d_N_finished = 0;
|
||||
d_max_wait_time = 600;
|
||||
memset( (void *) d_active, 0, MAX_NUM_THREADS / 8 );
|
||||
memset( (void *) d_cancel, 0, MAX_NUM_THREADS / 8 );
|
||||
d_wait_last = nullptr;
|
||||
for ( int i = 0; i < MAX_WAIT; i++ )
|
||||
d_wait[i] = nullptr;
|
||||
for ( auto &i : d_wait )
|
||||
i = nullptr;
|
||||
// Initialize the id
|
||||
d_id_assign = thread_id_t::maxThreadID;
|
||||
// Create the threads
|
||||
@@ -583,10 +617,10 @@ void ThreadPool::initialize( const int N, const char *affinity, int N_procs, con
|
||||
******************************************************************/
|
||||
ThreadPool::~ThreadPool()
|
||||
{
|
||||
if ( !is_valid( this ) ) {
|
||||
std::cerr << "Thread pool is not valid\n";
|
||||
std::terminate();
|
||||
}
|
||||
DISABLE_WARNINGS
|
||||
if ( !is_valid( this ) )
|
||||
throw std::logic_error( "Thread pool is not valid" );
|
||||
ENABLE_WARNINGS
|
||||
// Destroy the threads
|
||||
setNumThreads( 0 );
|
||||
// Delete all remaining data
|
||||
@@ -598,9 +632,8 @@ ThreadPool::~ThreadPool()
|
||||
// Print the performance metrics
|
||||
printp( "ThreadPool Performance:\n" );
|
||||
printp( "add_work: %lu us, %lu us, %lu us, %lu us, %lu us\n",
|
||||
total_add_work_time[0]/1000, total_add_work_time[1]/1000,
|
||||
total_add_work_time[2]/1000, total_add_work_time[3]/1000,
|
||||
total_add_work_time[4]/1000 );
|
||||
total_add_work_time[0] / 1000, total_add_work_time[1] / 1000, total_add_work_time[2] / 1000,
|
||||
total_add_work_time[3] / 1000, total_add_work_time[4] / 1000 );
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -643,8 +676,8 @@ void ThreadPool::setNumThreads(
|
||||
int d_N_threads_diff = num_worker_threads - d_N_threads;
|
||||
if ( d_N_threads_diff > 0 ) {
|
||||
// Check that no threads are in the process of being deleted
|
||||
for ( int i = 0; i < MAX_NUM_THREADS / 64; i++ ) {
|
||||
if ( d_cancel[i] != 0 )
|
||||
for ( long i : d_cancel ) {
|
||||
if ( i != 0 )
|
||||
throw std::logic_error(
|
||||
"Threads are being created and destroyed at the same time" );
|
||||
}
|
||||
@@ -670,11 +703,11 @@ void ThreadPool::setNumThreads(
|
||||
j++;
|
||||
}
|
||||
// Wait for all of the threads to finish initialization
|
||||
while ( 1 ) {
|
||||
while ( true ) {
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds( 25 ) );
|
||||
bool wait = false;
|
||||
for ( int i = 0; i < MAX_NUM_THREADS / 64; i++ ) {
|
||||
if ( d_cancel[i] != 0 )
|
||||
for ( long i : d_cancel ) {
|
||||
if ( i != 0 )
|
||||
wait = true;
|
||||
}
|
||||
if ( !wait )
|
||||
@@ -752,7 +785,7 @@ void ThreadPool::setNumThreads(
|
||||
}
|
||||
} else {
|
||||
// There are fewer cpus than threads, threads will share a processor
|
||||
int N_threads_proc =
|
||||
auto N_threads_proc =
|
||||
static_cast<int>( ( cpus.size() + d_N_threads - 1 ) / cpus.size() );
|
||||
for ( int i = 0; i < d_N_threads; i++ )
|
||||
t_procs[i].push_back( cpus[i / N_threads_proc] );
|
||||
@@ -797,8 +830,8 @@ void ThreadPool::tpool_thread( int thread_id )
|
||||
try {
|
||||
std::vector<int> cpus = ThreadPool::getProcessAffinity();
|
||||
printp( "%i cpus for current thread: ", (int) cpus.size() );
|
||||
for ( size_t i = 0; i < cpus.size(); i++ )
|
||||
printp( "%i ", cpus[i] );
|
||||
for ( int cpu : cpus )
|
||||
printp( "%i ", cpu );
|
||||
printp( "\n" );
|
||||
} catch ( ... ) {
|
||||
printp( "Unable to get process affinity\n" );
|
||||
@@ -811,7 +844,8 @@ void ThreadPool::tpool_thread( int thread_id )
|
||||
// Check if there is work to do
|
||||
if ( d_queue_list.size() > 0 ) {
|
||||
// Get next work item to process
|
||||
auto work_id = d_queue_list.remove( []( const thread_id_t& id ) { return id.ready(); } );
|
||||
auto work_id =
|
||||
d_queue_list.remove( []( const thread_id_t &id ) { return id.ready(); } );
|
||||
if ( work_id.isNull() ) {
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
@@ -821,14 +855,28 @@ void ThreadPool::tpool_thread( int thread_id )
|
||||
// Start work here
|
||||
PROFILE_THREADPOOL_START( "thread working" );
|
||||
work->d_state = 2;
|
||||
if ( d_errorHandler ) {
|
||||
try {
|
||||
work->run();
|
||||
} catch ( std::exception &e ) {
|
||||
auto msg = Utilities::stringf(
|
||||
"Error, caught exception in thread %i:\n %s\n", thread_id, e.what() );
|
||||
d_errorHandler( msg );
|
||||
} catch ( ... ) {
|
||||
auto msg = Utilities::stringf(
|
||||
"Error, caught unknown exception in thread %i\n", thread_id );
|
||||
d_errorHandler( msg );
|
||||
}
|
||||
} else {
|
||||
work->run();
|
||||
}
|
||||
work->d_state = 3;
|
||||
PROFILE_THREADPOOL_STOP( "thread working" );
|
||||
AtomicOperations::atomic_increment( &d_N_finished );
|
||||
// Check if any threads are waiting on the current work item
|
||||
// This can be done without blocking
|
||||
for ( int i = 0; i < MAX_WAIT; i++ ) {
|
||||
const wait_ids_struct *wait = const_cast<const wait_ids_struct *>(d_wait[i]);
|
||||
for ( auto &i : d_wait ) {
|
||||
auto wait = AtomicOperations::atomic_get( &i );
|
||||
if ( wait != nullptr )
|
||||
wait->id_finished( work_id );
|
||||
}
|
||||
@@ -878,7 +926,8 @@ inline void ThreadPool::add_work( const ThreadPool::thread_id_t& id )
|
||||
const auto &id1 = work->d_ids[i];
|
||||
if ( !id1.started() && id1 < id ) {
|
||||
// Remove and add the id back with a higher priority
|
||||
auto id2 = d_queue_list.remove( []( const thread_id_t& a, const thread_id_t& b ) { return a==b; }, id1 );
|
||||
auto id2 = d_queue_list.remove(
|
||||
[]( const thread_id_t &a, const thread_id_t &b ) { return a == b; }, id1 );
|
||||
id2.setPriority( std::max( priority, id2.getPriority() ) );
|
||||
d_queue_list.insert( id2 );
|
||||
}
|
||||
@@ -926,7 +975,7 @@ void ThreadPool::add_work(
|
||||
}
|
||||
// Wait for enough room in the queue (doesn't need blocking since it isn't that precise)
|
||||
if ( N > static_cast<size_t>( MAX_QUEUED - d_queue_list.size() ) ) {
|
||||
int N_wait = static_cast<int>( N - ( MAX_QUEUED - d_queue_list.size() ) );
|
||||
auto N_wait = static_cast<int>( N - ( MAX_QUEUED - d_queue_list.size() ) );
|
||||
while ( N_wait > 0 ) {
|
||||
d_signal_count = static_cast<unsigned char>( std::min( N_wait, 255 ) );
|
||||
d_wait_finished.wait_for( 1e-4 );
|
||||
@@ -964,17 +1013,6 @@ void ThreadPool::add_work(
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* This function removes a finished work item *
|
||||
******************************************************************/
|
||||
ThreadPool::WorkItem *ThreadPool::getFinishedWorkItem( ThreadPool::thread_id_t id ) const
|
||||
{
|
||||
if ( id.finished() )
|
||||
return id.work();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************
|
||||
* This function waits for a some of the work items to finish *
|
||||
******************************************************************/
|
||||
@@ -1027,7 +1065,7 @@ int ThreadPool::wait_some(
|
||||
throw std::logic_error( "Internal error: failed to wait" );
|
||||
// Delete the wait event struct
|
||||
// Note: we want to maintain the reference in case a thread is still using it
|
||||
// Note: technically this should be atomic
|
||||
// Note: technically this should be atomic, but it really isn't necessary here
|
||||
std::swap( d_wait_last, tmp );
|
||||
delete tmp;
|
||||
return N_finished;
|
||||
@@ -1037,23 +1075,25 @@ int ThreadPool::wait_some(
|
||||
/******************************************************************
|
||||
* This function waits for all of the threads to finish their work *
|
||||
******************************************************************/
|
||||
void ThreadPool::check_wait_time( std::chrono::time_point<std::chrono::high_resolution_clock>& t1 ) const
|
||||
void ThreadPool::check_wait_time(
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> &t1 ) const
|
||||
{
|
||||
auto t2 = std::chrono::high_resolution_clock::now();
|
||||
if ( std::chrono::duration_cast<std::chrono::seconds>(t2-t1).count() > MAX_WAIT_TIME_DEBUG ) {
|
||||
std::cout << "Warning: Maximum wait time in ThreadPool exceeded, threads may be hung\n";
|
||||
std::cout << "N_active: " << d_num_active << std::endl;
|
||||
std::cout << "N_queued: " << d_queue_list.size() << std::endl;
|
||||
std::cout << "N_added: " << d_N_added << std::endl;
|
||||
std::cout << "N_started: " << d_N_started << std::endl;
|
||||
std::cout << "N_finished: " << d_N_finished << std::endl;
|
||||
std::cout << "queue.insert(): " << d_queue_list.N_insert() << std::endl;
|
||||
std::cout << "queue.remove(): " << d_queue_list.N_remove() << std::endl;
|
||||
std::cout << "Stack Trace:\n";
|
||||
if ( std::chrono::duration_cast<std::chrono::seconds>( t2 - t1 ).count() > d_max_wait_time ) {
|
||||
pout << "Warning: Maximum wait time in ThreadPool exceeded, threads may be hung\n";
|
||||
pout << "N_active: " << d_num_active << std::endl;
|
||||
pout << "N_queued: " << d_queue_list.size() << std::endl;
|
||||
pout << "N_added: " << d_N_added << std::endl;
|
||||
pout << "N_started: " << d_N_started << std::endl;
|
||||
pout << "N_finished: " << d_N_finished << std::endl;
|
||||
pout << "queue.insert(): " << d_queue_list.N_insert() << std::endl;
|
||||
pout << "queue.remove(): " << d_queue_list.N_remove() << std::endl;
|
||||
pout << "Stack Trace:\n";
|
||||
auto call_stack = StackTrace::getAllCallStacks();
|
||||
StackTrace::cleanupStackTrace( call_stack );
|
||||
auto text = call_stack.print( " " );
|
||||
for ( auto &line : text )
|
||||
std::cout << line << std::endl;
|
||||
pout << line << std::endl;
|
||||
t1 = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
@@ -1077,12 +1117,10 @@ void ThreadPool::wait_pool_finished() const
|
||||
/******************************************************************
|
||||
* Member functions of wait_ids_struct *
|
||||
******************************************************************/
|
||||
ThreadPool::wait_ids_struct::wait_ids_struct( size_t N, const ThreadPool::thread_id_t *ids, size_t N_wait,
|
||||
AtomicOperations::pool<condition_variable,128>& cv_pool, int N_wait_list, volatile wait_ids_struct **list ):
|
||||
d_wait( N_wait ),
|
||||
d_N(0),
|
||||
d_cv_pool( cv_pool ),
|
||||
d_wait_event( cv_pool.get() )
|
||||
ThreadPool::wait_ids_struct::wait_ids_struct( size_t N, const ThreadPool::thread_id_t *ids,
|
||||
size_t N_wait, AtomicOperations::pool<condition_variable, 128> &cv_pool, int N_wait_list,
|
||||
volatile wait_ids_struct **list )
|
||||
: d_wait( N_wait ), d_N( 0 ), d_cv_pool( cv_pool ), d_wait_event( cv_pool.get() )
|
||||
{
|
||||
d_ids = new ThreadPool::thread_id_t[N];
|
||||
for ( size_t i = 0; i < N; i++ ) {
|
||||
@@ -1095,9 +1133,18 @@ ThreadPool::wait_ids_struct::wait_ids_struct( size_t N, const ThreadPool::thread
|
||||
d_finished = new bool[d_N];
|
||||
memset( (void *) d_finished, 0, d_N );
|
||||
int i = 0;
|
||||
while ( !AtomicOperations::atomic_compare_and_swap( (void *volatile *) &list[i], nullptr, this ) ) { i = (i+1)%N_wait_list; }
|
||||
while (
|
||||
!AtomicOperations::atomic_compare_and_swap( (void *volatile *) &list[i], nullptr, this ) ) {
|
||||
i = ( i + 1 ) % N_wait_list;
|
||||
}
|
||||
d_ptr = &list[i];
|
||||
}
|
||||
ThreadPool::wait_ids_struct::~wait_ids_struct()
|
||||
{
|
||||
d_cv_pool.put( d_wait_event );
|
||||
delete[] d_finished;
|
||||
delete[] d_ids;
|
||||
}
|
||||
void ThreadPool::wait_ids_struct::id_finished( const ThreadPool::thread_id_t &id ) const
|
||||
{
|
||||
int index = find_id( d_N, d_ids, id );
|
||||
@@ -1107,9 +1154,10 @@ void ThreadPool::wait_ids_struct::id_finished( const ThreadPool::thread_id_t& id
|
||||
for ( int i = 0; i < d_N; i++ )
|
||||
N_finished += d_finished[i] ? 1 : 0;
|
||||
if ( N_finished >= d_wait ) {
|
||||
*d_ptr = nullptr;
|
||||
d_wait = 0;
|
||||
d_N = 0;
|
||||
d_wait = 0;
|
||||
AtomicOperations::atomic_compare_and_swap(
|
||||
(void *volatile *) d_ptr, (void *) *d_ptr, nullptr );
|
||||
d_wait_event->notify_all();
|
||||
}
|
||||
}
|
||||
@@ -1132,7 +1180,8 @@ bool ThreadPool::wait_ids_struct::wait_for( double seconds )
|
||||
break;
|
||||
}
|
||||
auto t2 = std::chrono::high_resolution_clock::now();
|
||||
if ( 1e-6*std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count() > seconds )
|
||||
if ( 1e-6 * std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count() >
|
||||
seconds )
|
||||
return false;
|
||||
d_wait_event->wait_for( 1e-5 );
|
||||
}
|
||||
@@ -1154,7 +1203,7 @@ void quicksort( int n, T *arr )
|
||||
jstack = 0;
|
||||
l = 0;
|
||||
ir = n - 1;
|
||||
while ( 1 ) {
|
||||
while ( true ) {
|
||||
if ( ir - l < 7 ) { // Insertion sort when subarray small enough.
|
||||
for ( j = l + 1; j <= ir; j++ ) {
|
||||
a = arr[j];
|
||||
@@ -1292,7 +1341,7 @@ void ThreadPool::WorkItem::add_dependencies( size_t N, const ThreadPool::thread_
|
||||
const_cast<thread_id_t &>( ids[i] ).swap( tmp[i] );
|
||||
delete[] tmp;
|
||||
d_size = N2;
|
||||
int* lock = reinterpret_cast<int*>(&d_ids[d_size-1]);
|
||||
auto *lock = reinterpret_cast<int *>( &d_ids[d_size - 1] );
|
||||
*lock = 0;
|
||||
}
|
||||
const ThreadPool::thread_id_t id0;
|
||||
|
||||
@@ -3,53 +3,25 @@
|
||||
// PARTICULAR PURPOSE.
|
||||
#ifndef included_AtomicModelThreadPool
|
||||
#define included_AtomicModelThreadPool
|
||||
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <stdarg.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
#include "threadpool/atomic_helpers.h"
|
||||
#include "threadpool/atomic_list.h"
|
||||
|
||||
|
||||
// Choose the OS
|
||||
#if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 )
|
||||
// Using windows
|
||||
#define USE_WINDOWS
|
||||
#elif defined( __APPLE__ )
|
||||
// Using MAC
|
||||
#define USE_MAC
|
||||
#elif defined( __linux ) || defined( __unix ) || defined( __posix )
|
||||
// Using linux
|
||||
#define USE_LINUX
|
||||
#else
|
||||
#error Unknown OS
|
||||
#endif
|
||||
|
||||
|
||||
// Set some definitions
|
||||
#define MAX_NUM_THREADS 128 // The maximum number of threads (must be a multiple of 64)
|
||||
#define MAX_QUEUED 1024 // The maximum number of items in the work queue at any moment
|
||||
#define MAX_WAIT 16 // The maximum number of active waits at any given time
|
||||
#define MAX_WAIT_TIME_DEBUG 600 // The maximum time in a wait command before printing a warning message
|
||||
|
||||
#define PROFILE_THREADPOOL_PERFORMANCE 0 // Add profile timers to the threadpool (default is 0)
|
||||
#define MONITOR_THREADPOOL_PERFORMANCE 0 // Add detailed performance counters (default is 0)
|
||||
|
||||
|
||||
// Check the c++ std
|
||||
#if CXX_STD==98
|
||||
#error Thread pool class requires c++11 or newer
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
|
||||
|
||||
/** \class ThreadPool
|
||||
@@ -75,6 +47,13 @@
|
||||
*/
|
||||
class ThreadPool
|
||||
{
|
||||
public:
|
||||
///// Set some global properties
|
||||
constexpr static int MAX_NUM_THREADS = 128; // The maximum number of threads (must be a multiple of 64)
|
||||
constexpr static int MAX_QUEUED = 1024; // The maximum number of items in the work queue at any moment
|
||||
constexpr static int MAX_WAIT = 16; // The maximum number of active waits at any given time
|
||||
constexpr static bool PROFILE_THREADPOOL_PERFORMANCE = false; // Add profile timers to the threadpool
|
||||
constexpr static bool MONITOR_THREADPOOL_PERFORMANCE = false; // Add detailed performance counters
|
||||
|
||||
public:
|
||||
///// Member classes
|
||||
@@ -102,7 +81,7 @@ public:
|
||||
inline thread_id_t( volatile thread_id_t &&rhs );
|
||||
inline thread_id_t &operator=( const thread_id_t &rhs ) volatile;
|
||||
inline thread_id_t &operator=( volatile thread_id_t &&rhs ) volatile;
|
||||
#ifndef USE_WINDOWS
|
||||
#if !defined( WIN32 ) && !defined( _WIN32 ) && !defined( WIN64 ) && !defined( _WIN64 )
|
||||
inline thread_id_t( const thread_id_t &rhs );
|
||||
inline thread_id_t &operator=( thread_id_t &&rhs );
|
||||
inline thread_id_t &operator=( const thread_id_t &rhs );
|
||||
@@ -245,7 +224,7 @@ public:
|
||||
//! Run the work item
|
||||
virtual void run() override = 0;
|
||||
//! Will the routine return a result
|
||||
virtual bool has_result() const override = 0;
|
||||
virtual bool has_result() const override final { return !std::is_same<return_type,void>::value; }
|
||||
//! Return the results
|
||||
return_type get_results() const { return d_result; }
|
||||
//! Virtual destructor
|
||||
@@ -353,10 +332,12 @@ public:
|
||||
* in the ThreadPool without checking the existing work unless the desired number of
|
||||
* threads is 0. In this case, the function will wait for all work items to finish
|
||||
* before deleting the existing work threads.
|
||||
|
||||
* Member threads may not call this function.
|
||||
* @param N The desired number of worker threads
|
||||
* @param affinity The affinity scheduler to use:
|
||||
* none - Let the OS handle the affinities (default)
|
||||
|
||||
* independent - Give each thread an independent set of processors
|
||||
* @param procs The processors to use (defaults to the process affinitiy list)
|
||||
*/
|
||||
@@ -368,6 +349,16 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Function to set the maximum wait time
|
||||
* \details This function sets the maximum time the thread pool will
|
||||
* wait before warning about a possible hung thread.
|
||||
* Default is to wait 10 minutes.
|
||||
* @param time The number of seconds to wait (seconds)
|
||||
*/
|
||||
inline void setMaxWaitTimeDebug( const int time ) { d_max_wait_time = time; }
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Function to return the current thread number
|
||||
* \details This function will return the thread number of current active thread.
|
||||
@@ -400,16 +391,14 @@ public:
|
||||
* @param id The id of the work item
|
||||
*/
|
||||
template <class return_type>
|
||||
inline return_type getFunctionRet( const thread_id_t &id ) const;
|
||||
static inline return_type getFunctionRet( const thread_id_t &id );
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Function to create a work item
|
||||
* \details This function creates a work item that can be added to the queue
|
||||
* @param work Pointer to the work item to add
|
||||
* Note that the threadpool will automatically destroy the item when
|
||||
* finished
|
||||
* @param priority A value indicating the priority of the work item (0-default)
|
||||
* @param routine Function to call from the thread pool
|
||||
* @param args Function arguments to pass
|
||||
*/
|
||||
template <class Ret, class... Args>
|
||||
static inline WorkItem* createWork( Ret( *routine )( Args... ), Args... args );
|
||||
@@ -505,6 +494,7 @@ public:
|
||||
* If successful it returns the indicies of the finished work items (the index in the array ids).
|
||||
* Note: any thread may call this routine, but they will block until finished.
|
||||
* For worker threads this may eventually lead to a deadlock.
|
||||
* @param N_wait Number of work items to wait for
|
||||
* @param ids Vector of work items to wait for
|
||||
*/
|
||||
inline std::vector<int> wait_some( int N_wait, const std::vector<thread_id_t> &ids ) const;
|
||||
@@ -552,6 +542,69 @@ public:
|
||||
//! Return the number of items queued
|
||||
int N_queued( ) const { return d_queue_list.size(); }
|
||||
|
||||
|
||||
//! Set the error handler for threads
|
||||
void setErrorHandler( std::function<void(const std::string&)> fun );
|
||||
|
||||
|
||||
public: // Static interface
|
||||
|
||||
/*!
|
||||
* \brief Function to return the number of work threads
|
||||
* \details This function returns the number of threads in the thread pool,
|
||||
* or 0 if the thread pool is empty or does not exist
|
||||
* @param tpool Threadpool to add work to (may be null)
|
||||
*/
|
||||
static inline int numThreads( const ThreadPool* tpool ) { return tpool ? tpool->getNumThreads() : 0; }
|
||||
|
||||
/*!
|
||||
* \brief Function to add a work item
|
||||
* \details This function adds a work item to the queue
|
||||
* Note: any thread may call this routine.
|
||||
* @param tpool Threadpool to add work to (may be null)
|
||||
* @param work Pointer to the work item to add
|
||||
* Note that the threadpool will automatically destroy the item when finished
|
||||
* @param priority A value indicating the priority of the work item (0-default)
|
||||
*/
|
||||
static inline thread_id_t add_work( ThreadPool* tpool, ThreadPool::WorkItem *work, int priority = 0 );
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Function to add multiple work items
|
||||
* \details This function adds multiple work item to the queue
|
||||
* Note: any thread may call this routine.
|
||||
* @param tpool Threadpool to add work to (may be null)
|
||||
* @param work Vector of pointers to the work items to add
|
||||
* Note that the threadpool will automatically destroy the item when finished
|
||||
* @param priority Vector of values indicating the priority of the work items
|
||||
*/
|
||||
static inline std::vector<thread_id_t> add_work( ThreadPool* tpool, const std::vector<ThreadPool::WorkItem *> &work,
|
||||
const std::vector<int> &priority = std::vector<int>() );
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Function to wait until all of the given work items have finished their work
|
||||
* \details This is the function waits for all given of the work items to finish. It returns 0
|
||||
* if successful.
|
||||
* Note: any thread may call this routine, but they will block until finished.
|
||||
* For worker threads this may eventually lead to a deadlock.
|
||||
* @param tpool Threadpool containing work (must match call to add_work)
|
||||
* @param ids Vector of work items to wait for
|
||||
*/
|
||||
static inline int wait_all( const ThreadPool* tpool, const std::vector<thread_id_t> &ids );
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Function to wait until all work items in the thread pool have finished their work
|
||||
* \details This function will wait until all work has finished.
|
||||
* Note: member threads may not call this function.
|
||||
* Only one non-member thread should call this routine at a time.
|
||||
* @param tpool Threadpool containing work (must match call to add_work)
|
||||
*/
|
||||
static inline void wait_pool_finished( const ThreadPool* tpool ) { if ( tpool ) { tpool->wait_pool_finished(); } }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
typedef AtomicOperations::int32_atomic int32_atomic;
|
||||
|
||||
@@ -593,7 +646,7 @@ private:
|
||||
public:
|
||||
wait_ids_struct( size_t N, const ThreadPool::thread_id_t *ids, size_t N_wait,
|
||||
AtomicOperations::pool<condition_variable,128>& cv_pool, int N_wait_list, volatile wait_ids_struct **list );
|
||||
~wait_ids_struct( ) { d_cv_pool.put( d_wait_event ); delete [] d_finished; delete [] d_ids; }
|
||||
~wait_ids_struct( );
|
||||
void id_finished( const ThreadPool::thread_id_t& id ) const;
|
||||
bool wait_for( double seconds );
|
||||
private:
|
||||
@@ -628,7 +681,10 @@ private:
|
||||
inline void add_work( const ThreadPool::thread_id_t& id );
|
||||
|
||||
// Function to get a work item that has finished
|
||||
WorkItem *getFinishedWorkItem( ThreadPool::thread_id_t id ) const;
|
||||
static inline WorkItem *getFinishedWorkItem( const ThreadPool::thread_id_t& id )
|
||||
{
|
||||
return id.finished() ? id.work():nullptr;
|
||||
}
|
||||
|
||||
// This function provides a wrapper (needed for the threads)
|
||||
static inline void create_new_thread( ThreadPool *tpool, int id )
|
||||
@@ -676,10 +732,13 @@ private:
|
||||
std::thread::id d_threadId[MAX_NUM_THREADS]; // Unique id for each thread
|
||||
queue_type d_queue_list; // The work queue
|
||||
size_t d_NULL_TAIL; // Null data buffer to check memory bounds
|
||||
int d_max_wait_time; // The maximum time in a wait command before printing a warning message
|
||||
std::function<void(const std::string&)> d_errorHandler;
|
||||
};
|
||||
|
||||
|
||||
#include "threadpool/thread_pool.hpp"
|
||||
|
||||
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
#define TPOOL_TUPLE_TO_SEQ( t ) TPOOL_TUPLE_TO_SEQ_##II t
|
||||
#define TPOOL_TUPLE_TO_SEQ_II( a, ... ) a, ##__VA_ARGS__
|
||||
#ifdef USE_WINDOWS
|
||||
#if defined( WIN32 ) || defined( _WIN32 ) || defined( WIN64 ) || defined( _WIN64 )
|
||||
#define TPOOL_GET_PRIORITY( a, N, c, ... ) N
|
||||
#define TPOOL_ADD_WORK( TPOOL, FUNCTION, ARGS, ... ) \
|
||||
ThreadPool_add_work( TPOOL, TPOOL_GET_PRIORITY( 0, __VA_ARGS__, 0, 0 ) + 0, FUNCTION, \
|
||||
@@ -40,7 +40,6 @@
|
||||
// \cond HIDDEN_SYMBOLS
|
||||
|
||||
|
||||
|
||||
// Unpack a tuple and call a function
|
||||
template<int...>
|
||||
struct index_tuple {
|
||||
@@ -59,7 +58,8 @@ template <typename... Types>
|
||||
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {
|
||||
};
|
||||
template<class Ret, class... Args, int... Indexes>
|
||||
inline Ret apply_helper( Ret ( *pf )( Args... ), index_tuple<Indexes...>, std::tuple<Args...> &&tup )
|
||||
inline Ret apply_helper(
|
||||
Ret ( *pf )( Args... ), index_tuple<Indexes...>, std::tuple<Args...> &&tup )
|
||||
{
|
||||
return pf( std::forward<Args>( std::get<Indexes>( tup ) )... );
|
||||
}
|
||||
@@ -82,9 +82,9 @@ class ThreadPool::WorkItemRet<void> : public ThreadPool::WorkItem
|
||||
{
|
||||
public:
|
||||
virtual void run() override = 0;
|
||||
virtual bool has_result() const override { return false; }
|
||||
void get_results() {}
|
||||
virtual ~WorkItemRet() {}
|
||||
virtual bool has_result() const override final { return false; }
|
||||
};
|
||||
|
||||
|
||||
@@ -104,11 +104,7 @@ public:
|
||||
: ThreadPool::WorkItemRet<void>(), routine( routine2 ), args( ts... )
|
||||
{
|
||||
}
|
||||
virtual void run() override
|
||||
{
|
||||
apply( routine, args );
|
||||
}
|
||||
virtual bool has_result() const override { return false; }
|
||||
virtual void run() override { apply( routine, args ); }
|
||||
virtual ~WorkItemFull() {}
|
||||
};
|
||||
template<class Ret, class... Args>
|
||||
@@ -124,11 +120,7 @@ public:
|
||||
: ThreadPool::WorkItemRet<Ret>(), routine( routine2 ), args( ts... )
|
||||
{
|
||||
}
|
||||
virtual void run() override
|
||||
{
|
||||
this->d_result = apply( routine, args );
|
||||
}
|
||||
virtual bool has_result() const override { return true; }
|
||||
virtual void run() override { this->d_result = apply( routine, args ); }
|
||||
virtual ~WorkItemFull() {}
|
||||
};
|
||||
|
||||
@@ -138,15 +130,15 @@ template <class Ret, class... Ts>
|
||||
inline ThreadPool::thread_id_t ThreadPool_add_work(
|
||||
ThreadPool *tpool, int priority, Ret ( *routine )( Ts... ), Ts... ts )
|
||||
{
|
||||
ThreadPool::WorkItem *work = new WorkItemFull<Ret, Ts...>( routine, ts... );
|
||||
return tpool->add_work( work, priority );
|
||||
auto work = new WorkItemFull<Ret, Ts...>( routine, ts... );
|
||||
return ThreadPool::add_work( tpool, work, priority );
|
||||
}
|
||||
template<class Ret>
|
||||
inline ThreadPool::thread_id_t ThreadPool_add_work(
|
||||
ThreadPool *tpool, int priority, Ret ( *routine )(), void * )
|
||||
{
|
||||
ThreadPool::WorkItem *work = new WorkItemFull<Ret>( routine );
|
||||
return tpool->add_work( work, priority );
|
||||
auto work = new WorkItemFull<Ret>( routine );
|
||||
return ThreadPool::add_work( tpool, work, priority );
|
||||
}
|
||||
template<class Ret, class... Args>
|
||||
inline ThreadPool::WorkItem *ThreadPool::createWork( Ret ( *routine )( Args... ), Args... args )
|
||||
@@ -158,6 +150,7 @@ inline ThreadPool::WorkItem* ThreadPool::createWork( Ret( *routine )( Args... ),
|
||||
/******************************************************************
|
||||
* Function to get the returned function value *
|
||||
******************************************************************/
|
||||
// clang-format off
|
||||
template<class T> inline constexpr T zeroConstructor();
|
||||
template<> inline constexpr bool zeroConstructor<bool>() { return false; }
|
||||
template<> inline constexpr char zeroConstructor<char>() { return 0; }
|
||||
@@ -170,11 +163,12 @@ template<> inline constexpr float zeroConstructor<float>( ) { return 0; }
|
||||
template<> inline constexpr double zeroConstructor<double>() { return 0; }
|
||||
template<class T> inline constexpr T zeroConstructor() { return T(); }
|
||||
template<class Ret>
|
||||
inline Ret ThreadPool::getFunctionRet( const ThreadPool::thread_id_t &id ) const
|
||||
inline Ret ThreadPool::getFunctionRet( const ThreadPool::thread_id_t &id )
|
||||
{
|
||||
WorkItemRet<Ret> *work = dynamic_cast<WorkItemRet<Ret>*>( getFinishedWorkItem( id ) );
|
||||
auto work = dynamic_cast<WorkItemRet<Ret> *>( getFinishedWorkItem( id ) );
|
||||
return work == nullptr ? zeroConstructor<Ret>() : work->get_results();
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
|
||||
/******************************************************************
|
||||
@@ -234,7 +228,14 @@ inline int ThreadPool::wait_all( const std::vector<thread_id_t> &ids ) const
|
||||
delete[] finished;
|
||||
return 0;
|
||||
}
|
||||
inline std::vector<int> ThreadPool::wait_some( int N_wait, const std::vector<thread_id_t> &ids ) const
|
||||
inline int ThreadPool::wait_all( const ThreadPool *tpool, const std::vector<thread_id_t> &ids )
|
||||
{
|
||||
if ( tpool )
|
||||
return tpool->wait_all( ids );
|
||||
return ids.size();
|
||||
}
|
||||
inline std::vector<int> ThreadPool::wait_some(
|
||||
int N_wait, const std::vector<thread_id_t> &ids ) const
|
||||
{
|
||||
auto finished = new bool[ids.size()];
|
||||
int N_finished = wait_some( ids.size(), ids.data(), N_wait, finished );
|
||||
@@ -280,6 +281,32 @@ inline std::vector<ThreadPool::thread_id_t> ThreadPool::add_work(
|
||||
delete[] priority2;
|
||||
return ids;
|
||||
}
|
||||
inline ThreadPool::thread_id_t ThreadPool::add_work(
|
||||
ThreadPool *tpool, ThreadPool::WorkItem *work, int priority )
|
||||
{
|
||||
ThreadPool::thread_id_t id;
|
||||
if ( tpool ) {
|
||||
id = tpool->add_work( work, priority );
|
||||
} else {
|
||||
id.reset( priority, std::rand(), work );
|
||||
work->d_state = 2;
|
||||
work->run();
|
||||
work->d_state = 3;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
inline std::vector<ThreadPool::thread_id_t> ThreadPool::add_work( ThreadPool *tpool,
|
||||
const std::vector<ThreadPool::WorkItem *> &work, const std::vector<int> &priority )
|
||||
{
|
||||
if ( tpool ) {
|
||||
return tpool->add_work( work, priority );
|
||||
} else {
|
||||
std::vector<ThreadPool::thread_id_t> ids( work.size() );
|
||||
for ( size_t i = 0; i < work.size(); i++ )
|
||||
ids[i] = add_work( tpool, work[i], priority[i] );
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************
|
||||
@@ -326,7 +353,7 @@ inline ThreadPool::thread_id_t::thread_id_t( const volatile ThreadPool::thread_i
|
||||
if ( d_count != NULL )
|
||||
AtomicOperations::atomic_increment( d_count );
|
||||
}
|
||||
#ifndef USE_WINDOWS
|
||||
#if !defined( WIN32 ) && !defined( _WIN32 ) && !defined( WIN64 ) && !defined( _WIN64 )
|
||||
inline ThreadPool::thread_id_t::thread_id_t( const thread_id_t &rhs )
|
||||
: d_id( rhs.d_id ), d_count( rhs.d_count ), d_work( rhs.d_work )
|
||||
{
|
||||
@@ -488,7 +515,8 @@ inline bool ThreadPool::isValid( const ThreadPool::thread_id_t &id ) const
|
||||
static_assert( sizeof( atomic_64 ) == 8, "atomic_64 must be a 64-bit integer" );
|
||||
uint64_t local_id = id.getLocalID();
|
||||
uint64_t next_id = d_id_assign - 1;
|
||||
return local_id!=0 && id.initialized() && local_id<=thread_id_t::maxThreadID && local_id>next_id;
|
||||
return local_id != 0 && id.initialized() && local_id <= thread_id_t::maxThreadID &&
|
||||
local_id > next_id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user