Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LinalgRefactor - SGVector - inplace add #3391

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/shogun/mathematics/linalg/LinalgBackendBase.h
Expand Up @@ -94,6 +94,19 @@ class LinalgBackendBase
DEFINE_FOR_ALL_PTYPE(BACKEND_GENERIC_ADD, SGVector)
#undef BACKEND_GENERIC_ADD

/**
* Wrapper method of add operation the operation result = alpha*a + beta*b.
*
* @see linalg::add
*/
#define BACKEND_GENERIC_IN_PLACE_ADD(Type, Container) \
virtual void add(Container<Type>& a, Container<Type>& b, Type alpha, Type beta, Container<Type>& result) const \
{ \
SG_SNOTIMPLEMENTED; \
}
DEFINE_FOR_ALL_PTYPE(BACKEND_GENERIC_IN_PLACE_ADD, SGVector)
#undef BACKEND_GENERIC_IN_PLACE_ADD

/**
* Wrapper method of vector dot-product that works with generic vectors.
*
Expand Down
20 changes: 20 additions & 0 deletions src/shogun/mathematics/linalg/LinalgBackendEigen.h
Expand Up @@ -84,6 +84,15 @@ class LinalgBackendEigen : public LinalgBackendBase
DEFINE_FOR_ALL_PTYPE(BACKEND_GENERIC_ADD, SGVector)
#undef BACKEND_GENERIC_ADD

/** Implementation of @see LinalgBackendBase::add */
#define BACKEND_GENERIC_IN_PLACE_ADD(Type, Container) \
virtual void add(Container<Type>& a, Container<Type>& b, Type alpha, Type beta, Container<Type>& result) const \
{ \
add_impl(a, b, alpha, beta, result); \
}
DEFINE_FOR_ALL_PTYPE(BACKEND_GENERIC_IN_PLACE_ADD, SGVector)
#undef BACKEND_GENERIC_IN_PLACE_ADD

/** Implementation of @see LinalgBackendBase::dot */
#define BACKEND_GENERIC_DOT(Type, Container) \
virtual Type dot(const Container<Type>& a, const Container<Type>& b) const \
Expand Down Expand Up @@ -157,6 +166,17 @@ class LinalgBackendEigen : public LinalgBackendBase
return c;
}

/** Eigen3 vector result = alpha*A + beta*B method */
template <typename T>
void add_impl(SGVector<T>& a, SGVector<T>& b, T alpha, T beta, SGVector<T>& result) const
{
typename SGVector<T>::EigenVectorXtMap a_eig = a;
typename SGVector<T>::EigenVectorXtMap b_eig = b;
typename SGVector<T>::EigenVectorXtMap result_eig = result;

result_eig = alpha * a_eig + beta * b_eig;
}

/** Eigen3 vector dot-product method */
template <typename T>
T dot_impl(const SGVector<T>& a, const SGVector<T>& b) const
Expand Down
20 changes: 20 additions & 0 deletions src/shogun/mathematics/linalg/LinalgBackendViennacl.h
Expand Up @@ -75,6 +75,14 @@ class LinalgBackendViennaCL : public LinalgBackendGPUBase
DEFINE_FOR_ALL_PTYPE(BACKEND_GENERIC_ADD, SGVector)
#undef BACKEND_GENERIC_ADD

#define BACKEND_GENERIC_IN_PLACE_ADD(Type, Container) \
virtual void add(Container<Type>& a, Container<Type>& b, Type alpha, Type beta, Container<Type>& result) const \
{ \
add_impl(a, b, alpha, beta, result); \
}
DEFINE_FOR_ALL_PTYPE(BACKEND_GENERIC_IN_PLACE_ADD, SGVector)
#undef BACKEND_GENERIC_ADD

/** Implementation of @see LinalgBackendBase::dot */
#define BACKEND_GENERIC_DOT(Type, Container) \
virtual Type dot(const Container<Type>& a, const Container<Type>& b) const \
Expand Down Expand Up @@ -170,6 +178,18 @@ class LinalgBackendViennaCL : public LinalgBackendGPUBase
return SGVector<T>(c_gpu, a.size());
}

/** ViennaCL vector result = alpha*A + beta*B method */
template <typename T>
void add_impl(SGVector<T>& a, SGVector<T>& b, T alpha, T beta, SGVector<T>& result) const
{
GPUMemoryViennaCL<T>* a_gpu = cast_to_viennacl(a);
GPUMemoryViennaCL<T>* b_gpu = cast_to_viennacl(b);
GPUMemoryViennaCL<T>* result_gpu = cast_to_viennacl(result);

result_gpu->data_vector(a.size()) =
alpha * a_gpu->data_vector(a.size()) + beta * b_gpu->data_vector(b.size());
}

