@@ -1,6 +1,9 @@ | |||||
# project | # project | ||||
resources/ | resources/ | ||||
bin/ | bin/ | ||||
out/ | |||||
mat/ | |||||
mtx/ | |||||
# eclipse | # eclipse | ||||
.project | .project | ||||
@@ -38,9 +38,10 @@ DEP_DIR := $(BUILD_DIR)/.dep | |||||
# ========== Compiler settings ========== | # ========== Compiler settings ========== | ||||
# Compiler flags for debug and release | # Compiler flags for debug and release | ||||
DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c++14 | DEB_CFLAGS := -DDEBUG -g3 -Wall -Wextra -std=c++14 | ||||
REL_CFLAGS := -Wall -Wextra -O2 -std=c++14 | |||||
REL_CFLAGS := -DDEBUG -g3 -Wall -Wextra -O2 -std=c++14 | |||||
# Pre-defines | # Pre-defines | ||||
# PRE_DEFS := MYCAB=1729 SUPER_MODE | # PRE_DEFS := MYCAB=1729 SUPER_MODE | ||||
PRE_DEFS := | |||||
# ============== Linker settings ============== | # ============== Linker settings ============== | ||||
# Linker flags (example: -pthread -lm) | # Linker flags (example: -pthread -lm) | ||||
@@ -105,7 +106,7 @@ DEP := $(foreach file,$(SRC:%.cpp=%.d),$(DEP_DIR)/$(file)) | |||||
# Invoke cpp to create makefile rules with dependencies for each source file | # Invoke cpp to create makefile rules with dependencies for each source file | ||||
$(DEP_DIR)/%.d: %.cpp | $(DEP_DIR)/%.d: %.cpp | ||||
@mkdir -p $(@D) | @mkdir -p $(@D) | ||||
@$(DOCKER) $(CXX) -E $(CFLAGS) $(INC) $(DEF) -MM -MT $(OBJ_DIR)/$(<:.cpp=.o) -MF $@ $< | |||||
$(DOCKER) $(CXX) -E $(CFLAGS) $(INC) $(DEF) -MM -MT $(OBJ_DIR)/$(<:.cpp=.o) -MF $@ $< | |||||
# objects depent on .cpp AND dependency files, which have an empty recipe | # objects depent on .cpp AND dependency files, which have an empty recipe | ||||
$(OBJ_DIR)/%.o: %.cpp $(DEP_DIR)/%.d | $(OBJ_DIR)/%.o: %.cpp $(DEP_DIR)/%.d | ||||
@@ -137,29 +138,82 @@ clean: | |||||
@rm -rf $(DEP_DIR) | @rm -rf $(DEP_DIR) | ||||
@rm -rf $(BUILD_DIR) | @rm -rf $(BUILD_DIR) | ||||
# | # | ||||
# ================ Local build rules ================= | # ================ Local build rules ================= | ||||
# examples: | # examples: | ||||
# make debug | # make debug | ||||
.PHONY: debug | |||||
debug: CFLAGS := $(DEB_CFLAGS) | debug: CFLAGS := $(DEB_CFLAGS) | ||||
debug: $(BUILD_DIR)/$(TARGET) | debug: $(BUILD_DIR)/$(TARGET) | ||||
.PHONY: release | |||||
release: CFLAGS := $(REL_CFLAGS) | release: CFLAGS := $(REL_CFLAGS) | ||||
release: $(BUILD_DIR)/$(TARGET) | release: $(BUILD_DIR)/$(TARGET) | ||||
.PHONY: all | |||||
all: release | all: release | ||||
local_v3: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=V3 | |||||
local_v3: TARGET := local_v3 | |||||
local_v3: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
local_v4: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=V4 | |||||
local_v4: TARGET := local_v4 | |||||
local_v4: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
v3: DOCKER := $(DOCKER_CMD) | |||||
v3: CFLAGS := $(REL_CFLAGS) -DCODE_VERSION=V3 | |||||
v3: TARGET := tcount_v3 | |||||
v3: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
v3_cilk: DOCKER := $(DOCKER_CMD) | |||||
v3_cilk: CXX := /usr/local/OpenCilk-9.0.1-Linux/bin/clang++ | |||||
v3_cilk: CFLAGS := $(REL_CFLAGS) -fcilkplus -DCODE_VERSION=V3 -DCILK | |||||
v3_cilk: LDFLAGS += -fcilkplus | |||||
v3_cilk: TARGET := tcount_cilkv3 | |||||
v3_cilk: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
v3_omp: DOCKER := $(DOCKER_CMD) | |||||
v3_omp: CFLAGS := $(REL_CFLAGS) -fopenmp -DCODE_VERSION=V3 -DOMP | |||||
v3_omp: LDFLAGS += -fopenmp | |||||
v3_omp: TARGET := tcount_ompv3 | |||||
v3_omp: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
v4: DOCKER := $(DOCKER_CMD) | |||||
v4: CFLAGS := $(REL_CFLAGS) -DCODE_VERSION=V4 | |||||
v4: TARGET := tcount_v4 | |||||
v4: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
v4_cilk: DOCKER := $(DOCKER_CMD) | |||||
v4_cilk: CXX := /usr/local/OpenCilk-9.0.1-Linux/bin/clang++ | |||||
v4_cilk: CFLAGS := $(REL_CFLAGS) -fcilkplus -DCODE_VERSION=V4 -DCILK | |||||
v4_cilk: LDFLAGS += -fcilkplus | |||||
v4_cilk: TARGET := tcount_cilkv4 | |||||
v4_cilk: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
v4_omp: DOCKER := $(DOCKER_CMD) | |||||
v4_omp: CFLAGS := $(REL_CFLAGS) -fopenmp -DCODE_VERSION=V4 -DOMP | |||||
v4_omp: LDFLAGS += -fopenmp | |||||
v4_omp: TARGET := tcount_ompv4 | |||||
v4_omp: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
v4_pthreads: DOCKER := $(DOCKER_CMD) | |||||
v4_pthreads: CFLAGS := $(REL_CFLAGS) -DCODE_VERSION=V4 -DTHREADS | |||||
v4_pthreads: TARGET := tcount_pthv4 | |||||
v4_pthreads: $(BUILD_DIR)/$(TARGET) | |||||
cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) | |||||
# | # | ||||
# ================ Docker based rules ================ | # ================ Docker based rules ================ | ||||
# examples: | # examples: | ||||
# make IMAGE="gcc:8.3" dock | # make IMAGE="gcc:8.3" dock | ||||
# | # | ||||
.PHONY: dock | |||||
dock: DOCKER := $(DOCKER_CMD) | dock: DOCKER := $(DOCKER_CMD) | ||||
dock: CFLAGS := $(REL_CFLAGS) | dock: CFLAGS := $(REL_CFLAGS) | ||||
dock: $(BUILD_DIR)/$(TARGET) | dock: $(BUILD_DIR)/$(TARGET) | ||||
@@ -14,14 +14,19 @@ | |||||
#include <v3.h> | #include <v3.h> | ||||
#include <v4.h> | #include <v4.h> | ||||
/* | |||||
* Defines for different version of the exercise | |||||
*/ | |||||
#define V12 2 | #define V12 2 | ||||
#define V3 3 | #define V3 3 | ||||
#define V4 4 | #define V4 4 | ||||
// Fail-safe verision selection | |||||
#if !defined CODE_VERSION | #if !defined CODE_VERSION | ||||
#define CODE_VERSION V4 | #define CODE_VERSION V4 | ||||
#endif | #endif | ||||
// matrix alias template dispatcher based on pre-define flag from compiler (see Makefile) | |||||
#if CODE_VERSION == V12 | #if CODE_VERSION == V12 | ||||
using namespace v12; | using namespace v12; | ||||
using matrix = v12::matrix; | using matrix = v12::matrix; | ||||
@@ -168,25 +168,42 @@ Matrix<DataType, IndexType, Type> make_Matrix(IndexType s) { | |||||
return Matrix<DataType, IndexType, Type>(new DataType[Matrix<DataType, IndexType, Type>::capacity(s)], s); | return Matrix<DataType, IndexType, Type>(new DataType[Matrix<DataType, IndexType, Type>::capacity(s)], s); | ||||
} | } | ||||
template<typename DataType, typename IndexType> | |||||
using CooVal = std::tuple<DataType, IndexType, IndexType>; | |||||
/** | |||||
* A simple sparse matrix implementation. | |||||
* | |||||
* We use CSC format and provide get/set functionalities for each (i,j) item | |||||
* on the matrix. We also provide a () overload using a proxy SpMatVal object. | |||||
* This way the user can: | |||||
* \code | |||||
* auto v = A(3,4); | |||||
* A(3, 4) = 7; | |||||
* \endcode | |||||
* | |||||
* We also provide getCol() and getRow() functions witch return a viwer/iterator to rows and | |||||
* columns of the matrix. In the case of a symmetric matrix instead of a row we return the | |||||
* equivalent column. This way we gain speed due to CSC format nature. | |||||
* | |||||
* @tparam DataType The type for values | |||||
* @tparam IndexType The type for indexes | |||||
* @tparam Type The Matrix type (FULL or SYMMETRIC) | |||||
*/ | |||||
template<typename DataType, typename IndexType, MatrixType Type> | template<typename DataType, typename IndexType, MatrixType Type> | ||||
struct SpMat { | struct SpMat { | ||||
using dataType = DataType; //!< meta:export of underling data type | using dataType = DataType; //!< meta:export of underling data type | ||||
using indexType = IndexType; //!< meta:export of underling index type | using indexType = IndexType; //!< meta:export of underling index type | ||||
static constexpr MatrixType matrixType = Type; //!< export of array type | static constexpr MatrixType matrixType = Type; //!< export of array type | ||||
friend class SpMatCol<DataType, IndexType>; | |||||
friend class SpMatRow<DataType, IndexType>; | |||||
friend class SpMatVal<DataType, IndexType>; | |||||
friend struct SpMatCol<DataType, IndexType>; | |||||
friend struct SpMatRow<DataType, IndexType>; | |||||
friend struct SpMatVal<DataType, IndexType>; | |||||
/*! | /*! | ||||
* \name Obj lifetime | * \name Obj lifetime | ||||
*/ | */ | ||||
//! @{ | //! @{ | ||||
//! allocation with init value ctor | |||||
//! Default ctor with optional memory allocations | |||||
SpMat(IndexType n=IndexType{}, IndexType nnz=IndexType{}) : | SpMat(IndexType n=IndexType{}, IndexType nnz=IndexType{}) : | ||||
values(nnz, DataType{}), | values(nnz, DataType{}), | ||||
rows(nnz, IndexType{}), | rows(nnz, IndexType{}), | ||||
@@ -194,6 +211,7 @@ struct SpMat { | |||||
N(n), | N(n), | ||||
NNZ(nnz) { } | NNZ(nnz) { } | ||||
//! A ctor using csc array data | |||||
SpMat(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) : | SpMat(IndexType n, IndexType nnz, const IndexType* row, const IndexType* col) : | ||||
values(nnz, 1), | values(nnz, 1), | ||||
rows(row, row+nnz), | rows(row, row+nnz), | ||||
@@ -201,6 +219,7 @@ struct SpMat { | |||||
N(n), | N(n), | ||||
NNZ(nnz) { } | NNZ(nnz) { } | ||||
//! ctor using csc array data with value array | |||||
SpMat(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) : | SpMat(IndexType n, IndexType nnz, const DataType* v, const IndexType* row, const IndexType* col) : | ||||
values(v, v+nnz), | values(v, v+nnz), | ||||
rows(row, row+nnz), | rows(row, row+nnz), | ||||
@@ -208,6 +227,7 @@ struct SpMat { | |||||
N(n), | N(n), | ||||
NNZ(nnz) { } | NNZ(nnz) { } | ||||
//! ctor vectors of row/col and default value for values array | |||||
SpMat(IndexType n, IndexType nnz, const DataType v, const std::vector<IndexType>& row, const std::vector<IndexType>& col) : | SpMat(IndexType n, IndexType nnz, const DataType v, const std::vector<IndexType>& row, const std::vector<IndexType>& col) : | ||||
values(nnz, v), | values(nnz, v), | ||||
rows (row), | rows (row), | ||||
@@ -219,38 +239,72 @@ struct SpMat { | |||||
SpMat(SpMat&& a) noexcept { moves(std::move(a)); } | SpMat(SpMat&& a) noexcept { moves(std::move(a)); } | ||||
//! move | //! move | ||||
SpMat& operator=(SpMat&& a) noexcept { moves(std::move(a)); return *this; } | SpMat& operator=(SpMat&& a) noexcept { moves(std::move(a)); return *this; } | ||||
SpMat(const SpMat& a) = delete; //!< No copy ctor | |||||
SpMat& operator=(const SpMat& a) = delete; //!< No copy assignment | |||||
SpMat(const SpMat& a) = delete; //!< make sure there are no copies | |||||
SpMat& operator=(const SpMat& a) = delete; //!< make sure there are no copies | |||||
//! @} | //! @} | ||||
//! \name Data exposure | //! \name Data exposure | ||||
//! @{ | //! @{ | ||||
//! \return the dimension of the matrix | |||||
IndexType size() noexcept { return N; } | IndexType size() noexcept { return N; } | ||||
//! After construction size configuration tool | |||||
IndexType size(IndexType n) { | IndexType size(IndexType n) { | ||||
col_ptr.resize(n+1); | col_ptr.resize(n+1); | ||||
return N = n; | return N = n; | ||||
} | } | ||||
//! \return the NNZ of the matrix | |||||
IndexType capacity() noexcept { return NNZ; } | IndexType capacity() noexcept { return NNZ; } | ||||
//! After construction NNZ size configuration tool | |||||
IndexType capacity(IndexType nnz) { | IndexType capacity(IndexType nnz) { | ||||
values.reserve(nnz); | values.reserve(nnz); | ||||
rows.reserve(nnz); | rows.reserve(nnz); | ||||
return NNZ; | return NNZ; | ||||
} | } | ||||
// getters for row arrays of the struct (unused) | |||||
std::vector<DataType>& getValues() noexcept { return values; } | |||||
std::vector<IndexType>& getRows() noexcept { return rows; } | std::vector<IndexType>& getRows() noexcept { return rows; } | ||||
std::vector<IndexType>& getCols() noexcept { return col_ptr; } | std::vector<IndexType>& getCols() noexcept { return col_ptr; } | ||||
/*! | |||||
* Return a proxy SpMatVal object with read and write capabilities. | |||||
* @param i The row number | |||||
* @param j The column number | |||||
* @return tHE SpMatVal object | |||||
*/ | |||||
SpMatVal<DataType, IndexType> operator()(IndexType i, IndexType j) { | SpMatVal<DataType, IndexType> operator()(IndexType i, IndexType j) { | ||||
return SpMatVal<DataType, IndexType>(this, get(i, j), i, j); | return SpMatVal<DataType, IndexType>(this, get(i, j), i, j); | ||||
} | } | ||||
/*! | |||||
* A read item functionality using binary search to find the correct row | |||||
* | |||||
* @param i The row number | |||||
* @param j The column number | |||||
* @return The value of the item or DataType{} if is not present. | |||||
*/ | |||||
DataType get(IndexType i, IndexType j) { | DataType get(IndexType i, IndexType j) { | ||||
IndexType idx; | |||||
bool found; | |||||
std::tie(idx, found) =find_idx(rows, col_ptr[j], col_ptr[j+1], i); | |||||
return (found) ? values[idx] : 0; | |||||
IndexType end, idx =find_idx(rows, col_ptr[j], end=col_ptr[j+1], i); | |||||
return (idx != end) ? values[idx] : 0; | |||||
} | } | ||||
/*! | |||||
* A write item functionality. | |||||
* | |||||
* First we search if the matrix has already a value in (i, j) position. | |||||
* If so we just change it to a new value. If not we add the item on the matrix. | |||||
* | |||||
* @note | |||||
* We don't increase the NNZ value of the struct. We expect the user has already | |||||
* change the NNZ value to the right one using @see capacity() function. | |||||
* | |||||
* @param i The row number | |||||
* @param j The column number | |||||
* @return The new value of the item . | |||||
*/ | |||||
DataType set(DataType v, IndexType i, IndexType j) { | DataType set(DataType v, IndexType i, IndexType j) { | ||||
IndexType idx; bool found; | IndexType idx; bool found; | ||||
std::tie(idx, found) = find_idx(rows, col_ptr[j], col_ptr[j+1], i); | |||||
std::tie(idx, found) = find2_idx(rows, col_ptr[j], col_ptr[j+1], i); | |||||
if (found) | if (found) | ||||
return values[idx] = v; // we don't change NNZ even if we write "0" | return values[idx] = v; // we don't change NNZ even if we write "0" | ||||
else { | else { | ||||
@@ -264,34 +318,47 @@ struct SpMat { | |||||
} | } | ||||
} | } | ||||
/*! | |||||
* Get a view of a CSC column | |||||
* @param j The column to get | |||||
* @return The SpMatCol object @see SpMatCol | |||||
*/ | |||||
SpMatCol<DataType, IndexType> getCol(IndexType j) { | SpMatCol<DataType, IndexType> getCol(IndexType j) { | ||||
return SpMatCol<DataType, IndexType>(this, col_ptr[j], col_ptr[j+1]); | return SpMatCol<DataType, IndexType>(this, col_ptr[j], col_ptr[j+1]); | ||||
} | } | ||||
/*! | |||||
* Get a view of a CSC row | |||||
* | |||||
* In case of a SYMMETRIC matrix we can return a column instead. | |||||
* | |||||
* @param j The row to get | |||||
* @return The SpMatCol object @see SpMatCol | |||||
*/ | |||||
template<MatrixType AT= Type> | template<MatrixType AT= Type> | ||||
std::enable_if_t<AT==MatrixType::SYMMETRIC, SpMatCol<DataType, IndexType>> | std::enable_if_t<AT==MatrixType::SYMMETRIC, SpMatCol<DataType, IndexType>> | ||||
getRow(IndexType i) { | getRow(IndexType i) { | ||||
return getCol(i); | return getCol(i); | ||||
} | } | ||||
/*! | |||||
* Get a view of a CSC row | |||||
* | |||||
* @param j The row to get | |||||
* @return The SpMatRow object @see SpMatRow | |||||
*/ | |||||
template<MatrixType AT= Type> | template<MatrixType AT= Type> | ||||
std::enable_if_t<AT==MatrixType::FULL, SpMatCol<DataType, IndexType>> | std::enable_if_t<AT==MatrixType::FULL, SpMatCol<DataType, IndexType>> | ||||
getRow(IndexType i) { | getRow(IndexType i) { | ||||
return SpMatRow<DataType, IndexType>(this, i); | return SpMatRow<DataType, IndexType>(this, i); | ||||
} | } | ||||
// iterator support | |||||
// values only iterator support | |||||
DataType* begin() noexcept { return values.begin(); } | DataType* begin() noexcept { return values.begin(); } | ||||
DataType* end() noexcept { return values.end(); } | DataType* end() noexcept { return values.end(); } | ||||
//! @} | //! @} | ||||
/*! | |||||
* \name Safe iteration API | |||||
* | |||||
* This api automates the iteration over the array based on | |||||
* MatrixType | |||||
*/ | |||||
//! @{ | |||||
//! A small iteration helper | |||||
template<typename F, typename... Args> | template<typename F, typename... Args> | ||||
void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) { | void for_each_in (IndexType begin, IndexType end, F&& lambda, Args&&... args) { | ||||
for (IndexType it=begin ; it<end ; ++it) { | for (IndexType it=begin ; it<end ; ++it) { | ||||
@@ -299,15 +366,45 @@ struct SpMat { | |||||
} | } | ||||
} | } | ||||
//! @} | |||||
// operations | |||||
// friend operations for printing | |||||
template<typename D, typename I, MatrixType T> friend void print(SpMat<D, I, T>& mat); | template<typename D, typename I, MatrixType T> friend void print(SpMat<D, I, T>& mat); | ||||
template<typename D, typename I, MatrixType T> friend void print_dense(SpMat<D, I, T>& mat); | template<typename D, typename I, MatrixType T> friend void print_dense(SpMat<D, I, T>& mat); | ||||
private: | private: | ||||
// index-find helper | |||||
std::pair<IndexType, bool> find_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) { | |||||
/*! | |||||
* A small binary search implementation using index for begin-end instead of iterators. | |||||
* | |||||
* \param v Reference to vector to search | |||||
* \param begin The vector's index to begin | |||||
* \param end The vector's index to end | |||||
* \param match What to search | |||||
* @return The index of the item or end on failure. | |||||
*/ | |||||
IndexType find_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) { | |||||
IndexType b = begin, e = end-1; | |||||
while (true) { | |||||
IndexType m = (b+e)/2; | |||||
if (v[m] == match) return m; | |||||
else if (b >= e) return end; | |||||
else { | |||||
if (v[m] < match) b = m +1; | |||||
else e = m -1; | |||||
} | |||||
} | |||||
return end; | |||||
} | |||||
/*! | |||||
* find helper for set using index for begin-end instead of iterators. | |||||
* | |||||
* We search for the item or a place to add the item. | |||||
* So we return the index if we find it. Otherwise we return the place to add it. | |||||
* | |||||
* \param v Reference to vector to search | |||||
* \param begin The vector's index to begin | |||||
* \param end The vector's index to end | |||||
* \param match What to search | |||||
*/ | |||||
std::pair<IndexType, bool> find2_idx(const std::vector<IndexType>& v, IndexType begin, IndexType end, IndexType match) { | |||||
for ( ; begin < end ; ++begin) { | for ( ; begin < end ; ++begin) { | ||||
if (match == v[begin]) return std::make_pair(begin, true); | if (match == v[begin]) return std::make_pair(begin, true); | ||||
else if (match < v[begin]) return std::make_pair(begin, false); | else if (match < v[begin]) return std::make_pair(begin, false); | ||||
@@ -324,38 +421,58 @@ private: | |||||
} | } | ||||
//! \name Data | //! \name Data | ||||
//! @{ | //! @{ | ||||
std::vector<DataType> values {}; | |||||
std::vector<IndexType> rows{}; | |||||
std::vector<IndexType> col_ptr{1,0}; | |||||
IndexType N{0}; | |||||
IndexType NNZ{0}; | |||||
std::vector<DataType> values {}; //!< vector to store the values of the matrix | |||||
std::vector<IndexType> rows{}; //!< vector to store the row information | |||||
std::vector<IndexType> col_ptr{1,0}; //!< vector to stor the column pointers | |||||
IndexType N{0}; //!< The dimension of the matrix (square) | |||||
IndexType NNZ{0}; //!< The NNZ (capacity of the matrix) | |||||
//! @} | //! @} | ||||
}; | }; | ||||
/*! | |||||
* A view/iterator hybrid object for SpMat columns. | |||||
* | |||||
* This object provides access to a column of a SpMat. The public functionalities | |||||
* allow data access using indexes instead of iterators. We prefer indexes over iterators | |||||
* because we can apply the same index to different inner vector of SpMat without conversion. | |||||
* | |||||
* @tparam DataType | |||||
* @tparam IndexType | |||||
*/ | |||||
template<typename DataType, typename IndexType> | template<typename DataType, typename IndexType> | ||||
struct SpMatCol { | struct SpMatCol { | ||||
using owner_t = SpMat<DataType, IndexType>; | using owner_t = SpMat<DataType, IndexType>; | ||||
/*! | |||||
* ctor using column pointers for begin-end. own is pointer to SpMat. | |||||
*/ | |||||
SpMatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept : | SpMatCol(owner_t* own, const IndexType begin, const IndexType end) noexcept : | ||||
owner_(own), index_(begin), begin_(begin), end_(end) { | owner_(own), index_(begin), begin_(begin), end_(end) { | ||||
vindex_ = vIndexCalc(index_); | vindex_ = vIndexCalc(index_); | ||||
} | } | ||||
SpMatCol() = default; | SpMatCol() = default; | ||||
SpMatCol(const SpMatCol&) = delete; | |||||
SpMatCol& operator=(const SpMatCol&)= delete; | |||||
SpMatCol(const SpMatCol&) = delete; //!< make sure there are no copies | |||||
SpMatCol& operator=(const SpMatCol&)= delete; //!< make sure there are no copies | |||||
SpMatCol(SpMatCol&&) = default; | SpMatCol(SpMatCol&&) = default; | ||||
SpMatCol& operator=(SpMatCol&&) = default; | SpMatCol& operator=(SpMatCol&&) = default; | ||||
//! a simple dereference operator, like an iterator | |||||
DataType operator* () { | DataType operator* () { | ||||
return get(); | return get(); | ||||
} | } | ||||
//! Increment operator acts on index(), like an iterator | |||||
SpMatCol& operator++ () { advance(); return *this; } | SpMatCol& operator++ () { advance(); return *this; } | ||||
SpMatCol& operator++ (int) { SpMatCol& p = *this; advance(); return p; } | SpMatCol& operator++ (int) { SpMatCol& p = *this; advance(); return p; } | ||||
//! () operator acts as member access (like a view) | |||||
DataType operator()(IndexType x) { | DataType operator()(IndexType x) { | ||||
return (x == index())? get() : DataType{}; | return (x == index())? get() : DataType{}; | ||||
} | } | ||||
//! = operator acts as member assignment (like a view) | |||||
DataType operator= (DataType v) { return owner_->values[index_] = v; } | DataType operator= (DataType v) { return owner_->values[index_] = v; } | ||||
// iterator like handlers | |||||
// these return a virtual index value based on the items position on the full matrix | |||||
// but the move of the index is just a ++ away. | |||||
IndexType index() noexcept { return vindex_; } | IndexType index() noexcept { return vindex_; } | ||||
const IndexType index() const noexcept { return vindex_; } | const IndexType index() const noexcept { return vindex_; } | ||||
IndexType begin() noexcept { return vIndexCalc(begin_); } | IndexType begin() noexcept { return vIndexCalc(begin_); } | ||||
@@ -363,6 +480,13 @@ struct SpMatCol { | |||||
IndexType end() noexcept { return owner_->N; } | IndexType end() noexcept { return owner_->N; } | ||||
const IndexType end() const noexcept { return owner_->N; } | const IndexType end() const noexcept { return owner_->N; } | ||||
/*! | |||||
* Multiplication operator | |||||
* @tparam C Universal reference for the type right half site column | |||||
* | |||||
* @param c The right hand site matrix | |||||
* @return The value of the inner product of two vectors | |||||
*/ | |||||
template <typename C> | template <typename C> | ||||
DataType operator* (C&& c) { | DataType operator* (C&& c) { | ||||
static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), ""); | static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), ""); | ||||
@@ -380,25 +504,42 @@ struct SpMatCol { | |||||
} | } | ||||
private: | private: | ||||
//! small tool to increase the index pointers to SpMat matrix | |||||
void advance() noexcept { | void advance() noexcept { | ||||
++index_; | ++index_; | ||||
vindex_ = vIndexCalc(index_); | vindex_ = vIndexCalc(index_); | ||||
} | } | ||||
//! tool to translate between col_ptr indexes and SpMat "virtual" full matrix indexes | |||||
IndexType vIndexCalc(IndexType idx) { | IndexType vIndexCalc(IndexType idx) { | ||||
return (idx < end_) ? owner_->rows[idx] : end(); | return (idx < end_) ? owner_->rows[idx] : end(); | ||||
} | } | ||||
//! small get tool | |||||
DataType get() { return owner_->values[index_]; } | DataType get() { return owner_->values[index_]; } | ||||
owner_t* owner_{nullptr}; | |||||
IndexType vindex_ {IndexType{}}; | |||||
IndexType index_{IndexType{}}; | |||||
IndexType begin_{IndexType{}}; | |||||
IndexType end_{IndexType{}}; | |||||
owner_t* owner_ {nullptr}; //!< Pointer to owner SpMat. SpMatCol is just a view | |||||
IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix | |||||
IndexType index_ {IndexType{}}; //!< index to SpMat::rows | |||||
IndexType begin_ {IndexType{}}; //!< beginning index of the column in SpMat::rows | |||||
IndexType end_ {IndexType{}}; //!< ending index of the column in SpMat::rows | |||||
}; | }; | ||||
/*! | |||||
* A view/iterator hybrid object for SpMat rows. | |||||
* | |||||
* This object provides access to a column of a SpMat. The public functionalities | |||||
* allow data access using indexes instead of iterators. We prefer indexes over iterators | |||||
* because we can apply the same index to different inner vector of SpMat without conversion. | |||||
* | |||||
* @tparam DataType | |||||
* @tparam IndexType | |||||
*/ | |||||
template<typename DataType, typename IndexType> | template<typename DataType, typename IndexType> | ||||
struct SpMatRow { | struct SpMatRow { | ||||
using owner_t = SpMat<DataType, IndexType>; | using owner_t = SpMat<DataType, IndexType>; | ||||
/*! | |||||
* ctor using virtual full matrix row index. own is pointer to SpMat. | |||||
*/ | |||||
SpMatRow(owner_t* own, const IndexType row) noexcept : | SpMatRow(owner_t* own, const IndexType row) noexcept : | ||||
owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}), | owner_(own), vindex_(IndexType{}), row_(row), index_(IndexType{}), | ||||
begin_(IndexType{}), end_(owner_->NNZ) { | begin_(IndexType{}), end_(owner_->NNZ) { | ||||
@@ -410,20 +551,29 @@ struct SpMatRow { | |||||
advance(); | advance(); | ||||
} | } | ||||
SpMatRow() = default; | SpMatRow() = default; | ||||
SpMatRow(const SpMatRow&) = delete; | |||||
SpMatRow& operator=(const SpMatRow&)= delete; | |||||
SpMatRow(const SpMatRow&) = delete; //!< make sure there are no copies | |||||
SpMatRow& operator=(const SpMatRow&)= delete; //!< make sure there are no copies | |||||
SpMatRow(SpMatRow&&) = default; | SpMatRow(SpMatRow&&) = default; | ||||
SpMatRow& operator=(SpMatRow&&) = default; | SpMatRow& operator=(SpMatRow&&) = default; | ||||
//! a simple dereference operator, like an iterator | |||||
DataType operator* () { | DataType operator* () { | ||||
return get(); | return get(); | ||||
} | } | ||||
//! Increment operator acts on index(), like an iterator | |||||
//! here the increment is a O(N) process. | |||||
SpMatRow& operator++ () { advance(); return *this; } | SpMatRow& operator++ () { advance(); return *this; } | ||||
SpMatRow& operator++ (int) { SpMatRow& p = *this; advance(); return p; } | SpMatRow& operator++ (int) { SpMatRow& p = *this; advance(); return p; } | ||||
//! () operator acts as member access (like a view) | |||||
DataType operator()(IndexType x) { | DataType operator()(IndexType x) { | ||||
return (x == index())? get() : DataType{}; | return (x == index())? get() : DataType{}; | ||||
} | } | ||||
//! = operator acts as member assignment (like a view) | |||||
DataType operator= (DataType v) { return owner_->values[index_] = v; } | DataType operator= (DataType v) { return owner_->values[index_] = v; } | ||||
// iterator like handlers | |||||
// these return a virtual index value based on the items position on the full matrix | |||||
// but the move of the index is just a ++ away. | |||||
IndexType index() noexcept { return vindex_; } | IndexType index() noexcept { return vindex_; } | ||||
const IndexType index() const noexcept { return vindex_; } | const IndexType index() const noexcept { return vindex_; } | ||||
IndexType begin() noexcept { return vIndexCalc(begin_); } | IndexType begin() noexcept { return vIndexCalc(begin_); } | ||||
@@ -431,6 +581,13 @@ struct SpMatRow { | |||||
IndexType end() noexcept { return owner_->N; } | IndexType end() noexcept { return owner_->N; } | ||||
const IndexType end() const noexcept { return owner_->N; } | const IndexType end() const noexcept { return owner_->N; } | ||||
/*! | |||||
* Multiplication operator | |||||
* @tparam C Universal reference for the type right half site column | |||||
* | |||||
* @param c The right hand site matrix | |||||
* @return The value of the inner product of two vectors | |||||
*/ | |||||
template <typename C> | template <typename C> | ||||
DataType operator* (C&& c) { | DataType operator* (C&& c) { | ||||
static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), ""); | static_assert(std::is_same<remove_cvref_t<C>, SpMatCol<DataType, IndexType>>(), ""); | ||||
@@ -447,70 +604,96 @@ struct SpMatRow { | |||||
return v; | return v; | ||||
} | } | ||||
private: | private: | ||||
//! small tool to increase the index pointers to SpMat matrix | |||||
//! We have to search the entire rows vector in SpMat to find the next | |||||
//! virtual row position. | |||||
//! time complexity O(N) | |||||
void advance() noexcept { | void advance() noexcept { | ||||
do | do | ||||
++index_; | ++index_; | ||||
while(index_ != end_ && owner_->rows[index_] != row_); | while(index_ != end_ && owner_->rows[index_] != row_); | ||||
vindex_ = vIndexCalc(index_); | vindex_ = vIndexCalc(index_); | ||||
} | } | ||||
//! tool to translate between col_ptr indexes and SpMat "virtual" full matrix indexes | |||||
IndexType vIndexCalc(IndexType idx) { | IndexType vIndexCalc(IndexType idx) { | ||||
for(IndexType i =0 ; i<(owner_->N+1) ; ++i) | for(IndexType i =0 ; i<(owner_->N+1) ; ++i) | ||||
if (idx < owner_->col_ptr[i]) | if (idx < owner_->col_ptr[i]) | ||||
return i-1; | return i-1; | ||||
return end(); | return end(); | ||||
} | } | ||||
//! small get tool | |||||
DataType get() { return owner_->values[index_]; } | DataType get() { return owner_->values[index_]; } | ||||
owner_t* owner_ {nullptr}; | |||||
IndexType vindex_ {IndexType{}}; | |||||
IndexType row_ {IndexType{}}; | |||||
IndexType index_ {IndexType{}}; | |||||
IndexType begin_ {IndexType{}}; | |||||
IndexType end_ {IndexType{}}; | |||||
owner_t* owner_ {nullptr}; //!< Pointer to owner SpMat. SpMatCol is just a view | |||||
IndexType vindex_ {IndexType{}}; //!< Virtual index of full matrix | |||||
IndexType row_ {IndexType{}}; //!< The virtual full matrix row of the object | |||||
IndexType index_ {IndexType{}}; //!< index to SpMat::rows | |||||
IndexType begin_ {IndexType{}}; //!< beginning index of the column in SpMat::rows | |||||
IndexType end_ {IndexType{}}; //!< ending index of the column in SpMat::rows | |||||
}; | }; | ||||
/*! | |||||
* A proxy SpMat value object/view. | |||||
* | |||||
* This object acts as proxy to provide read/write access to an SpMat item. | |||||
* | |||||
* @tparam DataType The type of the values of the SpMat matrix | |||||
* @tparam IndexType The type of the indexes of the SpMat matrix | |||||
*/ | |||||
template<typename DataType, typename IndexType> | template<typename DataType, typename IndexType> | ||||
struct SpMatVal { | struct SpMatVal { | ||||
using owner_t = SpMat<DataType, IndexType>; | using owner_t = SpMat<DataType, IndexType>; | ||||
//!< ctor using all value-row-column data, plus a pointer to owner SpMat object | |||||
SpMatVal(owner_t* own, DataType v, IndexType i, IndexType j) : | SpMatVal(owner_t* own, DataType v, IndexType i, IndexType j) : | ||||
owner_(own), v_(v), i_(i), j_(j) { } | owner_(own), v_(v), i_(i), j_(j) { } | ||||
SpMatVal() = default; | SpMatVal() = default; | ||||
SpMatVal(const SpMatVal&) = delete; | |||||
SpMatVal& operator=(const SpMatVal&) = delete; | |||||
SpMatVal(const SpMatVal&) = delete; //!< make sure there are no copies | |||||
SpMatVal& operator=(const SpMatVal&) = delete; //!< make sure there are no copies | |||||
SpMatVal(SpMatVal&&) = default; | SpMatVal(SpMatVal&&) = default; | ||||
SpMatVal& operator=(SpMatVal&&) = default; | SpMatVal& operator=(SpMatVal&&) = default; | ||||
//! Operator to return the DataType value implicitly | |||||
operator DataType() { return v_; } | operator DataType() { return v_; } | ||||
//! Operator to write back to owner the assigned value | |||||
//! for ex: A(2,3) = 5; | |||||
SpMatVal& operator=(DataType v) { | SpMatVal& operator=(DataType v) { | ||||
v_ = v; | v_ = v; | ||||
owner_->set(v_, i_, j_); | owner_->set(v_, i_, j_); | ||||
return *this; | return *this; | ||||
} | } | ||||
private: | private: | ||||
owner_t* owner_{nullptr};; | |||||
DataType v_{DataType{}}; | |||||
IndexType i_{IndexType{}}; | |||||
IndexType j_{IndexType{}}; | |||||
owner_t* owner_{nullptr}; //!< Pointer to owner SpMat. SpMatVal is just a view. | |||||
DataType v_{DataType{}}; //!< The value of the row-column pair (for speed) | |||||
IndexType i_{IndexType{}}; //!< The row | |||||
IndexType j_{IndexType{}}; //!< the column | |||||
}; | }; | ||||
enum class InputMatrix{ | |||||
GENERATE, | |||||
MTX | |||||
}; | |||||
//! enumerator for input matrix type. | |||||
enum class InputMatrix{ UNSPECIFIED, GENERATE, MTX }; | |||||
//! enumerator for output handling | |||||
enum class OutputMode{ STD, FILE }; | |||||
/*! | /*! | ||||
* Session option for each invocation of the executable | * Session option for each invocation of the executable | ||||
*/ | */ | ||||
struct session_t { | struct session_t { | ||||
std::size_t size {0}; | |||||
double probability {0}; | |||||
InputMatrix inputMatrix {InputMatrix::GENERATE}; | |||||
std::ifstream mtxFile {}; | |||||
std::size_t print_size {80}; | |||||
bool timing {false}; | |||||
bool print {false}; | |||||
bool makeSymmetric {false}; | |||||
InputMatrix inputMatrix {InputMatrix::UNSPECIFIED}; //!< Source of the matrix | |||||
std::ifstream mtxFile {}; //!< matrix file in MatrixMarket format | |||||
std::size_t gen_size {}; //!< size of the matrix if we select to generate a random one | |||||
double gen_prob {}; //!< probability of the binomial distribution for the matrix | |||||
//!< if we generate one | |||||
OutputMode outputMode {OutputMode::STD}; //!< Type of the output file | |||||
std::ofstream outFile {}; //!< File to use for output | |||||
std::size_t max_threads {}; //!< Maximum threads to use | |||||
bool timing {false}; //!< Enable timing prints of the program | |||||
bool verbose {false}; //!< Flag to enable verbose output to stdout | |||||
bool makeSymmetric {true}; //!< symmetric matrix creation flag (true by default) | |||||
bool validate_mtx {false}; //!< Flag to request mtx input data triangular validation. | |||||
bool print_count {false}; //!< Flag to request total count printing | |||||
bool mtx_print {false}; //!< matrix print flag | |||||
std::size_t mtx_print_size {}; //!< matrix print size | |||||
}; | }; | ||||
extern session_t session; | extern session_t session; | ||||
@@ -17,6 +17,10 @@ | |||||
#include <impl.hpp> | #include <impl.hpp> | ||||
#include <config.h> | #include <config.h> | ||||
/*! | |||||
* A small RAII utility to memory allocation arrays. | |||||
* @tparam T The type of pointer for the memory | |||||
*/ | |||||
template <typename T> | template <typename T> | ||||
struct buffer_t { | struct buffer_t { | ||||
buffer_t(size_t s) { p = new T[s]; } | buffer_t(size_t s) { p = new T[s]; } | ||||
@@ -34,8 +38,14 @@ private: | |||||
T* p{nullptr}; | T* p{nullptr}; | ||||
}; | }; | ||||
/*! | |||||
* A toolbox for MatrixMarket format handling | |||||
*/ | |||||
struct Mtx { | struct Mtx { | ||||
/*! | |||||
* A template version of the coo2csc function provided by PDS lab stuff. | |||||
*/ | |||||
template<typename I> | template<typename I> | ||||
static void coo2csc(I *row, I *col, I const* row_coo, I const* col_coo, I nnz, I n, I isOneBased) { | static void coo2csc(I *row, I *col, I const* row_coo, I const* col_coo, I nnz, I n, I isOneBased) { | ||||
// ----- cannot assume that input is already 0! | // ----- cannot assume that input is already 0! | ||||
@@ -70,6 +80,12 @@ struct Mtx { | |||||
} | } | ||||
} | } | ||||
/*! | |||||
* Utility to check if a matrix input is strictly triangular or not. | |||||
* @tparam I Index type | |||||
* @param file Reference to input file stream | |||||
* @return The status of the operation | |||||
*/ | |||||
template<typename I> | template<typename I> | ||||
static bool is_triangular (std::ifstream& file) { | static bool is_triangular (std::ifstream& file) { | ||||
std::string line, token; | std::string line, token; | ||||
@@ -105,6 +121,14 @@ struct Mtx { | |||||
return true; | return true; | ||||
} | } | ||||
/*! | |||||
* A utility to load an MatrixMarket file to memory | |||||
* @tparam DataT The data type | |||||
* @tparam IndexT The indexes type | |||||
* @param M Reference to matrix for output | |||||
* @param file Reference to input file stream to read from | |||||
* @return The status of the operation | |||||
*/ | |||||
template<typename DataT, typename IndexT, MatrixType MatrixT> | template<typename DataT, typename IndexT, MatrixType MatrixT> | ||||
static bool load (SpMat<DataT, IndexT, MatrixT>& M, std::ifstream& file) { | static bool load (SpMat<DataT, IndexT, MatrixT>& M, std::ifstream& file) { | ||||
std::string line, token; | std::string line, token; | ||||
@@ -139,60 +163,119 @@ struct Mtx { | |||||
if (line[0] == '%') continue; | if (line[0] == '%') continue; | ||||
IndexT i, j; | IndexT i, j; | ||||
ss >> i >> j; | ss >> i >> j; | ||||
if (session.makeSymmetric) { | |||||
if (LU == Z) { | |||||
LU = (i<j) ? UPPER: LOWER; | |||||
} | |||||
if ((LU==LOWER && j<i) || (LU==UPPER && i<j)) { | |||||
coo_row[cnt] = i; | |||||
coo_col[cnt++] = j; | |||||
coo_row[cnt] = j; | |||||
coo_col[cnt++] = i; | |||||
} | |||||
if (LU == Z) { | |||||
LU = (i<j) ? UPPER: LOWER; | |||||
} | } | ||||
else { | |||||
// ignore all values outside the triangle area | |||||
if ((LU==LOWER && j<i) || (LU==UPPER && i<j)) { | |||||
coo_row[cnt] = i; | coo_row[cnt] = i; | ||||
coo_col[cnt++] = j; | coo_col[cnt++] = j; | ||||
if (session.makeSymmetric) { | |||||
coo_row[cnt] = j; | |||||
coo_col[cnt++] = i; | |||||
} | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
coo2csc(&row[0], &col[0], &coo_row[0], &coo_col[0], cnt, n1, 1); | |||||
M = SpMat<DataT, IndexT, MatrixT>(n1, cnt, &row[0], &col[0]); | |||||
return true; | |||||
if (cnt) { | |||||
// convert and construct | |||||
coo2csc(&row[0], &col[0], &coo_row[0], &coo_col[0], cnt, n1, 1); | |||||
M = SpMat<DataT, IndexT, MatrixT>(n1, cnt, &row[0], &col[0]); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | } | ||||
}; | }; | ||||
/*! | |||||
* A small timing utility based on chrono. | |||||
*/ | |||||
struct Timing{ | struct Timing{ | ||||
using Tpoint = std::chrono::steady_clock::time_point; | using Tpoint = std::chrono::steady_clock::time_point; | ||||
using microseconds = std::chrono::microseconds; | using microseconds = std::chrono::microseconds; | ||||
using milliseconds = std::chrono::milliseconds; | using milliseconds = std::chrono::milliseconds; | ||||
using seconds = std::chrono::seconds; | using seconds = std::chrono::seconds; | ||||
//! tool to mark the starting point | |||||
Tpoint start () noexcept { return start_ = std::chrono::steady_clock::now(); } | Tpoint start () noexcept { return start_ = std::chrono::steady_clock::now(); } | ||||
//! tool to mark the ending point | |||||
Tpoint stop () noexcept { return stop_ = std::chrono::steady_clock::now(); } | Tpoint stop () noexcept { return stop_ = std::chrono::steady_clock::now(); } | ||||
auto dt () noexcept { | auto dt () noexcept { | ||||
return std::chrono::duration_cast<std::chrono::microseconds>(stop_ - start_).count(); | return std::chrono::duration_cast<std::chrono::microseconds>(stop_ - start_).count(); | ||||
} | } | ||||
void print_dt () noexcept { | |||||
auto t = stop_ - start_; | |||||
if (std::chrono::duration_cast<microseconds>(t).count() < 10000) | |||||
std::cout << "time: " << std::to_string(std::chrono::duration_cast<microseconds>(t).count()) << " [usec]\n"; | |||||
else if (std::chrono::duration_cast<milliseconds>(t).count() < 10000) | |||||
std::cout << "time: " << std::to_string(std::chrono::duration_cast<milliseconds>(t).count()) << " [msec]\n"; | |||||
else | |||||
std::cout << "time: " << std::to_string(std::chrono::duration_cast<seconds>(t).count()) << " [sec]\n"; | |||||
//! tool to print the time interval | |||||
void print_dt (const char* what) noexcept { | |||||
if (session.timing) { | |||||
auto t = stop_ - start_; | |||||
if (std::chrono::duration_cast<microseconds>(t).count() < 10000) | |||||
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<microseconds>(t).count()) << " [usec]\n"; | |||||
else if (std::chrono::duration_cast<milliseconds>(t).count() < 10000) | |||||
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<milliseconds>(t).count()) << " [msec]\n"; | |||||
else | |||||
std::cout << "[Timing]: " << what << ": " << std::to_string(std::chrono::duration_cast<seconds>(t).count()) << " [sec]\n"; | |||||
} | |||||
} | } | ||||
private: | private: | ||||
Tpoint start_; | Tpoint start_; | ||||
Tpoint stop_; | Tpoint stop_; | ||||
}; | }; | ||||
/*! | |||||
* A Logger for entire programm. | |||||
*/ | |||||
struct Log { | |||||
struct Endl {} endl; //!< a tag objec to to use it as a new line request. | |||||
//! We provide logging via << operator | |||||
template<typename T> | |||||
Log& operator<< (T&& t) { | |||||
if (session.verbose) { | |||||
if (line_) { | |||||
std::cout << "[Log]: " << t; | |||||
line_ = false; | |||||
} | |||||
else | |||||
std::cout << t; | |||||
} | |||||
return *this; | |||||
} | |||||
// overload for special end line handling | |||||
Log& operator<< (Endl e) { (void)e; | |||||
if (session.verbose) { | |||||
std::cout << '\n'; | |||||
line_ = true; | |||||
} | |||||
return *this; | |||||
} | |||||
private: | |||||
bool line_ {true}; | |||||
}; | |||||
extern Log logger; | |||||
//! Total count result printing function | |||||
template<typename F> | |||||
void triangle_out (value_t s, F&& f) { | |||||
f << "Total triangles: " << s << '\n'; | |||||
} | |||||
//! vector out result printing function | |||||
template<typename F> | |||||
void vector_out (std::vector<value_t>& v, F&& f) { | |||||
size_t idx{}; | |||||
f << "id,c3\n"; | |||||
for (auto& it : v) f << idx++ <<',' << it << '\n'; | |||||
f << '\n'; | |||||
} | |||||
/* | |||||
* Public non-template api. | |||||
* We use matrix alias template. So it has to be defined somewhere | |||||
*/ | |||||
void init_ER_graph (matrix& A, double p); | void init_ER_graph (matrix& A, double p); | ||||
void print_ER_graph (matrix& A); | |||||
void print_graph (matrix& A); | |||||
void threads_info (); | |||||
#endif /* UTILS_H_ */ | #endif /* UTILS_H_ */ |
@@ -9,13 +9,39 @@ | |||||
#ifndef V3_H_ | #ifndef V3_H_ | ||||
#define V3_H_ | #define V3_H_ | ||||
#include <iostream> | |||||
#include <mutex> | |||||
#include <impl.hpp> | #include <impl.hpp> | ||||
#if defined CILK | |||||
#include <cilk/cilk.h> | |||||
#include <cilk/cilk_api.h> | |||||
#include <cilk/reducer_opadd.h> | |||||
#elif defined OMP | |||||
#include <omp.h> | |||||
#elif defined THREADS | |||||
#include <thread> | |||||
#else | |||||
#endif | |||||
namespace v3 { | namespace v3 { | ||||
//! Select a data representation suited for V3. | |||||
using matrix = SpMat<int, int>; | using matrix = SpMat<int, int>; | ||||
int triang_count (matrix& A); | |||||
using index_t = typename matrix::indexType; //!< syntactic sugar alias for index type | |||||
using value_t = typename matrix::dataType; //!< syntactic sugar alias for value type | |||||
/* | |||||
* Common api for all the versions | |||||
*/ | |||||
int nworkers(); | |||||
std::vector<value_t> triang_v(matrix& A); | |||||
value_t triang_count (std::vector<value_t>& c); | |||||
}; | }; | ||||
#endif /* V3_H_ */ | #endif /* V3_H_ */ |
@@ -9,13 +9,40 @@ | |||||
#ifndef V4_H_ | #ifndef V4_H_ | ||||
#define V4_H_ | #define V4_H_ | ||||
#include <iostream> | |||||
#include <string> | |||||
#include <mutex> | |||||
#include <impl.hpp> | #include <impl.hpp> | ||||
#if defined CILK | |||||
#include <cilk/cilk.h> | |||||
#include <cilk/cilk_api.h> | |||||
#include <cilk/reducer_opadd.h> | |||||
#elif defined OMP | |||||
#include <omp.h> | |||||
#elif defined THREADS | |||||
#include <thread> | |||||
#else | |||||
#endif | |||||
namespace v4 { | namespace v4 { | ||||
//! Select a data representation suited for V3. | |||||
using matrix = SpMat<int, int>; | using matrix = SpMat<int, int>; | ||||
int triang_count (matrix& A); | |||||
using index_t = typename matrix::indexType; //!< syntactic sugar alias for index type | |||||
using value_t = typename matrix::dataType; //!< syntactic sugar alias for value type | |||||
/* | |||||
* Common api for all the versions | |||||
*/ | |||||
int nworkers(); | |||||
std::vector<value_t> triang_v(matrix& A); | |||||
value_t triang_count (std::vector<value_t>& c); | |||||
}; | }; | ||||
#endif /* V4_H_ */ | #endif /* V4_H_ */ |
@@ -16,6 +16,7 @@ | |||||
// Global session data | // Global session data | ||||
session_t session; | session_t session; | ||||
Log logger; | |||||
/*! | /*! | ||||
* A small command line argument parser | * A small command line argument parser | ||||
@@ -35,74 +36,123 @@ bool get_options(int argc, char* argv[]){ | |||||
else | else | ||||
status = false; | status = false; | ||||
} | } | ||||
else if (arg == "-g" || arg == "--generate") | |||||
else if (arg == "-o" || arg == "--output") { | |||||
session.outputMode = OutputMode::FILE; | |||||
if (i+1 < argc) | |||||
session.outFile = std::ofstream(argv[++i]); | |||||
else | |||||
status = false; | |||||
} | |||||
else if (arg == "-g" || arg == "--generate") { | |||||
session.inputMatrix = InputMatrix::GENERATE; | session.inputMatrix = InputMatrix::GENERATE; | ||||
else if (arg == "-s" || arg == "--size") | |||||
session.size = (i+1 < argc) ? std::atoi(argv[++i]) : session.size; | |||||
else if (arg == "-p" || arg == "--probability") | |||||
session.probability = (i+1 < argc) ? std::atof(argv[++i]) : session.probability; | |||||
else if (arg == "--print") { | |||||
session.print = true; | |||||
session.print_size = (i+1 < argc) ? std::atoi(argv[++i]) : session.print_size; | |||||
if (i+2 < argc) { | |||||
session.gen_size = std::atoi(argv[++i]); | |||||
session.gen_prob = std::atof(argv[++i]); | |||||
} | |||||
else | |||||
status = false; | |||||
} | |||||
else if (arg == "-n" || arg == "--max_trheads") { | |||||
session.max_threads = (i+1 < argc) ? std::atoi(argv[++i]) : session.max_threads; | |||||
} | } | ||||
else if (arg == "--make_symmetric") | |||||
session.makeSymmetric = true; | |||||
else if (arg == "-t" || arg == "--timing") | else if (arg == "-t" || arg == "--timing") | ||||
session.timing = true; | session.timing = true; | ||||
else if (arg == "-v" || arg == "--verbose") | |||||
session.verbose = true; | |||||
else if (arg == "--triangular_only") | |||||
session.makeSymmetric = false; | |||||
else if (arg == "--validate_mtx") | |||||
session.validate_mtx = true; | |||||
else if (arg == "--print_count") | |||||
session.print_count = true; | |||||
else if (arg == "--print_graph") { | |||||
session.mtx_print = true; | |||||
session.mtx_print_size = (i+1 < argc) ? std::atoi(argv[++i]) : session.mtx_print_size; | |||||
} | |||||
else if (arg == "-h" || arg == "--help") { | else if (arg == "-h" || arg == "--help") { | ||||
std::cout << "Help message\n"; | std::cout << "Help message\n"; | ||||
exit(0); | exit(0); | ||||
} | } | ||||
else { | |||||
else { // parse error | |||||
std::cout << "Error message\n"; | std::cout << "Error message\n"; | ||||
status = false; | status = false; | ||||
} | } | ||||
} | } | ||||
// Input checkers | |||||
if (session.inputMatrix == InputMatrix::UNSPECIFIED) { | |||||
std::cout << "Error message\n"; | |||||
status = false; | |||||
} | |||||
return status; | return status; | ||||
} | } | ||||
int main(int argc, char* argv[]) try { | |||||
Timing timer; | |||||
matrix A; | |||||
// try to read command line | |||||
if (!get_options(argc, argv)) | |||||
exit(1); | |||||
// get or generate matrix | |||||
/*! | |||||
* get or generate matrix | |||||
* \param A Reference to matrix for output (move using RVO) | |||||
* \param timer Reference to timer utility to access time printing functionality | |||||
*/ | |||||
void prepare_matrix (matrix& A, Timing& timer) { | |||||
if (session.inputMatrix == InputMatrix::GENERATE) { | if (session.inputMatrix == InputMatrix::GENERATE) { | ||||
std::cout << "Initialize matrix with size: " << session.size << " and probability: " << session.probability << '\n'; | |||||
logger << "Initialize matrix with size: " << session.gen_size << " and probability: " << session.gen_prob << logger.endl; | |||||
timer.start(); | timer.start(); | ||||
A.size(session.size); | |||||
init_ER_graph(A, session.probability); | |||||
A.size(session.gen_size); | |||||
init_ER_graph(A, session.gen_prob); | |||||
timer.stop(); | timer.stop(); | ||||
if (session.timing) timer.print_dt(); | |||||
timer.print_dt("generate matrix"); | |||||
} | } | ||||
else { | else { | ||||
std::cout << "Read matrix from file\n"; | |||||
logger << "Read matrix from file" << logger.endl; | |||||
timer.start(); | timer.start(); | ||||
if (session.makeSymmetric && !Mtx::is_triangular<matrix::indexType> (session.mtxFile)) | |||||
if (session.validate_mtx && !Mtx::is_triangular<matrix::indexType> (session.mtxFile)) | |||||
throw std::runtime_error("Error: Matrix is not strictly upper or lower"); | throw std::runtime_error("Error: Matrix is not strictly upper or lower"); | ||||
if (!Mtx::load (A, session.mtxFile)) { | if (!Mtx::load (A, session.mtxFile)) { | ||||
throw std::runtime_error("Error: fail to load matrix"); | throw std::runtime_error("Error: fail to load matrix"); | ||||
} | } | ||||
timer.stop(); | timer.stop(); | ||||
std::cout << "Matrix size: " << A.size() << " and capacity: " << A.capacity() <<'\n'; | |||||
if (session.timing) timer.print_dt(); | |||||
logger << "Matrix size: " << A.size() << " and capacity: " << A.capacity() << logger.endl; | |||||
timer.print_dt("load matrix"); | |||||
} | } | ||||
if (session.print) { | |||||
std::cout << "Array A:\n"; | |||||
print_ER_graph (A); | |||||
if (session.verbose && session.mtx_print) { | |||||
logger << "\nMatrix:" << logger.endl; | |||||
print_graph (A); | |||||
} | } | ||||
} | |||||
/* | |||||
* main program | |||||
*/ | |||||
int main(int argc, char* argv[]) try { | |||||
Timing timer; | |||||
matrix A; | |||||
std::vector<value_t> c; | |||||
index_t s; | |||||
std::cout << "count triangles\n"; | |||||
// try to read command line | |||||
if (!get_options(argc, argv)) | |||||
exit(1); | |||||
prepare_matrix(A, timer); | |||||
threads_info(); | |||||
logger << "Create count vector" << logger.endl; | |||||
timer.start(); | timer.start(); | ||||
std::cout << "There are " << triang_count(A) << " triangles\n"; | |||||
c = triang_v (A); | |||||
timer.stop(); | timer.stop(); | ||||
if (session.timing) timer.print_dt(); | |||||
timer.print_dt("create count vector"); | |||||
if (session.print_count) { | |||||
logger << "Calculate total triangles" << logger.endl; | |||||
timer.start(); | |||||
s = triang_count(c); | |||||
timer.stop(); | |||||
logger << "There are " << s << " triangles" << logger.endl; | |||||
timer.print_dt("calculate sum"); | |||||
} | |||||
// output results | |||||
if (session.print_count) | |||||
triangle_out (s, (session.outputMode == OutputMode::FILE) ? session.outFile : std::cout); | |||||
else | |||||
vector_out (c, (session.outputMode == OutputMode::FILE) ? session.outFile : std::cout); | |||||
return 0; | return 0; | ||||
} | } | ||||
catch (std::exception& e) { | catch (std::exception& e) { | ||||
@@ -40,8 +40,8 @@ void init_ER_graph (matrix& A, double p) { | |||||
/*! | /*! | ||||
* Utility to print the graph to sdtout | * Utility to print the graph to sdtout | ||||
*/ | */ | ||||
void print_ER_graph (matrix& A) { | |||||
matrix::indexType N = (A.size() < (matrix::indexType)session.print_size) ? A.size() : session.print_size; | |||||
void print_graph (matrix& A) { | |||||
matrix::indexType N = (A.size() < (matrix::indexType)session.mtx_print_size) ? A.size() : session.mtx_print_size; | |||||
A.for_each_in(0, N, [&](auto i){ | A.for_each_in(0, N, [&](auto i){ | ||||
A.for_each_in(0, N, [&](auto j) { | A.for_each_in(0, N, [&](auto j) { | ||||
@@ -51,3 +51,20 @@ void print_ER_graph (matrix& A) { | |||||
}); | }); | ||||
} | } | ||||
/*! | |||||
* Utility to print to logger thread information | |||||
*/ | |||||
void threads_info () { | |||||
#if defined CILK | |||||
logger << "Running with max threads: " << __cilkrts_get_nworkers() << logger.endl; | |||||
logger << "Utilizing " << __cilkrts_get_nworkers() << " threads for calculating vector." << logger.endl; | |||||
logger << "Utilizing " << nworkers() << " threads for calculating sum." << logger.endl; | |||||
#elif defined OMP | |||||
logger << "Running with max threads: " << nworkers() << logger.endl; | |||||
#elif defined THREADS | |||||
logger << "Running with max threads: " << nworkers() << logger.endl; | |||||
#else | |||||
logger << "Running the serial version of the algorithm." << logger.endl; | |||||
#endif | |||||
} | |||||
@@ -6,30 +6,128 @@ | |||||
* Christos Choutouridis AEM:8997 | * Christos Choutouridis AEM:8997 | ||||
* <cchoutou@ece.auth.gr> | * <cchoutou@ece.auth.gr> | ||||
*/ | */ | ||||
#include <iostream> | |||||
#include <random> | |||||
#include <v3.h> | #include <v3.h> | ||||
// for (int i=0 ; i<A.size() ; ++i) { | |||||
// for (int j = A.col_ptr[i]; j<A.col_ptr[i+1] ; ++j) { | |||||
// int j_idx = A.rows[j]; | |||||
// for (int k = A.col_ptr[j_idx] ; k<A.col_ptr[j_idx+1] ; ++k) { | |||||
// int k_idx = A.rows[k]; | |||||
// if (A.get(k_idx, i)) { | |||||
// ++c[i]; | |||||
// } | |||||
// } | |||||
// } | |||||
// } | |||||
namespace v3 { | namespace v3 { | ||||
using index_t = typename matrix::indexType; | |||||
using value_t = typename matrix::dataType; | |||||
#if defined CILK | |||||
/*! | |||||
* A naive triangle counting algorithm | |||||
* \param A The adjacency matrix | |||||
* \return The number of triangles | |||||
// export CILK_NWORKERS=<num> | |||||
int nworkers() { | |||||
if (session.max_threads) | |||||
return (session.max_threads < __cilkrts_get_nworkers()) ? | |||||
session.max_threads : __cilkrts_get_nworkers(); | |||||
else | |||||
return __cilkrts_get_nworkers(); | |||||
} | |||||
std::vector<value_t> triang_v(matrix& A) { | |||||
std::vector<value_t> c(A.size()); | |||||
cilk_for (int i=0 ; i<A.size() ; ++i) { | |||||
for (auto j = A.getCol(i); j.index() != j.end() ; ++j) // j list all the edges with i | |||||
for (auto k = A.getCol(j.index()); k.index() != k.end() ; ++k) // k list all the edges with j | |||||
if (A.get(k.index(), i)) // search for i-k edge | |||||
++c[i]; | |||||
} | |||||
if (session.makeSymmetric) | |||||
std::transform (c.begin(), c.end(), c.begin(), [] (value_t& x) { | |||||
return x/2; | |||||
}); | |||||
return c; | |||||
} | |||||
void do_sum (value_t& out_sum, std::vector<value_t>& v, index_t begin, index_t end) { | |||||
for (auto i =begin ; i != end ; ++i) | |||||
out_sum += v[i]; | |||||
} | |||||
value_t sum (std::vector<value_t>& v) { | |||||
int n = nworkers(); | |||||
std::vector<value_t> sum_v(n, 0); | |||||
for (index_t i =0 ; i < n ; ++i) { | |||||
cilk_spawn do_sum(sum_v[i], v, i*v.size()/n, (i+1)*v.size()/n); | |||||
} | |||||
cilk_sync; | |||||
value_t s =0; | |||||
for (auto& it : sum_v) s += it; | |||||
return s; | |||||
} | |||||
#elif defined OMP | |||||
/* | |||||
// export OMP_NUM_THREADS=<num> | |||||
*/ | */ | ||||
int nworkers() { | |||||
if (session.max_threads && session.max_threads < (size_t)omp_get_max_threads()) { | |||||
omp_set_dynamic(0); | |||||
omp_set_num_threads(session.max_threads); | |||||
return session.max_threads; | |||||
} | |||||
else { | |||||
omp_set_dynamic(1); | |||||
return omp_get_max_threads(); | |||||
} | |||||
} | |||||
std::vector<value_t> triang_v(matrix& A) { | |||||
std::vector<value_t> c(A.size()); | |||||
#pragma omp parallel for shared(c) | |||||
for (int i=0 ; i<A.size() ; ++i) { | |||||
for (auto j = A.getCol(i); j.index() != j.end() ; ++j) // j list all the edges with i | |||||
for (auto k = A.getCol(j.index()); k.index() != k.end() ; ++k) // k list all the edges with j | |||||
if (A.get(k.index(), i)) // search for i-k edge | |||||
++c[i]; | |||||
} | |||||
if (session.makeSymmetric) | |||||
std::transform (c.begin(), c.end(), c.begin(), [] (value_t& x) { | |||||
return x/2; | |||||
}); | |||||
return c; | |||||
} | |||||
value_t sum (std::vector<value_t>& v) { | |||||
value_t s =0; | |||||
#pragma omp parallel for reduction(+:s) | |||||
for (auto i =0u ; i<v.size() ; ++i) | |||||
s += v[i]; | |||||
return s; | |||||
} | |||||
#else | |||||
int nworkers() { return 1; } | |||||
std::vector<value_t> triang_v(matrix& A) { | std::vector<value_t> triang_v(matrix& A) { | ||||
std::vector<value_t> c(A.size()); | std::vector<value_t> c(A.size()); | ||||
for (int i=0 ; i<A.size() ; ++i) { | for (int i=0 ; i<A.size() ; ++i) { | ||||
for (auto j = A.getCol(i); j.index() != j.end() ; ++j) // j list all the edges with i | for (auto j = A.getCol(i); j.index() != j.end() ; ++j) // j list all the edges with i | ||||
for (auto k = A.getCol(j.index()); k.index() != k.end() ; ++k) // k list all the edges with j | for (auto k = A.getCol(j.index()); k.index() != k.end() ; ++k) // k list all the edges with j | ||||
for (auto ii = A.getCol(i) ; ii.index() <= k.index() ; ++ii) // search for i-k edge (this could be binary search) | |||||
if (ii.index() == k.index()) ++c[i]; | |||||
if (A.get(k.index(), i)) // search for i-k edge | |||||
++c[i]; | |||||
} | } | ||||
if (session.makeSymmetric) | |||||
std::transform (c.begin(), c.end(), c.begin(), [] (value_t& x) { | |||||
return x/2; | |||||
}); | |||||
return c; | return c; | ||||
} | } | ||||
@@ -40,9 +138,11 @@ value_t sum (std::vector<value_t>& v) { | |||||
return s; | return s; | ||||
} | } | ||||
value_t triang_count (matrix& A) { | |||||
auto v = triang_v(A); | |||||
return sum(v); | |||||
#endif | |||||
value_t triang_count (std::vector<value_t>& c) { | |||||
return (session.makeSymmetric) ? sum(c)/3 : sum(c); | |||||
} | } | ||||
} | } |
@@ -6,27 +6,176 @@ | |||||
* Christos Choutouridis AEM:8997 | * Christos Choutouridis AEM:8997 | ||||
* <cchoutou@ece.auth.gr> | * <cchoutou@ece.auth.gr> | ||||
*/ | */ | ||||
#include <iostream> | |||||
#include <random> | |||||
#include <v4.h> | #include <v4.h> | ||||
namespace v4 { | namespace v4 { | ||||
using index_t = typename matrix::indexType; | |||||
using value_t = typename matrix::dataType; | |||||
#if defined CILK | |||||
// export CILK_NWORKERS=<num> | |||||
int nworkers() { | |||||
if (session.max_threads) | |||||
return (session.max_threads < __cilkrts_get_nworkers()) ? | |||||
session.max_threads : __cilkrts_get_nworkers(); | |||||
else | |||||
return __cilkrts_get_nworkers(); | |||||
} | |||||
std::vector<value_t> mmacc_v(matrix& A, matrix& B) { | std::vector<value_t> mmacc_v(matrix& A, matrix& B) { | ||||
std::vector<value_t> c(A.size()); | std::vector<value_t> c(A.size()); | ||||
for (int i=0 ; i<A.size() ; ++i) { | |||||
cilk_for (int i=0 ; i<A.size() ; ++i) { | |||||
for (auto j = A.getRow(i); j.index() != j.end() ; ++j){ | for (auto j = A.getRow(i); j.index() != j.end() ; ++j){ | ||||
c[i] += A.getRow(i)*B.getCol(j.index()); | c[i] += A.getRow(i)*B.getCol(j.index()); | ||||
} | } | ||||
} | } | ||||
if (session.makeSymmetric) | |||||
std::transform (c.begin(), c.end(), c.begin(), [] (value_t& x) { | |||||
return x/2; | |||||
}); | |||||
return c; | return c; | ||||
} | } | ||||
void do_sum (value_t& out_sum, std::vector<value_t>& v, index_t begin, index_t end) { | |||||
for (auto i =begin ; i != end ; ++i) | |||||
out_sum += v[i]; | |||||
} | |||||
value_t sum (std::vector<value_t>& v) { | |||||
int n = nworkers(); | |||||
std::vector<value_t> sum_v(n, 0); | |||||
for (index_t i =0 ; i < n ; ++i) { | |||||
cilk_spawn do_sum(sum_v[i], v, i*v.size()/n, (i+1)*v.size()/n); | |||||
} | |||||
cilk_sync; | |||||
value_t s =0; | |||||
for (auto& it : sum_v) s += it; | |||||
return s; | |||||
} | |||||
#elif defined OMP | |||||
/* | |||||
// export OMP_NUM_THREADS=<num> | |||||
*/ | |||||
int nworkers() { | |||||
if (session.max_threads && session.max_threads < (size_t)omp_get_max_threads()) { | |||||
omp_set_dynamic(0); | |||||
omp_set_num_threads(session.max_threads); | |||||
return session.max_threads; | |||||
} | |||||
else { | |||||
omp_set_dynamic(1); | |||||
return omp_get_max_threads(); | |||||
} | |||||
} | |||||
std::vector<value_t> mmacc_v(matrix& A, matrix& B) { | |||||
std::vector<value_t> c(A.size()); | |||||
#pragma omp parallel for shared(c) | |||||
for (int i=0 ; i<A.size() ; ++i) { | |||||
for (auto j = A.getRow(i); j.index() != j.end() ; ++j) { | |||||
c[i] += A.getRow(i)*B.getCol(j.index()); | |||||
} | |||||
} | |||||
if (session.makeSymmetric) | |||||
std::transform (c.begin(), c.end(), c.begin(), [] (value_t& x) { | |||||
return x/2; | |||||
}); | |||||
return c; | |||||
} | |||||
value_t sum (std::vector<value_t>& v) { | |||||
value_t s =0; | |||||
#pragma omp parallel for reduction(+:s) | |||||
for (auto i =0u ; i<v.size() ; ++i) | |||||
s += v[i]; | |||||
return s; | |||||
} | |||||
#elif defined THREADS | |||||
/* | |||||
* std::thread::hardware_concurrency() | |||||
*/ | |||||
int nworkers() { | |||||
if (session.max_threads) | |||||
return (session.max_threads < std::thread::hardware_concurrency()) ? | |||||
session.max_threads : std::thread::hardware_concurrency(); | |||||
else | |||||
return std::thread::hardware_concurrency(); | |||||
} | |||||
std::vector<value_t> mmacc_v_rng(std::vector<value_t>& out, matrix& A, matrix& B, index_t begin, index_t end) { | |||||
for (index_t i=begin ; i<end ; ++i) { | |||||
for (auto j = A.getRow(i); j.index() != j.end() ; ++j){ | |||||
out[i] += A.getRow(i)*B.getCol(j.index()); | |||||
} | |||||
} | |||||
return out; | |||||
} | |||||
std::vector<value_t> mmacc_v(matrix& A, matrix& B) { | |||||
std::vector<std::thread> workers; | |||||
std::vector<value_t> c(A.size()); | |||||
int n = nworkers(); | |||||
for (index_t i=0 ; i<n ; ++i) | |||||
workers.push_back (std::thread (mmacc_v_rng, std::ref(c), std::ref(A), std::ref(B), i*c.size()/n, (i+1)*c.size()/n)); | |||||
std::for_each(workers.begin(), workers.end(), [](std::thread& t){ | |||||
t.join(); | |||||
}); | |||||
if (session.makeSymmetric) | |||||
std::transform (c.begin(), c.end(), c.begin(), [] (value_t& x) { | |||||
return x/2; | |||||
}); | |||||
return c; | |||||
} | |||||
void do_sum (value_t& out_sum, std::vector<value_t>& v, index_t begin, index_t end) { | |||||
for (auto i =begin ; i != end ; ++i) | |||||
out_sum += v[i]; | |||||
} | |||||
value_t sum (std::vector<value_t>& v) { | |||||
int n = nworkers(); | |||||
std::vector<value_t> sum_v(n, 0); | |||||
std::vector<std::thread> workers; | |||||
for (index_t i =0 ; i < n ; ++i) | |||||
workers.push_back (std::thread (do_sum, std::ref(sum_v[i]), std::ref(v), i*v.size()/n, (i+1)*v.size()/n)); | |||||
std::for_each(workers.begin(), workers.end(), [](std::thread& t){ | |||||
t.join(); | |||||
}); | |||||
value_t s =0; | |||||
for (auto& it : sum_v) s += it; | |||||
return s; | |||||
} | |||||
#else | |||||
int nworkers() { return 1; } | |||||
std::vector<value_t> mmacc_v(matrix& A, matrix& B) { | |||||
std::vector<value_t> c(A.size()); | |||||
for (int i=0 ; i<A.size() ; ++i) { | |||||
for (auto j = A.getRow(i); j.index() != j.end() ; ++j){ | |||||
c[i] += A.getRow(i)*B.getCol(j.index()); | |||||
} | |||||
} | |||||
if (session.makeSymmetric) | |||||
std::transform (c.begin(), c.end(), c.begin(), [] (value_t& x) { | |||||
return x/2; | |||||
}); | |||||
return c; | |||||
} | |||||
value_t sum (std::vector<value_t>& v) { | value_t sum (std::vector<value_t>& v) { | ||||
value_t s =0; | value_t s =0; | ||||
@@ -35,9 +184,14 @@ value_t sum (std::vector<value_t>& v) { | |||||
return s; | return s; | ||||
} | } | ||||
value_t triang_count (matrix& A) { | |||||
auto v = mmacc_v(A, A); | |||||
return (session.makeSymmetric) ? sum(v)/6 : sum(v); | |||||
#endif | |||||
std::vector<value_t> triang_v(matrix& A) { | |||||
return mmacc_v(A, A); | |||||
} | } | ||||
value_t triang_count (std::vector<value_t>& c) { | |||||
return (session.makeSymmetric) ? sum(c)/3 : sum(c); | |||||
} | } | ||||
} // namespace v4 |