Discussion:
[eigen] Raising double to integer powers
Ian Bell
2021-05-10 21:14:42 UTC
Permalink
I have a simple question that I have spent ages trying to work around over
the years and still haven't found a satisfactory solution. I have a single
double value, and without any allocation, I need to generate an Eigen
expression that allows me to take a double to an array of integers. In my
case, 25% of my code's execution time is spent working on this problem, and
I think I should be able to get that contribution back to closer to zero.
Initially I asked the question here:
https://stackoverflow.com/questions/25354205/how-to-raise-double-to-array-powers-in-eigen

This code doesn't compile (e.g., on godbolt):

#include <Eigen/Dense>

int main(){
auto c = Eigen::ArrayXi::LinSpaced(11, 0, 10);
auto r = Eigen::pow(3.7, c);
}

but if I switch the ArrayXi for ArrayXd it compiles (because the scalar
types match), but that is not what I want.

Really, what I need is to call my own implementation of pow(double, int),
because it is MUCH faster than pow(double, double).

In the end, I would like some guidance on how I can craft an Eigen
expression for this use case that will not introduce any intermediate
allocation.

Thanks,
Ian
Ian Bell
2021-05-10 22:27:52 UTC
Permalink
Of course, shortly after having sent this message I figured it out, but it
doesn't actually result in an increase in my throughput sadly. For
posterity:

#include <Eigen/Dense>
#include <iostream>

using namespace Eigen;

struct myUnaryFunctor {
const double m_base;
myUnaryFunctor(double base): m_base(base) {};
typedef double result_type;
result_type operator()(const int &e) const
{
return pow(m_base, e);
}
};

int main()
{
auto e = Eigen::ArrayXi::LinSpaced(11, 0, 10).eval();
double base = 2.9;
std::cout << e.unaryExpr(myUnaryFunctor(base));
}
Post by Ian Bell
I have a simple question that I have spent ages trying to work around over
the years and still haven't found a satisfactory solution. I have a single
double value, and without any allocation, I need to generate an Eigen
expression that allows me to take a double to an array of integers. In my
case, 25% of my code's execution time is spent working on this problem, and
I think I should be able to get that contribution back to closer to zero.
https://stackoverflow.com/questions/25354205/how-to-raise-double-to-array-powers-in-eigen
#include <Eigen/Dense>
int main(){
auto c = Eigen::ArrayXi::LinSpaced(11, 0, 10);
auto r = Eigen::pow(3.7, c);
}
but if I switch the ArrayXi for ArrayXd it compiles (because the scalar
types match), but that is not what I want.
Really, what I need is to call my own implementation of pow(double, int),
because it is MUCH faster than pow(double, double).
In the end, I would like some guidance on how I can craft an Eigen
expression for this use case that will not introduce any intermediate
allocation.
Thanks,
Ian
Marc Glisse
2021-05-10 23:27:55 UTC
Permalink
Post by Ian Bell
Of course, shortly after having sent this message I figured it out, but it
doesn't actually result in an increase in my throughput sadly. For
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
struct myUnaryFunctor {
const double m_base;
myUnaryFunctor(double base): m_base(base) {};
typedef double result_type;
result_type operator()(const int &e) const
{
return pow(m_base, e);
}
};
int main()
{
auto e = Eigen::ArrayXi::LinSpaced(11, 0, 10).eval();
double base = 2.9;
std::cout << e.unaryExpr(myUnaryFunctor(base));
}
Assuming pow is actually your own function and does the usual repeated
squaring, unlike std::pow, this may do a lot of redundant computation (in
particular base*base is computed many times). Do you know anything about
the integers? In particular, are they always small? I assume the LinSpaced
example doesn't look like the true data. Does your pow function already
cache some results?
--
Marc Glisse
Rasmus Munk Larsen
2021-05-10 23:52:15 UTC
Permalink
I recently vectorized the implementation of pow in Eigen for float and
double arguments. It does not apply to pow(float, int), however, but
should give you a significant speedup if you cast your exponents to
double. I thought about implementing a more efficient algorithm if the
exponents are all integers, but didn't get round to it. Could you
please try if this helps you? The improvements are in the master
branch (as well as the 3.4 branch that we are preparing for release).
Post by Marc Glisse
Post by Ian Bell
Of course, shortly after having sent this message I figured it out, but it
doesn't actually result in an increase in my throughput sadly. For
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
struct myUnaryFunctor {
const double m_base;
myUnaryFunctor(double base): m_base(base) {};
typedef double result_type;
result_type operator()(const int &e) const
{
return pow(m_base, e);
}
};
int main()
{
auto e = Eigen::ArrayXi::LinSpaced(11, 0, 10).eval();
double base = 2.9;
std::cout << e.unaryExpr(myUnaryFunctor(base));
}
Assuming pow is actually your own function and does the usual repeated
squaring, unlike std::pow, this may do a lot of redundant computation (in
particular base*base is computed many times). Do you know anything about
the integers? In particular, are they always small? I assume the LinSpaced
example doesn't look like the true data. Does your pow function already
cache some results?
--
Marc Glisse
Ian Bell
2021-05-11 00:09:42 UTC
Permalink
Rasmus, your commits are these, right:

