Basic Vector Classes

From Daxtoolkit
Jump to: navigation, search

So far, not a lot of though has gone into the basic vector classes. There are two basic vector classes: a dax::Vector3 and a dax::Vector4. They are patterned after the CUDA vector structures. They have x, y, z fields and when compiling with CUDA they actually are typedef’ed CUDA vectors. That said, there might be several improvements possible. This document proposes possible changes to these basic vector classes.

Array Type Accessors

Currently, the basic vectors look like (actually are) simple structs with symbolic accessors (i.e., x, y, z, etc.). Although such accessors are quick to type, they preclude some code simplification such as using them in a small loop. (Then again, this forced loop unrolling may be faster.) It makes it more difficult to create functions template on these vectors since you have to treat the fields specially for each. It also looks inconsistent when mixed with other types of data types that might actually be arrays.

In contrast, a better way might be make a class with the actual storage hidden and an overloaded bracket operator.

class Vector3
{
public:
  dax::Scalar &operator[](int component) { ... }
  const dax::Scalar &operator[](int component) const { ... }
  static int NumberOfComponents() { return 3; } // Should be static const number?
  typedef dax::Scalar ValueType;
  ...

Notice that also added is an easy way to get the number of components, which is useful for template functions. There is also a typedef for the component type it returns to, say, distinguish it from dax::Id3.

Vector Traits

One particular problem with the vector classes is that you cannot do anything with them in a general sense without knowing the type. That is, you cannot create a function templated on the vector type and do much with it. For example, if you look at our simple sin worklet, you see stupidity in the form of having two separate sin worklets, one for a dax::Vector3, one for a dax::Scalar. (And what if you want to sin the components of a Vector4?)

The problem can be solved by declaring vector traits for these basic types. The traits include information about the length of the vector, its component type, and accessor functions. The default implementation of the vector traits can simply get information from the vector classes themselves.

template<class VectorType>
class VectorTraits {
public:
  typedef VectorType::ValueType ValueType;
  static int NumberOfComponents() { return VectorType::NumberOfComponents(); } // Should be const number?
  static ValueType &GetComponent(VectorType &vector, int component) {
    return vector[component]; 
  }
  static const ValueType &GetComponent(const VectorType &vector, int component) {
    return vector[component]; 
  }
  static void SetComponent(VectorType &vector, int component, ValueType value) {
    vector[component] = value;
  }
};

These traits are easily specialized for basic types like dax::Scalar and dax::Id so that they may also be treated like vectors (of size 1).

template<>
class VectorTraits<dax::Scalar> {
public:
  typedef dax::Scalar ValueType;
  static int NumberOfComponents() { return 1; } // Should be const number?
  static dax::Scalar GetComponent(dax::Scalar vector, int component) { return vector; }
  static void SetComponent(dax::Scalar &vector, int component, dax::Scalar value) {
    vector = value;
  }
};

Now something like the sin worklet can be written to be templated on field type and apply the operation to all values in a vector.

template<class CellType, class FieldType>
DAX_WORKLET void Sin(
    DAX_IN dax::exec::WorkMapField<CellType> &work,
    DAX_IN const dax::exec::Field<FieldType> &inField,
    DAX_OUT dax::exec::Field<FieldType> &outField)
{
  FieldType inVector = work.GetFieldValue(inField);
  FieldType outVector;
  for (int component = 0;
       component < dax::VectorTraits<FieldType>::NumberOfComponents();
       component++)
    {
    dax::VectorTraits<FieldType>::ValueType inValue
      = dax::VectorTraits<FieldType>::GetComponent(inVector, component);

    dax::VectorTraits<FieldType>::ValueType outValue
      = sinf(inValue);
    dax::VectorTraits<FieldValue>::SetComponent(outVector, component, outValue);
    }
  work.SetFieldValue(outVector);
}

Templated Vector Lengths

I’ve suggested in an earlier document the possibility of creating a generic Vector class teplated on the length of the vector. This would be easy to implement and would provide some advantages.

  • One implementation addresses any size vectors.
  • Can template on a vector of independent size.

However, these advantages are probably not providing all that much. One issue is that we will probably have to limit the types of fields supported. Thus, supporting an infinite number of vector types (one for any size length) is not practical. Being able to template code based on a vector of arbitrary size doesn’t really provide much that the vector traits cannot. There are also some questions about how you could treat a scalar field as either a dax::Scalar or a vector of size 1.


Robert's Proposal

--Robert Maynard 10:09, 8 December 2011 (EST) I really like the idea of making the Vectors real classes, but I wonder if the traits can be replaced with iterators to simplify the syntax of using the vectors. So the Vector3 class would look more like:

class Vector3
{
public:

  dax::Scalar &operator[](int component) { ... }
  const dax::Scalar &operator[](int component) const { ... }
  static int NumberOfComponents() { return 3; } // Should be static const number?
  typedef dax::Scalar ValueType;
  
  typedef dax_iterator<Vector3 < ValueType > > iterator;
  typedef dax_const_iterator<Vector3 <ValueType > > const_iterator;

  iterator begin();
  iterator end();

  const_iterator begin() const;
  const_iterator end() const;

  ...

Now that we have a iterators the worklet simplifies down to:

template<class CellType, class FieldType>
DAX_WORKLET void Sin(
    DAX_IN dax::exec::WorkMapField<CellType> &work,
    DAX_IN const dax::exec::Field<FieldType> &inField,
    DAX_OUT dax::exec::Field<FieldType> &outField)
{
  FieldType inVector = work.GetFieldValue(inField);
  FieldType outVector;
   
  for(FieldType::const_iterator i = inVector.begin(), FieldType::iterator out = outVector.begin();
      i != inVector.end();
      ++i, ++out)
    {
    *out = sinf(*i);
    }

  work.SetFieldValue(outVector);
}


Acknowledgements

Sandia National Laboratories is a multi-program laboratory managed and operated by Sandia Corporation, a wholly owned subsidiary of Lockheed Martin Corporation, for the U.S. Department of Energy's National Nuclear Security Administration under contract DE-AC04-94AL85000.

SandiaLogo.png DOELogo.png

SAND 2011-9186P