File: eval.cc

package info (click to toggle)
vspline 1.1.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,708 kB
  • sloc: cpp: 15,905; ansic: 443; sh: 17; makefile: 2
file content (245 lines) | stat: -rw-r--r-- 8,714 bytes parent folder | download
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 ;
    }
  }
}