Re: [eigen] State of eigen support for small integers(16 bit, signed/unsigned) |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/eigen Archives
]
- To: eigen@xxxxxxxxxxxxxxxxxxx
- Subject: Re: [eigen] State of eigen support for small integers(16 bit, signed/unsigned)
- From: Benoit Jacob <jacob.benoit.1@xxxxxxxxx>
- Date: Thu, 20 Aug 2009 16:08:44 -0400
- Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:content-type; bh=7dsWwNtDNVyE2htOUb2gO9hqa4NchlbW9ClvcoVjlOA=; b=seKJUkqB7w101UGIeEdVJEvD5Kv2MSHP1CiPiNMPE7PqE1lLq+HiIea9Fy9xkJpAnn kr4MKarbFloazBPk0JC8BhwghL1JoMUW0uVB7zr97atVcmZwbPm5CoUNHrLhYdUj4bWB qQcEgxXUDkQAwQg79qjA+WTWG93j+J+F/Qok8=
- Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :content-type; b=SeGSInWLzFuioT+AnC3wnQfdPlD/50qY2XaMePpMjD7SR3er7DeSWWdUPqxV8d+5vb rQLlvCCm814KHtMMKbqB62mTSn5EZIsGYYf0kG5vIJZ1T8fmqw470rofXVZrONr1fAVo ZfiPmo9eTmYNGD+462KO1JD5YNbhrsDxdJyiE=
OK, let me try to put all my answers in 1 mail:
2009/8/20 Rohit Garg <rpg.314@xxxxxxxxx>:
> I have been adding stuff for both short and unsigned short so far.
> Mainly because I need right shifts for unsigned shorts, which are
> logical bit shifts, instead of algebraic bit shifts. I turns out that
> there are a few missing intrinsics in sse2, as usual, for signed and
> unsigned stuff. So it will perhaps be easier to just do short instead
> of unsigned short too. There is no unsigned int in eigen for instance.
Feel free to add unsigned int! So far we had no use for it. but I
understand that as one works with shorter int types, the benefit of
unsigned gets bigger, so there's a need for uint8 and uint16, and then
for consistency it makes sense to add uint32.
Especially if you start a new module SmallIntegers, it really doesn't
hurt to add more types.
> But since I need, unsigned shorts, I propose, that we offer both
> algebraic, logical bit shifting as a template parameter option.
> Comments?
I dont have an opinion as I didnt think much about it, just go ahead
with your plans, he who codes decides :)
> Yes, int and short do share an sse type. :(
> Now what?
Now I can see 2 options.
a) you make the packet types like Packet4f be structs containing a
typedef to the standard SSE type --- instead of being that typedef
themselves. In this way, you make sure that they are treated as
different types even if the underlying SSE type is the same.
or:
b) you rework our design here, ei_unpacket_traits doesnt work so you
come up with a design to do without it. That would mean that the
functions that are template only in the packet type and then use
ei_unpacket_traits to find back the scalar type, should be modified to
be template in the scalar type and use ei_packet_traits to find the
packet type.
Perhaps try b first (just my gut feeling) and fall back to a if it
gets too cumbersome.
>There is a Functors.h in both array and core. I looked at both. The
>array one provides a EIGEN_FUNCTOR_PLUGIN. But I dont know how to put
>it to use. Suggestions?
at the moment it escapes me why EIGEN_FUNCTOR_PLUGIN is needed and i
don't understand the comment there anymore :(
Let's explain.
Binary shifting is an operation that applies to every coeff of a
single matrix. That is what we call a "coefficient-wise unary
operation". Since such expressions share a lot of generic code, we
unified them in a CwiseUnaryOp class. See Core/CwiseUnaryOp.h. That
class is template in the matrix type (to which the operation is
applied) and also in another type describing the operation : that is
called the functor type. Whenever you want to add another such
operation, all you need to do is:
1) add the corresponding functor
2) add a specialization of ei_functor_traits for this functor
3) expose this operation in class MatrixBase or in class Cwise (the
latter means that the user will have to write .cwise() to use your
operation: in your case, I think that's what we want)
Here's an example. Let's look at the implementation between .cwise().abs().
Go to Core/Functors.h line 164:
/** \internal
* \brief Template functor to compute the absolute value of a scalar
*
* \sa class CwiseUnaryOp, Cwise::abs
*/
template<typename Scalar> struct ei_scalar_abs_op EIGEN_EMPTY_STRUCT {
typedef typename NumTraits<Scalar>::Real result_type;
EIGEN_STRONG_INLINE const result_type operator() (const Scalar& a)
const { return ei_abs(a); }
};
template<typename Scalar>
struct ei_functor_traits<ei_scalar_abs_op<Scalar> >
{
enum {
Cost = NumTraits<Scalar>::AddCost,
PacketAccess = false // this could actually be vectorized with SSSE3.
};
};
as you can see we define the functor class ei_scalar_abs_op, it
contains 2 informations: the result type, and an operator() doing the
computation, making objects of this class behave like functions --
whence the name functor. Note the EIGEN_EMPTY_STRUCT, it helps some
compiler generate better code by adding a dummy member. Then comes the
specialization of ei_functor_traits, where we give a rough estimate of
the cost of the operation, and we tell whether the operation could be
vectorized.
Note that if the operation could be vectorized, the functor should
provide a packetOp() method. Here's an example, line 30 in the same
file:
/** \internal
* \brief Template functor to compute the sum of two scalars
*
* \sa class CwiseBinaryOp, MatrixBase::operator+, class
PartialRedux, MatrixBase::sum()
*/
template<typename Scalar> struct ei_scalar_sum_op EIGEN_EMPTY_STRUCT {
EIGEN_STRONG_INLINE const Scalar operator() (const Scalar& a, const
Scalar& b) const { return a + b; }
template<typename PacketScalar>
EIGEN_STRONG_INLINE const PacketScalar packetOp(const PacketScalar&
a, const PacketScalar& b) const
{ return ei_padd(a,b); }
};
template<typename Scalar>
struct ei_functor_traits<ei_scalar_sum_op<Scalar> > {
enum {
Cost = NumTraits<Scalar>::AddCost,
PacketAccess = ei_packet_traits<Scalar>::size>1
};
};
Here we say that there is "packet access" i.e. vectorizability if the
scalar type has the property that packets contain more than one
scalar. That's a standard way of saying whether a scalar type is
vectorizable.
Notice how the packetOp method is templated in the packet type! That's
a little discrepancy with the operator(), i admit.
OK, then how is this exposed in the API ? Go to Core/CwiseUnaryOp.h line 130:
/** \returns an expression of the coefficient-wise absolute value of \c *this
*
* Example: \include Cwise_abs.cpp
* Output: \verbinclude Cwise_abs.out
*
* \sa abs2()
*/
template<typename ExpressionType>
EIGEN_STRONG_INLINE const EIGEN_CWISE_UNOP_RETURN_TYPE(ei_scalar_abs_op)
Cwise<ExpressionType>::abs() const
{
return _expression();
}
so this is a method in class Cwise, in practice the user constructs an
object of class Cwise by calling .cwise(), that's why the resulting
API is .cwise().abs().
> I am through modding Numtraits.h, MathFunctions .h to add shorts. In
> the PacketMath.h there are these 3 functions whose purpose I did not
> understand.
>
> ei_palign_impl
> ei_preduxp
> ei_predux
Have a look at Core/GenericPacketMath.h, this is where all the comments are ;)
Benoit