@@ -4134,18 +4134,30 @@ Tcl_TimeRateObjCmd(
4134
4134
int result , i ;
4135
4135
Tcl_Obj * calibrate = NULL , * direct = NULL ;
4136
4136
Tcl_WideUInt count = 0 ; /* Holds repetition count */
4137
+ Tcl_WideUInt lastCount = 0 ; /* Repetition count since last calculation. */
4137
4138
Tcl_WideInt maxms = WIDE_MIN ;
4138
4139
/* Maximal running time (in milliseconds) */
4139
- Tcl_WideUInt maxcnt = WIDE_MAX ;
4140
+ Tcl_WideUInt maxcnt = UWIDE_MAX ;
4140
4141
/* Maximal count of iterations. */
4141
4142
Tcl_WideUInt threshold = 1 ; /* Current threshold for check time (faster
4142
4143
* repeat count without time check) */
4143
- Tcl_WideUInt maxIterTm = 1 ; /* Max time of some iteration as max
4144
- * threshold, additionally avoiding divide to
4145
- * zero (i.e., never < 1) */
4146
- unsigned short factor = 50 ; /* Factor (4..50) limiting threshold to avoid
4144
+ Tcl_WideUInt avgIterTm = 1 ; /* Average time of all processed iterations. */
4145
+ Tcl_WideUInt lastIterTm = 1 ;/* Average time of last block of iterations. */
4146
+ double estIterTm = 1.0 ; /* Estimated time of next iteration,
4147
+ * considering the growth of lastIterTm. */
4148
+ #ifdef TCL_WIDE_CLICKS
4149
+ # define TR_SCALE 10 /* Fraction is 10ns (from wide click 100ns). */
4150
+ #else
4151
+ # define TR_SCALE 100 /* Fraction is 10ns (from 1us = 1000ns). */
4152
+ #endif
4153
+ #define TR_MIN_FACTOR 2 /* Min allowed factor calculating threshold. */
4154
+ #define TR_MAX_FACTOR 50 /* Max allowed factor calculating threshold. */
4155
+ #define TR_FACT_SINGLE_ITER 25 /* This or larger factor value will force the
4156
+ * threshold 1, to avoid drastic growth of
4157
+ * execution time by quadratic O() complexity. */
4158
+ unsigned short factor = 16 ; /* Factor (2..50) limiting threshold to avoid
4147
4159
* growth of execution time. */
4148
- Tcl_WideInt start , middle , stop ;
4160
+ Tcl_WideInt start , last , middle , stop ;
4149
4161
#ifndef TCL_WIDE_CLICKS
4150
4162
Tcl_Time now ;
4151
4163
#endif /* !TCL_WIDE_CLICKS */
@@ -4351,7 +4363,7 @@ Tcl_TimeRateObjCmd(
4351
4363
*/
4352
4364
4353
4365
#ifdef TCL_WIDE_CLICKS
4354
- start = middle = TclpGetWideClicks ();
4366
+ start = last = middle = TclpGetWideClicks ();
4355
4367
4356
4368
/*
4357
4369
* Time to stop execution (in wide clicks).
@@ -4363,7 +4375,7 @@ Tcl_TimeRateObjCmd(
4363
4375
start = now .sec ;
4364
4376
start *= 1000000 ;
4365
4377
start += now .usec ;
4366
- middle = start ;
4378
+ last = middle = start ;
4367
4379
4368
4380
/*
4369
4381
* Time to stop execution (in microsecs).
@@ -4444,61 +4456,93 @@ Tcl_TimeRateObjCmd(
4444
4456
}
4445
4457
4446
4458
/*
4447
- * Don't calculate threshold by few iterations, because sometimes
4448
- * first iteration(s) can be too fast or slow (cached, delayed
4449
- * clean up, etc).
4459
+ * Average iteration time (scaled) in fractions of wide clicks
4460
+ * or microseconds.
4450
4461
*/
4451
4462
4452
- if (count < 10 ) {
4453
- threshold = 1 ;
4454
- continue ;
4455
- }
4456
-
4457
- /*
4458
- * Average iteration time in microsecs.
4459
- */
4460
-
4461
- threshold = (middle - start ) / count ;
4462
- if (threshold > maxIterTm ) {
4463
- maxIterTm = threshold ;
4463
+ threshold = (Tcl_WideUInt )(middle - start ) * TR_SCALE / count ;
4464
+ if (threshold > avgIterTm ) {
4464
4465
4465
4466
/*
4466
4467
* Iterations seem to be longer.
4467
4468
*/
4468
4469
4469
- if (threshold > maxIterTm * 2 ) {
4470
+ if (threshold > avgIterTm * 2 ) {
4470
4471
factor *= 2 ;
4471
- if (factor > 50 ) {
4472
- factor = 50 ;
4473
- }
4474
4472
} else {
4475
- if (factor < 50 ) {
4476
- factor ++ ;
4477
- }
4473
+ factor ++ ;
4478
4474
}
4479
- } else if (factor > 4 ) {
4475
+ if (factor > TR_MAX_FACTOR ) {
4476
+ factor = TR_MAX_FACTOR ;
4477
+ }
4478
+ } else if (factor > TR_MIN_FACTOR ) {
4480
4479
/*
4481
4480
* Iterations seem to be shorter.
4482
4481
*/
4483
4482
4484
- if (threshold < (maxIterTm / 2 )) {
4483
+ if (threshold < (avgIterTm / 2 )) {
4485
4484
factor /= 2 ;
4486
- if (factor < 4 ) {
4487
- factor = 4 ;
4485
+ if (factor < TR_MIN_FACTOR ) {
4486
+ factor = TR_MIN_FACTOR ;
4488
4487
}
4489
4488
} else {
4490
4489
factor -- ;
4491
4490
}
4492
4491
}
4493
4492
4493
+ if (!threshold ) {
4494
+ /* too short and too few iterations */
4495
+ threshold = 1 ;
4496
+ continue ;
4497
+ }
4498
+ avgIterTm = threshold ;
4499
+
4494
4500
/*
4495
- * As relation between remaining time and time since last check,
4496
- * maximal some % of time (by factor), so avoid growing of the
4497
- * execution time if iterations are not consistent, e.g. was
4498
- * continuously on time).
4501
+ * Estimate last iteration time growth and time of next iteration.
4499
4502
*/
4503
+ lastCount = count - lastCount ;
4504
+ if (last != start && lastCount ) {
4505
+ Tcl_WideUInt lastTm ;
4506
+
4507
+ lastTm = (Tcl_WideUInt )(middle - last ) * TR_SCALE / lastCount ;
4508
+ estIterTm = (double )lastTm / (lastIterTm ? lastIterTm : avgIterTm );
4509
+ lastIterTm = lastTm > avgIterTm ? lastTm : avgIterTm ;
4510
+ } else {
4511
+ lastIterTm = avgIterTm ;
4512
+ }
4513
+ estIterTm *= lastIterTm ;
4514
+ last = middle ; lastCount = count ;
4500
4515
4501
- threshold = ((stop - middle ) / maxIterTm ) / factor + 1 ;
4516
+ /*
4517
+ * Calculate next threshold to check.
4518
+ * Firstly check iteration time is not larger than remaining time,
4519
+ * considering last known iteration growth factor.
4520
+ */
4521
+ threshold = (Tcl_WideUInt )(stop - middle ) * TR_SCALE ;
4522
+ /*
4523
+ * Estimated count of iteration til the end of execution.
4524
+ * Thereby 2.5% longer execution time would be OK.
4525
+ */
4526
+ if (threshold / estIterTm < 0.975 ) {
4527
+ /* estimated time for next iteration is too large */
4528
+ break ;
4529
+ }
4530
+ threshold /= estIterTm ;
4531
+ /*
4532
+ * Don't use threshold by few iterations, because sometimes
4533
+ * first iteration(s) can be too fast or slow (cached, delayed
4534
+ * clean up, etc). Also avoid unexpected execution time growth,
4535
+ * so if iterations continuously grow, stay by single iteration.
4536
+ */
4537
+ if (count < 10 || factor >= TR_FACT_SINGLE_ITER ) {
4538
+ threshold = 1 ;
4539
+ continue ;
4540
+ }
4541
+ /*
4542
+ * Reduce it by last known factor, to avoid unexpected execution
4543
+ * time growth if iterations are not consistent (may be longer).
4544
+ */
4545
+ threshold = threshold / factor + 1 ;
4502
4546
if (threshold > 100000 ) { /* fix for too large threshold */
4503
4547
threshold = 100000 ;
4504
4548
}
@@ -4648,6 +4692,10 @@ Tcl_TimeRateObjCmd(
4648
4692
TclReleaseByteCode (codePtr );
4649
4693
}
4650
4694
return result ;
4695
+ #undef TR_SCALE
4696
+ #undef TR_MIN_FACTOR
4697
+ #undef TR_MAX_FACTOR
4698
+ #undef TR_FACT_SINGLE_ITER
4651
4699
}
4652
4700
4653
4701
/*
0 commit comments