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 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
|
/************************************************************************/
/* */
/* vspline - a set of generic tools for creation and evaluation */
/* of uniform b-splines */
/* */
/* Copyright 2015 - 2023 by Kay F. Jahnke */
/* */
/* Permission is hereby granted, free of charge, to any person */
/* obtaining a copy of this software and associated documentation */
/* files (the "Software"), to deal in the Software without */
/* restriction, including without limitation the rights to use, */
/* copy, modify, merge, publish, distribute, sublicense, and/or */
/* sell copies of the Software, and to permit persons to whom the */
/* Software is furnished to do so, subject to the following */
/* conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the */
/* Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
/* OTHER DEALINGS IN THE SOFTWARE. */
/* */
/************************************************************************/
/// \file eval.cc
///
/// \brief simple demonstration of creation and evaluation of a b-spline
///
/// takes a set of knot point values from std::cin, calculates a 1D b-spline
/// over them, and evaluates it at coordinates taken from std::cin.
/// The output shows how the coordinate is split into integral and real
/// part and the result of evaluating the spline at this point.
/// Note how the coordinate is automatically folded into the defined range.
///
/// Two evaluations are demonstrated, using unvectorized and vectorized
/// input/output.
///
/// Since this is a convenient place to add code for testing evaluation
/// speed, you may pass a number on the command line. eval.cc will then
/// perform as many evaluations and print the time it took.
/// The evaluations will be distributed to several threads, so there is
/// quite a bit of overhead; pass numbers from 1000000 up to get realistic
/// timings.
///
/// compile: ./examples.sh eval.cc
///
/// compare speed test results from different compilers/back-ends, using
/// 'echo' to provide the arguments normally taken from cin. This invocation
/// runs the test with a periodic degree-19 spline over knot points 0 and 1
/// and only shows the name of the binary and the result of the speed test:
///
/// for f in *eval*++; do echo $f;echo 19 2 0 1 . .5 | ./$f 100000000 | grep took; done
///
/// this will produce output like:
/// hwy_eval_clang++
/// 100000000 evaluations took 2229 ms
/// hwy_eval_g++
/// 100000000 evaluations took 2527 ms
/// stds_eval_clang++
/// 100000000 evaluations took 3401 ms
/// stds_eval_g++
/// 100000000 evaluations took 2351 ms
/// vc_eval_clang++
/// 100000000 evaluations took 3281 ms
/// vc_eval_g++
/// 100000000 evaluations took 3385 ms
/// vs_eval_clang++
/// 100000000 evaluations took 3883 ms
/// vs_eval_g++
/// 100000000 evaluations took 5479 ms
///
/// note how both the back-end and the compiler make a difference, highway
/// and clang++ currently coming out on top on my system. This is a moving
/// target, as both the back-ends and the compilers evolve.
#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>
#include <vspline/vspline.h>
// you can use float, but then can't use very high spline degrees.
// If you use long double, the code won't be vectorized in hardware.
typedef double dtype ;
typedef double rc_type ;
enum { vsize = vspline::vector_traits<dtype>::vsize } ;
// speed_test will perform as many vectorized evaluations as the
// range it receives spans. The evaluation is always at the same place,
// trying to lower all memory access effects to the minimum.
template < class ev_type >
void speed_test ( ev_type & ev , std::size_t nr_ev )
{
typename ev_type::in_v in = dtype(.111) ;
typename ev_type::out_v out ;
while ( nr_ev-- )
ev.eval ( in , out ) ;
}
int main ( int argc , char * argv[] )
{
long TIMES = 0 ;
if ( argc > 1 )
TIMES = std::atoi ( argv[1] ) ;
// get the spline degree and boundary conditions from the console
std::cout << "enter spline degree: " ;
int spline_degree ;
std::cin >> spline_degree ;
int bci = -1 ;
vspline::bc_code bc ;
while ( bci < 1 || bci > 4 )
{
std::cout << "choose boundary condition" << std::endl ;
std::cout << "1) MIRROR" << std::endl ;
std::cout << "2) PERIODIC" << std::endl ;
std::cout << "3) REFLECT" << std::endl ;
std::cout << "4) NATURAL" << std::endl ;
std::cin >> bci ;
}
switch ( bci )
{
case 1 :
bc = vspline::MIRROR ;
break ;
case 2 :
bc = vspline::PERIODIC ;
break ;
case 3 :
bc = vspline::REFLECT ;
break ;
case 4 :
bc = vspline::NATURAL ;
break ;
}
// obtain knot point values
dtype v ;
std::vector<dtype> dv ;
std::cout << "enter knot point values (end with single '.')" << std::endl ;
while ( std::cin >> v )
dv.push_back ( v ) ;
std::cin.clear() ;
std::cin.ignore() ;
// fix the type for the bspline object
typedef vspline::bspline < dtype , 1 > spline_type ;
spline_type bsp ( dv.size() , spline_degree , bc ) ;
std::cout << "created bspline object:" << std::endl << bsp << std::endl ;
// fill the data into the spline's 'core' area
for ( size_t i = 0 ; i < dv.size() ; i++ )
bsp.core[i] = dv[i] ;
// prefilter the data
bsp.prefilter() ;
std::cout << std::fixed << std::showpoint << std::setprecision(12) ;
std::cout << "spline coefficients (with frame)" << std::endl ;
for ( auto& coeff : bsp.container )
std::cout << " " << coeff << std::endl ;
// obtain a 'safe' evaluator which folds incoming coordinates
// into the defined range
auto ev = vspline::make_safe_evaluator ( bsp ) ;
typedef vspline::evaluator < int , double > iev_t ;
auto iev = iev_t ( bsp ) ;
int ic ;
rc_type rc ;
dtype res ;
std::cout << "enter coordinates to evaluate (end with EOF)"
<< std::endl ;
while ( std::cin >> rc )
{
if ( rc < bsp.lower_limit(0) || rc > bsp.upper_limit(0) )
{
std::cout << "warning: " << rc
<< " is outside the spline's defined range."
<< std::endl ;
std::cout << "using automatic folding to process this value."
<< std::endl ;
}
// evaluate the spline at this location
ev.eval ( rc , res ) ;
std::cout << rc << " -> " << res << std::endl ;
if ( TIMES )
{
std::chrono::system_clock::time_point start
= std::chrono::system_clock::now() ;
// for the speed test we build a plain evaluator; we'll
// only be evaluating the spline near 0.0 repeatedly, so
// we don't need folding into the safe range. We're not
// fixing 'specialize', so evaluation will be general
// b-spline evaluation, even for degree 0 and 1 splines.
auto ev = vspline::evaluator < dtype , dtype , vsize > ( bsp ) ;
std::size_t share = ( TIMES / vsize ) / vspline::default_njobs ;
std::function < void() > payload =
[&]() { speed_test ( ev , share ) ; } ;
vspline::multithread ( payload , vspline::default_njobs ) ;
std::chrono::system_clock::time_point end
= std::chrono::system_clock::now() ;
std::cout << TIMES << " evaluations took "
<< std::chrono::duration_cast<std::chrono::milliseconds>
( end - start ) . count()
<< " ms" << std::endl ;
}
}
}
|