/** ViennaCL vector dot-product method. */
template <typename T>
T dot_impl(const SGVector<T>& a, const SGVector<T>& b) const
Expand Down
23 changes: 23 additions & 0 deletions src/shogun/mathematics/linalg/LinalgNamespace.h
Expand Up @@ -112,6 +112,29 @@ Container<T> add(const Container<T>& a, const Container<T>& b, T alpha=1, T beta
return infer_backend(a, b)->add(a, b, alpha, beta);
}

/**
* Performs the operation result = alpha*a + beta*b.
*
* @param a first vector
* @param b second vector
* @param result the vector that saves the result
* @param alpha constant to be multiplied by the first vector
* @param beta constant to be multiplied by the second vector
*/
template <typename T>
void add(SGVector<T>& a, SGVector<T>& b, SGVector<T>& result, T alpha=1, T beta=1)
{
REQUIRE(a.vlen == b.vlen, "Length of vector a (%d) doesn't match vector b (%d).\n", a.vlen, b.vlen);
REQUIRE(result.vlen == b.vlen, "Length of vector result (%d) doesn't match vector a (%d).\n", result.vlen, a.vlen);

REQUIRE(!(result.on_gpu()^a.on_gpu()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XOR for victory! :)

"Cannot operate with vector result on_gpu (%d) and vector a on_gpu (%d).\n", result.on_gpu(), a.on_gpu());
REQUIRE(!(result.on_gpu()^b.on_gpu()),
"Cannot operate with vector result on_gpu (%d) and vector b on_gpu (%d).\n", result.on_gpu(), b.on_gpu());

infer_backend(a, b)->add(a, b, alpha, beta, result);
}

/**
* Vector dot-product that works with generic vectors.
*
Expand Down
Expand Up @@ -26,6 +26,26 @@ TEST(LinalgBackendEigen, SGVector_add)
EXPECT_NEAR(alpha*A[i]+beta*B[i], result[i], 1e-15);
}

TEST(LinalgBackendEigen, add_in_place)
{
const float64_t alpha = 0.3;
const float64_t beta = -1.5;

SGVector<float64_t> A(9), B(9), C(9);

for (index_t i = 0; i < 9; ++i)
{
A[i] = i;
B[i] = 0.5*i;
C[i] = i;
}

add(A, B, A, alpha, beta);

for (index_t i = 0; i < 9; ++i)
EXPECT_NEAR(alpha*C[i]+beta*B[i], A[i], 1e-15);
}

TEST(LinalgBackendEigen, SGVector_dot)
{
const index_t size = 3;
Expand Down
Expand Up @@ -34,6 +34,32 @@ TEST(LinalgBackendViennaCL, SGVector_add)
EXPECT_NEAR(alpha*A[i]+beta*B[i], result[i], 1e-15);
}

TEST(LinalgBackendViennaCL, add_in_place)
{
sg_linalg->set_gpu_backend(new LinalgBackendViennaCL());

const float32_t alpha = 0.3;
const float32_t beta = -1.5;

SGVector<float32_t> A(9), B(9), C(9);
SGVector<float32_t> A_gpu, B_gpu;

for (index_t i = 0; i < 9; ++i)
{
A[i] = i;
B[i] = 0.5*i;
C[i] = i;
}
A_gpu = to_gpu(A);
B_gpu = to_gpu(B);

add(A_gpu, B_gpu, A_gpu, alpha, beta);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this is an awkward interface, suggestions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean inplace should be avoided where possible (for speed reasons), and only applied in particular cases where the memory cannot be afforded. So in that sense I dont care if the interface is awkward. But still would be nicer to do this somehow nicer (i.e. with a flag or so)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer do it like scale(a, scale, inplace=T), which is real "inplace".
I don't see the necessity of allocating the memory in advance like:

Vector output(size);
scale(a, scale, output);

compared to
Vector output = scale(a, scale, output);
@karlnapf

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OXPHOS the 'necessity' of allocating the memory advance is because some people want to actually have full control of the mallocs happening within the code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah imagine you already have a big matrix where you want to put things for some reason (I sometimes do in my code)

A = from_gpu(A_gpu);

for (index_t i = 0; i < 9; ++i)
EXPECT_NEAR(alpha*C[i]+beta*B[i], A[i], 1e-15);
}

TEST(LinalgBackendViennaCL, SGVector_dot)
{
sg_linalg->set_gpu_backend(new LinalgBackendViennaCL());
Expand Down