https://gitlab.com/libeigen/eigen/-/commit/88d4c6d4c870f53d129ab5f8b43e01812d9b500e
https://gitlab.com/libeigen/eigen/-/commit/be0574e2159ce3d6a1748ba6060bea5dedccdbc9

Which Array methods pick up these new packet methods?
Post by Rasmus Munk Larsen
I recently vectorized the implementation of pow in Eigen for float and
double arguments. It does not apply to pow(float, int), however, but
should give you a significant speedup if you cast your exponents to
double. I thought about implementing a more efficient algorithm if the
exponents are all integers, but didn't get round to it. Could you
please try if this helps you? The improvements are in the master
branch (as well as the 3.4 branch that we are preparing for release).
Post by Marc Glisse
Post by Ian Bell
Of course, shortly after having sent this message I figured it out,
but it
Post by Marc Glisse
Post by Ian Bell
doesn't actually result in an increase in my throughput sadly. For
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
struct myUnaryFunctor {
const double m_base;
myUnaryFunctor(double base): m_base(base) {};
typedef double result_type;
result_type operator()(const int &e) const
{
return pow(m_base, e);
}
};
int main()
{
auto e = Eigen::ArrayXi::LinSpaced(11, 0, 10).eval();
double base = 2.9;
std::cout << e.unaryExpr(myUnaryFunctor(base));
}
Assuming pow is actually your own function and does the usual repeated
squaring, unlike std::pow, this may do a lot of redundant computation (in
particular base*base is computed many times). Do you know anything about
the integers? In particular, are they always small? I assume the
LinSpaced
Post by Marc Glisse
example doesn't look like the true data. Does your pow function already
cache some results?
--
Marc Glisse
Ian Bell
2021-05-11 00:12:02 UTC
Permalink
Also, Rasmus if you are interested, I can provide some use cases that would
be well suited to additional vectorization. Not sure if I can peer too
deeply into the internals of Eigen, but I'm happy to help around the
margins.
Post by Ian Bell
https://gitlab.com/libeigen/eigen/-/commit/88d4c6d4c870f53d129ab5f8b43e01812d9b500e
https://gitlab.com/libeigen/eigen/-/commit/be0574e2159ce3d6a1748ba6060bea5dedccdbc9
Which Array methods pick up these new packet methods?
Post by Rasmus Munk Larsen
I recently vectorized the implementation of pow in Eigen for float and
double arguments. It does not apply to pow(float, int), however, but
should give you a significant speedup if you cast your exponents to
double. I thought about implementing a more efficient algorithm if the
exponents are all integers, but didn't get round to it. Could you
please try if this helps you? The improvements are in the master
branch (as well as the 3.4 branch that we are preparing for release).
Post by Marc Glisse
Post by Ian Bell
Of course, shortly after having sent this message I figured it out,
but it
Post by Marc Glisse
Post by Ian Bell
doesn't actually result in an increase in my throughput sadly. For
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
struct myUnaryFunctor {
const double m_base;
myUnaryFunctor(double base): m_base(base) {};
typedef double result_type;
result_type operator()(const int &e) const
{
return pow(m_base, e);
}
};
int main()
{
auto e = Eigen::ArrayXi::LinSpaced(11, 0, 10).eval();
double base = 2.9;
std::cout << e.unaryExpr(myUnaryFunctor(base));
}
Assuming pow is actually your own function and does the usual repeated
squaring, unlike std::pow, this may do a lot of redundant computation
(in
Post by Marc Glisse
particular base*base is computed many times). Do you know anything about
the integers? In particular, are they always small? I assume the
LinSpaced
Post by Marc Glisse
example doesn't look like the true data. Does your pow function already
cache some results?
--
Marc Glisse
Rasmus Munk Larsen
2021-05-11 00:46:25 UTC
Permalink
Hi Ian,

They should speed up the `.pow()` method(s) on arrays if the scalar
type is float or double. I'd be happy to learn about additional cases
that are slow for you.

Rasmus
Also, Rasmus if you are interested, I can provide some use cases that would be well suited to additional vectorization. Not sure if I can peer too deeply into the internals of Eigen, but I'm happy to help around the margins.
Post by Ian Bell
https://gitlab.com/libeigen/eigen/-/commit/88d4c6d4c870f53d129ab5f8b43e01812d9b500e
https://gitlab.com/libeigen/eigen/-/commit/be0574e2159ce3d6a1748ba6060bea5dedccdbc9
Which Array methods pick up these new packet methods?
Post by Rasmus Munk Larsen
I recently vectorized the implementation of pow in Eigen for float and
double arguments. It does not apply to pow(float, int), however, but
should give you a significant speedup if you cast your exponents to
double. I thought about implementing a more efficient algorithm if the
exponents are all integers, but didn't get round to it. Could you
please try if this helps you? The improvements are in the master
branch (as well as the 3.4 branch that we are preparing for release).
Post by Marc Glisse
Post by Ian Bell
Of course, shortly after having sent this message I figured it out, but it
doesn't actually result in an increase in my throughput sadly. For
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
struct myUnaryFunctor {
const double m_base;
myUnaryFunctor(double base): m_base(base) {};
typedef double result_type;
result_type operator()(const int &e) const
{
return pow(m_base, e);
}
};
int main()
{
auto e = Eigen::ArrayXi::LinSpaced(11, 0, 10).eval();
double base = 2.9;
std::cout << e.unaryExpr(myUnaryFunctor(base));
}
Assuming pow is actually your own function and does the usual repeated
squaring, unlike std::pow, this may do a lot of redundant computation (in
particular base*base is computed many times). Do you know anything about
the integers? In particular, are they always small? I assume the LinSpaced
example doesn't look like the true data. Does your pow function already
cache some results?
--
Marc Glisse
Rasmus Munk Larsen
2021-05-11 00:48:20 UTC
Permalink
The first commit was:
https://gitlab.com/libeigen/eigen/-/commit/cdd8fdc32e730d5a65796a791ff13a92815c59b9
Post by Ian Bell
https://gitlab.com/libeigen/eigen/-/commit/88d4c6d4c870f53d129ab5f8b43e01812d9b500e
https://gitlab.com/libeigen/eigen/-/commit/be0574e2159ce3d6a1748ba6060bea5dedccdbc9
Which Array methods pick up these new packet methods?
Post by Rasmus Munk Larsen
I recently vectorized the implementation of pow in Eigen for float and
double arguments. It does not apply to pow(float, int), however, but
should give you a significant speedup if you cast your exponents to
double. I thought about implementing a more efficient algorithm if the
exponents are all integers, but didn't get round to it. Could you
please try if this helps you? The improvements are in the master
branch (as well as the 3.4 branch that we are preparing for release).
Post by Marc Glisse
Post by Ian Bell
Of course, shortly after having sent this message I figured it out, but it
doesn't actually result in an increase in my throughput sadly. For
#include <Eigen/Dense>
#include <iostream>
using namespace Eigen;
struct myUnaryFunctor {
const double m_base;
myUnaryFunctor(double base): m_base(base) {};
typedef double result_type;
result_type operator()(const int &e) const
{
return pow(m_base, e);
}
};
int main()
{
auto e = Eigen::ArrayXi::LinSpaced(11, 0, 10).eval();
double base = 2.9;
std::cout << e.unaryExpr(myUnaryFunctor(base));
}
Assuming pow is actually your own function and does the usual repeated
squaring, unlike std::pow, this may do a lot of redundant computation (in
particular base*base is computed many times). Do you know anything about
the integers? In particular, are they always small? I assume the LinSpaced
example doesn't look like the true data. Does your pow function already
cache some results?
--
Marc Glisse
Loading...