Updates will be applied - 3:30pm EDT (UTC -400). No downtime expected.

Commit 9a98d9cb authored by Robert Maynard's avatar Robert Maynard Committed by Code Review

Merge topic 'faster_ComputeScalarRange' into master

196bbe8a Refactor GetRange to compute all ranges at the same time.
parents f8cad122 196bbe8a
......@@ -500,6 +500,7 @@ set_source_files_properties(
vtkWeakPointerBase.cxx
vtkUnicodeString.cxx
vtkDataArrayTemplate.h
vtkDataArrayPrivate.txx
vtkABI.h
vtkArrayInterpolate.h
......
......@@ -52,6 +52,16 @@ int TestDataArray(int,char *[])
array->InsertNextTuple1(cc);
}
array->GetRange( range, 0 ); // Range is now 0-9. Used to check MTimes.
if ( range[0] != 0 || range[1] != 9 )
{
cerr
<< "Getting range (" << range[0] << "-" << range[1]
<< ") of array marked for modified didn't cause recomputation of range!";
array->Delete();
return 1;
}
cerr << "Getting range (" << range[0] << "-" << range[1] << ")" << std::endl;
array->RemoveFirstTuple();
array->RemoveTuple(3);
array->RemoveTuple(4);
......
......@@ -13,6 +13,7 @@
=========================================================================*/
#include "vtkDataArray.h"
#include "vtkDataArrayPrivate.txx"
#include "vtkBitArray.h"
#include "vtkCharArray.h"
#include "vtkDataArrayIteratorMacro.h"
......@@ -178,68 +179,34 @@ void vtkCopyTuples1(IT* input, vtkDataArray* output,
}
}
//----------------------------------------------------------------------------
template <class ValueType, class InputIterator>
void vtkDataArrayComputeScalarRange(InputIterator begin, InputIterator end,
int numberOfComponents, int component,
double range[2])
template<typename InfoType, typename KeyType>
bool hasValidKey(InfoType info, KeyType key,
unsigned long mtime, double range[2] )
{
ValueType minmaxRange[2];
minmaxRange[0] = *begin;
minmaxRange[1] = *begin;
//Special case for single value scalar range. This is done to help the
//compiler detect it can perform loop optimizations.
if(numberOfComponents == 1 && component == 0)
{
for(; begin != end; ++begin)
{
minmaxRange[0] = std::min(*begin,minmaxRange[0]);
minmaxRange[1] = std::max(*begin,minmaxRange[1]);
}
}
else
if ( info->Has( key ) )
{
//Since we are dealing with arbitrary iterators instead of pointers
//we can't compute the exact end iterator value since the iterators them
//selves could check if they go out of bounds. So we have to use the less
//then operator to evaluate that our iterator is still valid.
for(; begin < end; begin+=numberOfComponents)
if ( mtime <= info->GetMTime() )
{
minmaxRange[0] = std::min(begin[component],minmaxRange[0]);
minmaxRange[1] = std::max(begin[component],minmaxRange[1]);
info->Get( key, range );
return true;
}
}
range[0] = std::min(static_cast<double>(minmaxRange[0]),range[0]);
range[1] = std::max(static_cast<double>(minmaxRange[1]),range[1]);
return false;
}
//----------------------------------------------------------------------------
template <class ValueType, class InputIterator>
void vtkDataArrayComputeVectorRange(InputIterator begin, InputIterator end,
int numberOfComponents, double range[2])
template<typename InfoType, typename KeyType, typename ComponentKeyType>
bool hasValidKey(InfoType info, KeyType key, ComponentKeyType ckey,
unsigned long mtime, double range[2], int comp )
{
range[0] = vtkTypeTraits<double>::Max();
range[1] = vtkTypeTraits<double>::Min();
//iterate over all the tuples
for(; begin != end; begin+=numberOfComponents)
if ( info->Has( key ) )
{
double squaredSum = 0;
for(int i=0; i < numberOfComponents; ++i)
if ( mtime <= info->GetMTime() )
{
squaredSum += static_cast<double>(begin[i]) *
static_cast<double>(begin[i]);
info->Get( key )->GetInformationObject(comp)->Get( ckey, range );
return true;
}
range[0] = std::min(squaredSum,range[0]);
range[1] = std::max(squaredSum,range[1]);
}
//now that we have computed the smallest and largest value, take the
//square root of that value.
range[0] = sqrt(range[0]);
range[1] = sqrt(range[1]);
return false;
}
} // end anon namespace
......@@ -1076,113 +1043,104 @@ int vtkDataArray::CopyInformation(vtkInformation* infoFrom, int deep)
//----------------------------------------------------------------------------
void vtkDataArray::ComputeRange(double range[2], int comp)
{
//this method needs a large refactoring to be way easier to read
if ( comp >= this->NumberOfComponents )
{ // Ignore requests for nonexistent components.
return;
}
// If we got component -1 on a vector array, compute vector magnitude.
if (comp < 0 && this->NumberOfComponents == 1)
{
comp = 0;
}
range[0] = vtkTypeTraits<double>::Max();
range[1] = vtkTypeTraits<double>::Min();
vtkInformation* info = this->GetInformation();
vtkInformationDoubleVectorKey* rkey;
if ( comp < 0 )
{
rkey = L2_NORM_RANGE();
//hasValidKey will update range to the cached value if it exists.
if( !hasValidKey(info,rkey,this->GetMTime(),range) )
{
this->ComputeVectorRange(range);
info->Set( rkey, range, 2 );
}
return;
}
else
{
vtkInformationVector* infoVec;
if ( ! info->Has( PER_COMPONENT() ) )
{
infoVec = vtkInformationVector::New();
info->Set( PER_COMPONENT(), infoVec );
infoVec->FastDelete();
}
else
{
infoVec = info->Get( PER_COMPONENT() );
}
int vlen = infoVec->GetNumberOfInformationObjects();
if ( vlen < this->NumberOfComponents )
{
infoVec->SetNumberOfInformationObjects( this->NumberOfComponents );
double rtmp[2];
rtmp[0] = vtkTypeTraits<double>::Max();
rtmp[1] = vtkTypeTraits<double>::Min();
// Since the MTime() of these new keys will be newer than this->MTime(), we must
// be sure that their ranges are marked "invalid" so that we know they must be
// computed.
for ( int i = vlen; i < this->NumberOfComponents; ++i )
{
infoVec->GetInformationObject( i )->Set( COMPONENT_RANGE(), rtmp, 2 );
}
}
info = infoVec->GetInformationObject( comp );
rkey = COMPONENT_RANGE();
}
if ( info->Has( rkey ) )
{
if ( this->GetMTime() <= info->GetMTime() )
//hasValidKey will update range to the cached value if it exists.
if( !hasValidKey(info, PER_COMPONENT(), rkey,
this->GetMTime(), range, comp))
{
info->Get( rkey, range );
if ( range[0] != vtkTypeTraits<double>::Max() &&
range[1] != vtkTypeTraits<double>::Min() )
double* allCompRanges = new double[this->NumberOfComponents*2];
const bool computed = this->ComputeScalarRange(allCompRanges);
if(computed)
{
// Only accept these values if they are reasonable. Otherwise, it is an
// indication that they've never been computed before.
return;
//construct the keys and add them to the info object
vtkInformationVector* infoVec = vtkInformationVector::New();
info->Set( PER_COMPONENT(), infoVec );
infoVec->SetNumberOfInformationObjects( this->NumberOfComponents );
for ( int i = 0; i < this->NumberOfComponents; ++i )
{
infoVec->GetInformationObject( i )->Set( rkey,
allCompRanges+(i*2),
2 );
}
infoVec->FastDelete();
//update the range passed in since we have a valid range.
range[0] = allCompRanges[comp*2];
range[1] = allCompRanges[(comp*2)+1];
}
delete[] allCompRanges;
}
}
range[0] = vtkTypeTraits<double>::Max();
range[1] = vtkTypeTraits<double>::Min();
if ( comp < 0 )
{
this->ComputeVectorRange(range);
}
else
{
this->ComputeScalarRange(range, comp);
}
info->Set( rkey, range, 2 );
}
//----------------------------------------------------------------------------
void vtkDataArray::ComputeScalarRange(double range[2], int comp)
bool vtkDataArray::ComputeScalarRange(double* ranges)
{
bool computed = false;
switch (this->GetDataType())
{
vtkDataArrayIteratorMacro(this,
vtkDataArrayComputeScalarRange<vtkDAValueType>(
computed = vtkDataArrayPrivate::DoComputeScalarRange<vtkDAValueType>(
vtkDABegin, vtkDAEnd,
this->GetNumberOfComponents(), comp,
range)
this->GetNumberOfComponents(),
ranges)
);
default:
break;
}
return computed;
}
//-----------------------------------------------------------------------------
void vtkDataArray::ComputeVectorRange(double range[2])
bool vtkDataArray::ComputeVectorRange(double range[2])
{
bool computed = false;
switch (this->GetDataType())
{
vtkDataArrayIteratorMacro(this,
vtkDataArrayComputeVectorRange<vtkDAValueType>(
computed = vtkDataArrayPrivate::DoComputeVectorRange<vtkDAValueType>(
vtkDABegin, vtkDAEnd,
this->GetNumberOfComponents(), range)
this->GetNumberOfComponents(),
range)
);
default:
break;
}
return computed;
}
//----------------------------------------------------------------------------
......
......@@ -401,9 +401,15 @@ protected:
virtual void ComputeRange(double range[2], int comp);
// Description:
// Slow range computation methods. Reimplement.
virtual void ComputeScalarRange(double range[2], int comp);
virtual void ComputeVectorRange(double range[2]);
// Computes the range for each component of an array, the length
// of \a ranges must be two times the number of components.
// Returns true if the range was computed. Will return false
// if you try to compute the range of an array of length zero.
virtual bool ComputeScalarRange(double* ranges);
// Returns true if the range was computed. Will return false
// if you try to compute the range of an array of length zero.
virtual bool ComputeVectorRange(double range[2]);
// Construct object with default tuple dimension (number of components) of 1.
vtkDataArray();
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkDataArrayPrivate.txx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#ifndef __vtkDataArrayPrivate_txx
#define __vtkDataArrayPrivate_txx
#include "vtkTypeTraits.h"
#include <algorithm>
#include <cassert> // for assert()
namespace vtkDataArrayPrivate{
//----------------------------------------------------------------------------
template <class ValueType, int NumComps, int RangeSize>
struct ComputeScalarRange
{
template<class InputIteratorType>
bool operator()(InputIteratorType begin, InputIteratorType end,
double* ranges)
{
ValueType tempRange[RangeSize];
for(int i=0; i < NumComps; ++i)
{
tempRange[ (i*2) ] = vtkTypeTraits<ValueType>::Max();
tempRange[ (i*2)+1 ] = vtkTypeTraits<ValueType>::Min();
}
//compute the range for each component of the data array at the same time
for(InputIteratorType value = begin; value != end; value+=NumComps)
{
for(int i=0; i < NumComps; ++i)
{
tempRange[ (i*2) ] = std::min(value[i],tempRange[ (i*2)] );
tempRange[ (i*2)+1 ] = std::max(value[i],tempRange[ (i*2)+1] );
}
}
//convert the range to doubles
for(int i=0; i < NumComps; ++i)
{
ranges[ (i*2) ] = static_cast<double>(tempRange[ (i*2) ]);
ranges[ (i*2)+1 ] = static_cast<double>(tempRange[ (i*2)+1 ]);
}
return true;
}
};
//----------------------------------------------------------------------------
template <class ValueType, class InputIteratorType>
bool DoComputeScalarRange(InputIteratorType begin, InputIteratorType end,
const int numComp, double* ranges)
{
//setup the initial ranges to be the max,min for double
for(int i=0; i < numComp; ++i)
{
ranges[ (i*2) ] = vtkTypeTraits<double>::Max();
ranges[ (i*2)+1 ] = vtkTypeTraits<double>::Min();
}
//do this after we make sure range is max to min
if(begin == end)
{
return false;
}
//verify that length of the array is divisible by the number of components
//this will make sure we don't walk off the end
assert((end-begin) % numComp == 0);
//Special case for single value scalar range. This is done to help the
//compiler detect it can perform loop optimizations.
if(numComp == 1)
{
return ComputeScalarRange<ValueType,1,2>()(begin,end,ranges);
}
else if(numComp == 2)
{
return ComputeScalarRange<ValueType,2,4>()(begin,end,ranges);
}
else if(numComp == 3)
{
return ComputeScalarRange<ValueType,3,6>()(begin,end,ranges);
}
else if(numComp == 4)
{
return ComputeScalarRange<ValueType,4,8>()(begin,end,ranges);
}
else if(numComp == 5)
{
return ComputeScalarRange<ValueType,5,10>()(begin,end,ranges);
}
else if(numComp == 6)
{
return ComputeScalarRange<ValueType,6,12>()(begin,end,ranges);
}
else if(numComp == 7)
{
return ComputeScalarRange<ValueType,7,14>()(begin,end,ranges);
}
else if(numComp == 8)
{
return ComputeScalarRange<ValueType,8,16>()(begin,end,ranges);
}
else if(numComp == 9)
{
return ComputeScalarRange<ValueType,9,18>()(begin,end,ranges);
}
else
{
//initialize the temp range storage to min/max pairs
ValueType* tempRange = new ValueType[numComp*2];
for(int i=0; i < numComp; ++i)
{
tempRange[ (i*2) ] = vtkTypeTraits<ValueType>::Max();
tempRange[ (i*2)+1 ] = vtkTypeTraits<ValueType>::Min();
}
//compute the range for each component of the data array at the same time
for(InputIteratorType value = begin; value != end; value+=numComp)
{
for(int i=0; i < numComp; ++i)
{
tempRange[ (i*2) ] = std::min(value[i],tempRange[ (i*2)] );
tempRange[ (i*2)+1 ] = std::max(value[i],tempRange[ (i*2)+1] );
}
}
//convert the range to doubles
for(int i=0; i < numComp; ++i)
{
ranges[ (i*2) ] = static_cast<double>(tempRange[ (i*2) ]);
ranges[ (i*2)+1 ] = static_cast<double>(tempRange[ (i*2)+1 ]);
}
//cleanup temp range storage
delete[] tempRange;
return true;
}
}
//----------------------------------------------------------------------------
template <class ValueType, class InputIteratorType>
bool DoComputeVectorRange(InputIteratorType begin, InputIteratorType end,
int numComp, double range[2])
{
range[0] = vtkTypeTraits<double>::Max();
range[1] = vtkTypeTraits<double>::Min();
//do this after we make sure range is max to min
if(begin == end)
{
return false;
}
//verify that length of the array is divisible by the number of components
//this will make sure we don't walk off the end
assert((end-begin) % numComp == 0);
//iterate over all the tuples
for(InputIteratorType value = begin; value != end; value+=numComp)
{
double squaredSum = 0.0;
for(int i=0; i < numComp; ++i)
{
const double t = static_cast<double>(value[i]);
squaredSum += t * t;
}
range[0] = std::min(squaredSum,range[0]);
range[1] = std::max(squaredSum,range[1]);
}
//now that we have computed the smallest and largest value, take the
//square root of that value.
range[0] = sqrt(range[0]);
range[1] = sqrt(range[1]);
return true;
}
}
#endif
// VTK-HeaderTest-Exclude: vtkDataArrayPrivate.txx
......@@ -342,8 +342,8 @@ protected:
int SaveUserArray;
int DeleteMethod;
virtual void ComputeScalarRange(double range[2], int comp);
virtual void ComputeVectorRange(double range[2]);
virtual bool ComputeScalarRange(double* ranges);
virtual bool ComputeVectorRange(double range[2]);
private:
vtkDataArrayTemplate(const vtkDataArrayTemplate&); // Not implemented.
void operator=(const vtkDataArrayTemplate&); // Not implemented.
......
......@@ -16,6 +16,7 @@
#define __vtkDataArrayTemplate_txx
#include "vtkDataArrayTemplate.h"
#include "vtkDataArrayPrivate.txx"
#include "vtkArrayIteratorTemplate.h"
#include "vtkTypedDataArrayIterator.h"
......@@ -969,82 +970,26 @@ vtkIdType vtkDataArrayTemplate<T>::InsertNextValue(T f)
//----------------------------------------------------------------------------
template <class T>
void vtkDataArrayTemplate<T>::ComputeScalarRange(double range[2], int comp)
bool vtkDataArrayTemplate<T>::ComputeScalarRange(double* ranges)
{
// Compute range only if there are data.
T* begin = this->Array+comp;
T* end = this->Array+comp+this->MaxId+1;
if(begin == end)
{
return;
}
// Compute the range of scalar values.
int numComp = this->NumberOfComponents;
T tempRange[2] = {vtkTypeTraits<T>::Max(), vtkTypeTraits<T>::Min()};
if(numComp == 1)
{
//Special case for single value scalar range. This is done to help the
//compiler detect it can perform loop optimizations.
for(T* i = begin; i != end; ++i)
{
tempRange[0] = std::min(*i,tempRange[0]);
tempRange[1] = std::max(*i,tempRange[1]);
}
}
else
{
for(T* i = begin; i != end; i += numComp)
{
tempRange[0] = std::min(*i,tempRange[0]);
tempRange[1] = std::max(*i,tempRange[1]);
}
}
const T* begin = this->Array;
const T* end = this->Array+this->MaxId+1;
const int numComp = this->NumberOfComponents;
range[0] = static_cast<double>(tempRange[0]);
range[1] = static_cast<double>(tempRange[1]);
return vtkDataArrayPrivate::DoComputeScalarRange<T>(begin,end,
numComp,ranges);
}
//----------------------------------------------------------------------------
template <class T>
void vtkDataArrayTemplate<T>::ComputeVectorRange(double range[2])
bool vtkDataArrayTemplate<T>::ComputeVectorRange(double range[2])
{
// Compute range only if there are data.
T* begin = this->Array;
T* end = this->Array+this->MaxId+1;
if(begin == end)
{
return;
}
// Compute the range of vector magnitude squared.
int numComp = this->NumberOfComponents;
range[0] = vtkTypeTraits<double>::Max();
range[1] = vtkTypeTraits<double>::Min();
for(T* i = begin; i != end; i += numComp)
{
double s = 0.0;
for(int j=0; j < numComp; ++j)
{
double t = static_cast<double>(i[j]);
s += t*t;
}
if(s < range[0])
{
range[0] = s;
}
// this cannot be an elseif because there may be only one vector in which
// case the range[1] would be left at a bad value
if(s > range[1])
{
range[1] = s;
}
}
const T* begin = this->Array;
const T* end = this->Array+this->MaxId+1;
const int numComp = this->NumberOfComponents;
// Store the range of vector magnitude.
range[0] = sqrt(range[0]);
range[1] = sqrt(range[1]);
return vtkDataArrayPrivate::DoComputeVectorRange<T>(begin,end,
numComp,range);
}
//----------------------------------------------------------------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment