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

Generic non-SIMD, platform-agnostic version? #59

Open
barracuda156 opened this issue Nov 7, 2024 · 2 comments
Open

Generic non-SIMD, platform-agnostic version? #59

barracuda156 opened this issue Nov 7, 2024 · 2 comments

Comments

@barracuda156
Copy link

The website mentions that "EigenRand currently supports only x86-64 architecture (SSE, AVX, AVX2) and ARM64 NEON". Can non-SIMD version be added? Or it won't make sense?
Otherwise other platforms (PowerPC, RISC-V) cannot be supported.

@bab2min
Copy link
Owner

bab2min commented Nov 8, 2024

Hi @barracuda156 ,
In short, EigenRand already has non-SIMD version, and it may run on other platforms, but without acceleration. The reason I marked this as unsupported on other platforms is because I haven't (and don't plan on) testing that it works on all platforms, it doesn't mean that it's actually impossible to compile on PowerPC or RISC-V.

EigenRand actually has non-SIMD implementations like Eigen does. Every RNG of EigenRand has both scalarOp implementation (non-SIMD) & packetOp implementation (SIMD) and proper operation would be selected by Eigen's logic.

For example, StdNormalGen has both scalar version:

template<typename Rng>
EIGEN_STRONG_INLINE const _Scalar operator() (Rng&& rng)
{
using namespace Eigen::internal;
if (valid)
{
valid = false;
return OptCacheStore::get<_Scalar>();
}
valid = true;
_Scalar v1, v2, sx;
for (int _i = 0; ; ++_i)
{
EIGENRAND_CHECK_INFINITY_LOOP();
v1 = 2 * ur(rng) - 1;
v2 = 2 * ur(rng) - 1;
sx = v1 * v1 + v2 * v2;
if (sx && sx < 1) break;
}
_Scalar fx = std::sqrt((_Scalar)-2.0 * std::log(sx) / sx);
OptCacheStore::get<_Scalar>() = fx * v2;
return fx * v1;
}

and packet version:
template<typename Packet, typename Rng>
EIGEN_STRONG_INLINE const Packet packetOp(Rng&& rng)
{
using namespace Eigen::internal;
if (valid)
{
valid = false;
return OptCacheStore::template get<Packet>();
}
valid = true;
Packet u1 = ur.template packetOp<Packet>(rng),
u2 = ur.template packetOp<Packet>(rng);
u1 = psub(pset1<Packet>(1), u1);
auto radius = psqrt(pmul(pset1<Packet>(-2), plog(u1)));
auto theta = pmul(pset1<Packet>(2 * constant::pi), u2);
Packet sintheta, costheta;
psincos(theta, sintheta, costheta);
OptCacheStore::template get<Packet>() = pmul(radius, costheta);
return pmul(radius, sintheta);
}
};

And only when packet operation is available, the latter one is selected by PacketAccess attribute.

enum { Cost = HugeCost, PacketAccess = std::is_floating_point<_Scalar>::value ? packet_traits<_Scalar>::HasExp : packet_traits<_Scalar>::Vectorizable, IsRepeatable = false };

So, if you compile the code using EigenRand on other platforms, PacketAccess would be 0 and the scalar one would be selected. If any compile errors appears, please report because it must be a bug of EigenRand.

@barracuda156
Copy link
Author

Thank you very much for a detailed reply! I will try it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants