1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkFastNumericConversion.cxx,v $
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.
=========================================================================*/
// .NAME vtkFastNumericConversion - Enables fast conversion of floating point to fixed point
// .SECTION Description
// vtkFastNumericConversion uses a portable (assuming IEEE format) method for converting single and
// double precision floating point values to a fixed point representation. This allows fast
// integer flooring on platforms, such as Intel X86, in which CPU floating point flooring
// algorithms are very slow. It is based on the techniques described in Chris Hecker's article,
// "Let's Get to the (Floating) Point", in Game Developer Magazine, Feb/Mar 1996, and the
// techniques described in Michael Herf's website, http://www.stereopsis.com/FPU.html.
// The Hecker article can be found at http://www.d6.com/users/checker/pdfs/gdmfp.pdf.
// Unfortunately, each of these techniques is incomplete, and doesn't floor properly,
// in a way that depends on how many bits are reserved for fixed point fractional use, due to
// failing to properly account for the default round-towards-even rounding mode of the X86. Thus,
// my implementation incorporates some rounding correction that undoes the rounding that the
// FPU performs during denormalization of the floating point value. Note that
// the rounding affect I'm talking about here is not the effect on the fistp instruction,
// but rather the effect that occurs during the denormalization of a value that occurs when
// adding it to a much larger value. The bits must be shifted to the right, and when a "1" bit
// falls off the edge, the rounding mode determines what happens next, in order
// to avoid completely "losing" the 1-bit. Furthermore, my implementation works on Linux, where the
// default precision mode is 64-bit extended precision.
// This class is contributed to VTK by Chris Volpe of Applied Research Associates, Inc.
// (My employer requires me to say that -- CRV)
#include "vtkFastNumericConversion.h"
#include "vtkObjectFactory.h"
#include "vtkTimerLog.h"
vtkCxxRevisionMacro(vtkFastNumericConversion, "$Revision: 1.2 $");
vtkStandardNewMacro(vtkFastNumericConversion);
int vtkFastNumericConversion::TestQuickFloor(double val)
{
return vtkFastNumericConversion::QuickFloor(val);
}
int vtkFastNumericConversion::TestSafeFloor(double val)
{
return vtkFastNumericConversion::SafeFloor(val);
}
int vtkFastNumericConversion::TestRound(double val)
{
return vtkFastNumericConversion::Round(val);
}
int vtkFastNumericConversion::TestConvertFixedPointIntPart(double val)
{
int frac;
return this->ConvertFixedPoint(val, frac);
}
int vtkFastNumericConversion::TestConvertFixedPointFracPart(double val)
{
int frac;
this->ConvertFixedPoint(val, frac);
return frac;
}
void vtkFastNumericConversion::InternalRebuild()
{
int i;
this->fixRound=.5;
for (i=this->internalReservedFracBits; i; i--)
{
this->fixRound *= .5;
}
this->fracMask = (1<<this->internalReservedFracBits)-1;
this->fpDenormalizer = (((unsigned long)1) << (52-30-this->internalReservedFracBits)) *
this->two30() * this->BorrowBit();
this->epTempDenormalizer = this->fpDenormalizer * (((unsigned long)1) << (63-52));
}
void vtkFastNumericConversion::PrintSelf(ostream &os, vtkIndent indent)
{
os << indent << "ReservedFracBits: " << this->internalReservedFracBits << endl;
os << indent << "Bare time from last PerformanceTest() call: " << this->bare_time << endl;
os << indent << "Cast time from last PerformanceTest() call: " << this->cast_time << endl;
os << indent << "ConvertFixedPoint time from last PerformanceTest() call: " << this->convert_time << endl;
os << indent << "QuickFloor time from last PerformanceTest() call: " << this->quickfloor_time << endl;
os << indent << "SafeFloor time from last PerformanceTest() call: " << this->safefloor_time << endl;
os << indent << "Round time from last PerformanceTest() call: " << this->round_time << endl;
if (this->bare_time != 0.0)
{
// Don't do this if we haven't run the tests yet.
os << indent << "Speedup ratio from cast to quickfloor is: " <<
(this->cast_time-this->bare_time)/(this->quickfloor_time-this->bare_time) << endl;
os << indent << "Speedup ratio from cast to safefloor is: " <<
(this->cast_time-this->bare_time)/(this->safefloor_time-this->bare_time) << endl;
os << indent << "Speedup ratio from cast to round is: " <<
(this->cast_time-this->bare_time)/(this->round_time-this->bare_time) << endl;
}
}
void vtkFastNumericConversion::PerformanceTests(void)
{
const int inner_count = 10000;
const int outer_count = 10000;
double *dval = new double[inner_count];
int *ival = new int[inner_count];
int *frac = new int[inner_count];
int i,o;
vtkTimerLog *timer = vtkTimerLog::New();
for (i=0; i<inner_count; i++)
{
dval[i] = i;
ival[i] = 0;
}
timer->StartTimer();
for (o=0; o<outer_count; o++)
{
for (i=0; i<inner_count; i++)
{
// Pure bit copy
ival[i] = *((int *)(&dval[i]));
}
}
timer->StopTimer();
this->bare_time = timer->GetElapsedTime();
// Compute cast time
timer->StartTimer();
for (o=0; o<outer_count; o++)
{
for (i=0; i<inner_count; i++)
{
ival[i] = (int) dval[i];
}
}
timer->StopTimer();
this->cast_time = timer->GetElapsedTime();
// Compute convert time
timer->StartTimer();
for (o=0; o<outer_count; o++)
{
for (i=0; i<inner_count; i++)
{
ival[i] = this->ConvertFixedPoint(dval[i], frac[i]);
}
}
timer->StopTimer();
this->convert_time = timer->GetElapsedTime();
// Compute quickfloor time
timer->StartTimer();
for (o=0; o<outer_count; o++)
{
for (i=0; i<inner_count; i++)
{
ival[i] = vtkFastNumericConversion::QuickFloor(dval[i]);
}
}
timer->StopTimer();
this->quickfloor_time = timer->GetElapsedTime();
// Compute safefloor time
timer->StartTimer();
for (o=0; o<outer_count; o++)
{
for (i=0; i<inner_count; i++)
{
ival[i] = vtkFastNumericConversion::SafeFloor(dval[i]);
}
}
timer->StopTimer();
this->safefloor_time = timer->GetElapsedTime();
// Compute round time
timer->StartTimer();
for (o=0; o<outer_count; o++)
{
for (i=0; i<inner_count; i++)
{
ival[i] = vtkFastNumericConversion::Round(dval[i]);
}
}
timer->StopTimer();
this->round_time = timer->GetElapsedTime();
delete [] dval;
delete [] ival;
delete [] frac;
timer->Delete();
}
|