Does accessing the same array's different elements create a data race?
I have a "Matrix" wrapper class for an array with matrix interface, and i wrote a parallel multiplication by a scalar function for it.
I use CTPL library for thread pools.
I know that writing from a thread into an array cell passed by reference is not a data race (please correct me if i'm wrong) so i decided to pass a cell from the array to the function so i can write multiplication result into the cell itself, not by passing the reference to an array and the index, so i can avoid a data race.
I ran the function 10k times and the results did not differ even once, but a sanitizer i use ("-fsanitize=thread -fPIE -pie -g" in Cmake flags) still alerts me of a data race on the line where i create the thread pool.
Is the sanitizer mistaken or am i really experiencing a data race somewhere?
Here are the pieces of code, relevant to the prolem:
Wrapper:
class Matrix {
protected:
int width;
int height;
double* matrix;
public:
Matrix(int m, int n);
Matrix(int m, int n, const std::vector<double>& values);
int get_width() {
return width;
}
int get_height() {
return height;
}
double get_element(int row_num, int col_num);
void set_element(int row_num, int col_num, double el);
double* get_cell_ref(int row_num, int col_num);
};
Method implementations:
Matrix::Matrix(int m, int n) {
assert(m > 0 && n > 0);
matrix = new double[m * n]{0};
width = n;
height = m;
}
Matrix::Matrix(int m, int n, const std::vector<double>& values) {
assert(m > 0 && n > 0 && values.size() == m * n);
matrix = new double[m * n];
width = n;
height = m;
for (int i = 0; i < m * n; ++i) {
matrix[i] = values[i];
}
}
double Matrix::get_element(int row_num, int col_num) {
assert(check_valid(row_num, col_num, get_width(), get_height()));
return matrix[col_num + get_width() * row_num];
}
void Matrix::set_element(int row_num, int col_num, double el) {
assert(check_valid(row_num, col_num, get_width(), get_height()));
matrix[col_num + row_num * get_width()] = el;
}
double* Matrix::get_cell_ref(int row_num, int col_num) {
int idx = col_num + get_width() * row_num;
return &matrix[idx];
}
The function that supposedly has a data race:
Matrix* scalar_multiply_parallel(Matrix* a, double mul, int threadN) {
auto* b = new Matrix(a->get_height(), a->get_width());
ctpl::thread_pool thr_pool(threadN);
std::vector<std::future<void>> futures(a->get_height() * a->get_width());
for (int i =0; i < a->get_height(); i++) {
for (int j =0; j < a->get_width(); j++) {
int idx = j + a->get_width() * i;
auto util = [&a, &b, i, j, mul](int) {
//b->set_element(i, j, a->get_element(i, j) * mul);
double *cell;
cell = b->get_cell_ref(i, j);
*cell = a->get_element(i, j) * mul;
};
futures[idx] = thr_pool.push(util);
}
}
for (auto& f: futures) {
f.get();
}
return b;
}
Related
I have the following code where I am using both libraries Eigen and FFTW in C++. I am aware of the unsuported FFT that Eigen has, but I am using the FFTW library for additional features. So, I am trying to take the FFT with a C++ array and then map the output to a complex Eigen matrix (to perform some linear algebra operations on it). I have the code:
struct cplx_buffer
{
fftw_complex* a;
int rows;
int cols;
fftw_complex& operator()(int i, int j) const { return a[i * cols + j]; }
};
struct real_buffer
{
double* a;
int rows;
int cols;
double& operator()(int i, int j) const { return a[i * cols + j]; }
};
int main(){
static const int nx = 10;
static const int ny = 10;
static const int nyk = ny/2 + 1;
static const int mm = nx* 3/2;
cplx_buffer outW = my_fftw_allocate_cplx((ny+1), mm);
real_buffer inW = my_fftw_allocate_real((ny+1), mm);
//initialize the input
for (int i = 0; i < ny+1; i++){
for (int j = 0; j < mm; j++){
inW(i,j) = //expression
}
}
//Take FFT of rows
{ // Transform all the rows
fftw_execute(fftw_plan_many_dft_r2c(1, &nx, inW.rows, inW.a, &inW.cols, 1, inW.cols, outW.a, &inW.cols, 1, outW.cols, FFTW_ESTIMATE));
}
Eigen::Map<Eigen::MatrixXcd, Eigen::Unaligned> invnek(*reinterpret_cast<fftw_complex*>(&outW),(ny+1),mm); //ERROR
}
The error:
no instance of constructor "Eigen::Map<PlainObjectType, MapOptions, StrideType>::Map [with PlainObjectType=Eigen::MatrixXcd, MapOptions=0, StrideType=Eigen::Stride<0, 0>]" matches the argument list
How can I reinterpret_cast here correctly?
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 9 months ago.
Improve this question
This is the code for my matrix class
The goal here was to Complete the member functions 'void Matrix::add(const Matrix &), void Matrix::mul(double),
void Matrix::mul(const Matrix &), void Matrix::tr(void), and void Matrix::eye(int)'
of the Matrix class in the header file file matrix class
but as soon as I completed that my code started giving me errors and will not run. I am not sure what the problem is.
#ifndef MATRIX_H_
#define MATRIX_H_
#include <iostream>
#include <iomanip>
#include <sstream>
using namespace std;
#define ROW_MAX 10
#define COL_MAX 10
// In the following, the matrix object is referred to as A,
// upper case letters denote matrices,
// and lover case letters denote scalars.
class Matrix
{
public:
Matrix(int m_, int n_, double v_) : m(m_), n(n_) { fill(v_); }; // constructor for an m_ x n_ matrix A initialized to v_
Matrix(int m_, int n_) : Matrix(m_, n_, 0.0) {} // constructor for an m_ x n_ matrix A initialized to 0.0
Matrix(int m_) : Matrix(m_, m_) {} // constructor for an m_ x m_ matrix A initialized to 0.0
Matrix() : Matrix(0) {} // constructor for a 0 x 0 matrix A (empty matrix)
Matrix(const Matrix &A_) { set(A_); } // copy constructor
void from_str(const string &str_); // reads in m, n, and the matrix elements from the string str_ in the format of "m n A[0][0] A[0][1]...A[m-1][n-1]"
string to_str(void); // returns the string representation of A in the format of "m n A[0][0] A[0][1]...A[m-1][n-1]"
int getRows(void) const; // returns the number of rows
int getCols(void) const; // returns the number of columns
double get(int i_, int j_) const; // returns A[i_][j_]
void set(int i_, int j_, double v_); // sets A[i_][j_] to v_ (A[i_][j_] = v_)
void set(const Matrix &A_); // sets A to A_ (A = A_)
void add(const Matrix &A_); // adds A_ to A (A := A + A_)
void mul(double v_); // multiplies A by the scalar v_ (A := v_ A)
void mul(const Matrix &A_); // multiplies A by A_ (A := A A_)
void tr(void); // sets A to its transpose (A := A^T)
void eye(int m_); // sets A to the m_ x m_ identity matrix (A := I)
private:
int m; // the number of rows
int n; // the number of cols
void setRows(int m_); // sets the number of rows to m_
void setCols(int n_); // sets the number of columns to n_
double data[ROW_MAX][COL_MAX]; // holds the matrix data as 2D array
void fill(double v_); // fills the matrix with v_
};
void Matrix::fill(double v_)
{
for (int i = 0; i < getRows(); i++)
{
for (int j = 0; j < getCols(); j++)
{
set(i, j, v_);
}
}
}
void Matrix::from_str(const string &str_)
{
istringstream stream(str_);
int m_ = 0, n_ = 0;
stream >> m_;
stream >> n_;
setRows(m_);
setCols(n_);
int i = 0, j = 0;
double v_;
while (stream >> v_)
{
set(i, j, v_);
j += 1;
if (j == getCols())
{
i = i + 1;
j = 0;
}
if (i == getRows())
{
break;
}
}
}
string Matrix::to_str(void)
{
ostringstream _stream("");
_stream << getRows() << " " << getCols();
for (int i = 0; i < getRows(); i++)
{
for (int j = 0; j < getCols(); j++)
{
_stream << " " << fixed << defaultfloat << get(i, j);
}
}
return _stream.str();
}
int Matrix::getRows(void) const
{
return m;
}
int Matrix::getCols(void) const
{
return n;
}
void Matrix::setRows(int m_)
{
m = m_;
}
void Matrix::setCols(int n_)
{
n = n_;
}
double Matrix::get(int i_, int j_) const
{
return data[i_][j_];
}
void Matrix::set(int i_, int j_, double v_)
{
data[i_][j_] = v_;
}
void Matrix::set(const Matrix &A_)
{
setRows(A_.getRows());
setCols(A_.getCols());
for (int i = 0; i < getRows(); i++)
{
for (int j = 0; j < getCols(); j++)
{
set(i, j, A_.get(i, j));
}
}
}
void Matrix::add(const Matrix &A_)
{
int r = getRows();
int c = getCols();
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
A_[i][j] = A_[i][j] + A_[i][j];
}
}
}
void Matrix::mul(double v_)
{
int r = getRows();
int c = getCols();
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
A[i][j] = v_ * A[i][j];
}
}
}
void Matrix::mul(const Matrix &A_)
{
int r = getRows();
int c = getCols();
int result[r][c];
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
result[i][j] = 0;
for (int k = 0; k < r; k++)
{
result[i][j] += A[i][k] * A_[k][j];
}
}
}
}
void Matrix::tr(void)
{
int r = getRows();
int c = getCols();
int result[r][c];
for (int i = 0; i < N; i++)
{
for (intj = 0; j < N; j++)
{
result[i][j] = A[j][i];
}
}
}
void Matrix::eye(int m_)
{
int r = getRows();
int c = getCols();
for (int row = 0; row < m_; row++)
{
for (int col = 0; col < m_; col++)
{
if (row == col)
A[row][col] = 1;
else
A[row][col] = 0;
}
}
}
#endif
There's quite a few problems in your code. For example, in 'add', you have the following:
A_[i][j] = A_[i][j] + A_[i][j];
However, A_ is const, so you can't be modifying its data. Further, the Matrix class does not have an operator[], so you can't use A_[i] in the first place. You likely want the following:
data[i][j] = A_.data[i][j] + A_.data[i][j];
In your mul(double v_) function, you have the following line:
A[i][j] = v_ * A[i][j];
Again, there is no operator[] for your matrix, so it's not valid. Further, this function does not even have an A defined. You want to deal with the data directly.
data[i][j] = v_ * data[i][j];
In the mul(const Matrix &A_) you again have similar issues with using [], and again you have an A instead of A_. There's other issues in this function, such as not using the result after you've done the calculations.
In the tr function, you have intj instead of int j, and you're using a variable N which is not defined.
The function eye references the variable A which, again, never defined. You want data.
Using an IDE, or even an online compiler like godbolt, points out exactly where every one of these errors are. You can see here a fixed version of the code, though this doesn't fix any logic errors, just the syntax ones.
My2DClass::My2DClass(const int r, const int c, int mat[3][3]):m_r(r),m_c(c)
{
matrix = new int*[r];
for (int i = 0; i < r; i++)
matrix[i] = new int[c];
for (int i = 0; i < m_r; i++) {
for (int j = 0; j < m_c; j++) {
matrix[i][j] = mat[i][j];
}
}
//std::copy(&mat[0][0], &mat[0][0] + m_r * m_c, &matrix[0][0]);
}
How to use std::copy() for int**? The commented line throws an exception at runtime.
You are allocating an array of pointers to arrays, so a single call to std::copy() will not work. You would have to call std::copy() on every array individually. So really, only your inner-most loop can be replaced with std::copy(), eg:
My2DClass::My2DClass(const int r, const int c, int mat[3][3])
: m_r(r), m_c(c)
{
matrix = new int*[r];
for (int i = 0; i < r; i++)
matrix[i] = new int[c];
for (int i = 0; i < m_r; i++) {
std::copy(&mat[i][0], &mat[i][m_c], matrix[i]);
// or: std::copy_n(&mat[i][0], m_c, matrix[i]);
}
}
If you consolidate the 2 remaining loops into a single loop, eg:
My2DClass::My2DClass(const int r, const int c, int mat[3][3])
: m_r(r), m_c(c)
{
matrix = new int*[r];
for (int i = 0; i < r; ++i) {
matrix[i] = new int[c];
std::copy(&mat[i][0], &mat[i][c], matrix[i]);
// or: std::copy_n(&mat[i][0], c, matrix[i]);
}
}
Then you could replace that loop with std::for_each(), eg:
My2DClass::My2DClass(const int r, const int c, int mat[3][3])
: m_r(r), m_c(c)
{
matrix = new int*[r];
std::for_each(matrix, matrix + r,
[=](int* &arr){
arr = new int[c];
std::copy(&mat[i][0], &mat[i][c], arr);
// or: std::copy_n(&mat[i][0], c, arr);
}
);
}
Though, you really should avoid using new[] manually at all, consider using std::vector instead, which would greatly simplify management of your arrays, eg:
// std::vector<std::vector<int>> matrix;
My2DClass::My2DClass(const int r, const int c, int mat[3][3])
: m_r(r), m_c(c), matrix(r)
{
for(int i = 0; i < r; ++i) {
matrix[i].assign(&mat[i][0], &mat[i][c]);
}
}
Otherwise, consider flattening your matrix into a single 1-dimensional array instead. Especially since the input mat is a flat array in memory anyway. Then you can do a single std::copy() from one to the other, eg:
// int *matrix;
My2DClass::My2DClass(const int r, const int c, int mat[3][3])
: m_r(r), m_c(c), matrix(new int[r*c])
{
std::copy(&mat[0][0], &mat[r][c], matrix);
}
Or, using std::vector instead:
// std::vector<int> matrix;
My2DClass::My2DClass(const int r, const int c, int mat[3][3])
: m_r(r), m_c(c), matrix(&mat[0][0], &mat[r][c])
{
}
Either way, you can convert 2-dimensional indexes into 1-dimensional indexes using this formula:
(r * m_c) + c
For example:
int& My2DClass::operator()(int r, int c)
{
return matrix[(r * m_c) + c];
}
I created a matrix class by using a vector< vector >
structure. When created, the matrix is filled with zeroes (probably not the best way to do it, I'm planning to change it).
The header of the class goes something like this:
class Matrix{
public:
/*Basic constructor, accepts matrix dimensions
Matrix(int nr, int nc);
/*return element i,j*/
double elem(int i, int j);
/*operator () overloading - same thing as previous method*/
double operator()(int i, int j);
private:
vector<vector<double> > Matrix_;
int nr_, nc_;
};
while the implementation is:
//CONSTRUCTOR
Matrix::Matrix(int nrows, int ncols)
{
nc_ = ncols;
nr_ = nrows;
/*creates rows*/
for (int i = 0; i < nrows; i++)
{
vector<double> row;
Matrix_.push_back(row);
}
/*Fills matrix with zeroes*/
for (int i = 0; i < nr_; i++)
{
for (int j = 0; j < nc_; j++)
{
Matrix_[i].push_back(0);
}
}
}
/*method returning i,j element of the matrix (I overloaded () to do the same)*/
double Matrix::elem(int i, int j)
{
return Matrix_[i][j];
}
/*operator () overloading*/
double Matrix::operator()(int i, int j)
{
return Matrix_[i][j];
}
Finally, in the main program I have:
Matrix m1(rows, cols);
for (int i=0; i<rows; i++)
{
for (int j=0; j<cols; j++)
{
m1(i,j) = i*j;
/*OR, using the other method*/
m1.elem(i,j) = i*j;
}
}
and the problem is that I am always returned the error:
matrix.cpp:55:27: error: lvalue required as left operand of assignment
m1.elem(i,j) = i*j;
no matter if I am using the method .elem() or the operator ().
So, I guess the problem is that I am not accessing the elements the proper way to change their values, but I don't understand why.
Any suggestion would be greatly appreciated, thanks!
In order to be able to modify a matrix element you need to return a reference to it:
double& Matrix::elem(int i, int j) {
return Matrix_[i][j];
}
and:
double& Matrix::operator()(int i, int j) {
return Matrix_[i][j];
}
You can also add these for const matrices:
double Matrix::elem(int i, int j) const {
return Matrix_[i][j];
}
and:
double Matrix::operator()(int i, int j) const {
return Matrix_[i][j];
}
I have declared a matrix this way:
double **MB;
MB = new double *[650000];
for (int count = 0; count < 650000; count++)
MB[count] = new double [2];
Now I want to call my matrix in a function that should modify it.
bool notindic (..., double MB [][2], ...) {}
and in the main:
notindic(..., MB, ...)
Now it gives me this error: *[Error] cannot convert 'double**' to 'double ()[2]' for argument '3' to 'bool notindic(std::string, std::string, double ()[2], int, int)'
How can I fix it?
Thank you.
Just pass array pointer as parameter
#include <iostream>
const int NX = 65;
const int NY = 2;
bool notindic(double** MB) {
for(int i = 0; i < NX; ++i) {
for(int j = 0; j < NY; ++j) {
MB[i][j] = i + j;
}
}
}
int main() {
double **MB = new double *[650000];
for (int count = 0; count < 650000; count++) {
MB[count] = new double [2];
}
notindic(MB);
for(int i = 0; i < NX; ++i) {
for(int j = 0; j < NY; ++j) {
std::cout << MB[i][j] << " ";
}
std::cout << std::endl;
}
}
Forget about all that pointer nonsense. It's error-prone, exception-unsafe, hard to write, hard to read, hard to maintain and may perform poorly. Represent your matrix as std::vector<double> and calculate the offsets accordingly.
bool notindic (std::vector<double> const& matrix, int m) {
// ...
auto const element = matrix[y * m + x];
// ...
}
auto const m = 650000;
auto const n = 2;
std::vector<double> my_matrix(m * n);
auto const result = notindic(my_matrix, m);
While you're at it, wrap it in a class like this:
template <class T>
class Matrix final
{
public:
Matrix(int m, int n) :
data(m * n),
m(m)
{
}
T& operator()(int x, int y)
{
return data[y * m + x];
}
T const& operator()(int x, int y) const
{
return data[y * m + x];
}
private:
std::vector<T> data;
int m;
};
bool notindic (Matrix<double> const& matrix) {
// ...
auto const element = matrix(x, y);
// ...
}
auto const m = 650000;
auto const n = 2;
Matrix<double> my_matrix(m, n);
auto const result = notindic(my_matrix);
Add additional member functions if you need them.