@@ -185,13 +185,19 @@ impl fmt::Debug for TestFn {
185
185
/// This is fed into functions marked with `#[bench]` to allow for
186
186
/// set-up & tear-down before running a piece of code repeatedly via a
187
187
/// call to `iter`.
188
- #[ derive( Copy , Clone ) ]
188
+ #[ derive( Clone ) ]
189
189
pub struct Bencher {
190
- iterations : u64 ,
191
- dur : Duration ,
190
+ mode : BenchMode ,
191
+ summary : Option < stats :: Summary > ,
192
192
pub bytes : u64 ,
193
193
}
194
194
195
+ #[ derive( Clone , PartialEq , Eq ) ]
196
+ pub enum BenchMode {
197
+ Auto ,
198
+ Single ,
199
+ }
200
+
195
201
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
196
202
pub enum ShouldPanic {
197
203
No ,
@@ -1444,138 +1450,148 @@ impl Bencher {
1444
1450
pub fn iter < T , F > ( & mut self , mut inner : F )
1445
1451
where F : FnMut ( ) -> T
1446
1452
{
1447
- let start = Instant :: now ( ) ;
1448
- let k = self . iterations ;
1449
- for _ in 0 ..k {
1450
- black_box ( inner ( ) ) ;
1453
+ if self . mode == BenchMode :: Single {
1454
+ ns_iter_inner ( & mut inner, 1 ) ;
1455
+ return ;
1451
1456
}
1452
- self . dur = start. elapsed ( ) ;
1453
- }
1454
1457
1455
- pub fn ns_elapsed ( & mut self ) -> u64 {
1456
- self . dur . as_secs ( ) * 1_000_000_000 + ( self . dur . subsec_nanos ( ) as u64 )
1458
+ self . summary = Some ( iter ( & mut inner) ) ;
1457
1459
}
1458
1460
1459
- pub fn ns_per_iter ( & mut self ) -> u64 {
1460
- if self . iterations == 0 {
1461
- 0
1462
- } else {
1463
- self . ns_elapsed ( ) / cmp:: max ( self . iterations , 1 )
1464
- }
1465
- }
1466
-
1467
- pub fn bench_n < F > ( & mut self , n : u64 , f : F )
1468
- where F : FnOnce ( & mut Bencher )
1461
+ pub fn bench < F > ( & mut self , mut f : F ) -> Option < stats:: Summary >
1462
+ where F : FnMut ( & mut Bencher )
1469
1463
{
1470
- self . iterations = n;
1471
1464
f ( self ) ;
1465
+ return self . summary ;
1472
1466
}
1467
+ }
1473
1468
1474
- // This is a more statistics-driven benchmark algorithm
1475
- pub fn auto_bench < F > ( & mut self , mut f : F ) -> stats:: Summary
1476
- where F : FnMut ( & mut Bencher )
1477
- {
1478
- // Initial bench run to get ballpark figure.
1479
- let mut n = 1 ;
1480
- self . bench_n ( n, |x| f ( x) ) ;
1481
-
1482
- // Try to estimate iter count for 1ms falling back to 1m
1483
- // iterations if first run took < 1ns.
1484
- if self . ns_per_iter ( ) == 0 {
1485
- n = 1_000_000 ;
1486
- } else {
1487
- n = 1_000_000 / cmp:: max ( self . ns_per_iter ( ) , 1 ) ;
1488
- }
1489
- // if the first run took more than 1ms we don't want to just
1490
- // be left doing 0 iterations on every loop. The unfortunate
1491
- // side effect of not being able to do as many runs is
1492
- // automatically handled by the statistical analysis below
1493
- // (i.e. larger error bars).
1494
- if n == 0 {
1495
- n = 1 ;
1469
+ fn ns_from_dur ( dur : Duration ) -> u64 {
1470
+ dur. as_secs ( ) * 1_000_000_000 + ( dur. subsec_nanos ( ) as u64 )
1471
+ }
1472
+
1473
+ fn ns_iter_inner < T , F > ( inner : & mut F , k : u64 ) -> u64
1474
+ where F : FnMut ( ) -> T
1475
+ {
1476
+ let start = Instant :: now ( ) ;
1477
+ for _ in 0 ..k {
1478
+ black_box ( inner ( ) ) ;
1479
+ }
1480
+ return ns_from_dur ( start. elapsed ( ) ) ;
1481
+ }
1482
+
1483
+
1484
+ pub fn iter < T , F > ( inner : & mut F ) -> stats:: Summary
1485
+ where F : FnMut ( ) -> T
1486
+ {
1487
+ // Initial bench run to get ballpark figure.
1488
+ let ns_single = ns_iter_inner ( inner, 1 ) ;
1489
+
1490
+ // Try to estimate iter count for 1ms falling back to 1m
1491
+ // iterations if first run took < 1ns.
1492
+ let ns_target_total = 1_000_000 ; // 1ms
1493
+ let mut n = ns_target_total / cmp:: max ( 1 , ns_single) ;
1494
+
1495
+ // if the first run took more than 1ms we don't want to just
1496
+ // be left doing 0 iterations on every loop. The unfortunate
1497
+ // side effect of not being able to do as many runs is
1498
+ // automatically handled by the statistical analysis below
1499
+ // (i.e. larger error bars).
1500
+ n = cmp:: max ( 1 , n) ;
1501
+
1502
+ let mut total_run = Duration :: new ( 0 , 0 ) ;
1503
+ let samples: & mut [ f64 ] = & mut [ 0.0_f64 ; 50 ] ;
1504
+ loop {
1505
+ let loop_start = Instant :: now ( ) ;
1506
+
1507
+ for p in & mut * samples {
1508
+ * p = ns_iter_inner ( inner, n) as f64 / n as f64 ;
1496
1509
}
1497
1510
1498
- let mut total_run = Duration :: new ( 0 , 0 ) ;
1499
- let samples: & mut [ f64 ] = & mut [ 0.0_f64 ; 50 ] ;
1500
- loop {
1501
- let loop_start = Instant :: now ( ) ;
1511
+ stats:: winsorize ( samples, 5.0 ) ;
1512
+ let summ = stats:: Summary :: new ( samples) ;
1502
1513
1503
- for p in & mut * samples {
1504
- self . bench_n ( n , |x| f ( x ) ) ;
1505
- * p = self . ns_per_iter ( ) as f64 ;
1506
- }
1514
+ for p in & mut * samples {
1515
+ let ns = ns_iter_inner ( inner , 5 * n ) ;
1516
+ * p = ns as f64 / ( 5 * n ) as f64 ;
1517
+ }
1507
1518
1508
- stats:: winsorize ( samples, 5.0 ) ;
1509
- let summ = stats:: Summary :: new ( samples) ;
1519
+ stats:: winsorize ( samples, 5.0 ) ;
1520
+ let summ5 = stats:: Summary :: new ( samples) ;
1510
1521
1511
- for p in & mut * samples {
1512
- self . bench_n ( 5 * n, |x| f ( x) ) ;
1513
- * p = self . ns_per_iter ( ) as f64 ;
1514
- }
1522
+ let loop_run = loop_start. elapsed ( ) ;
1515
1523
1516
- stats:: winsorize ( samples, 5.0 ) ;
1517
- let summ5 = stats:: Summary :: new ( samples) ;
1518
- let loop_run = loop_start. elapsed ( ) ;
1524
+ // If we've run for 100ms and seem to have converged to a
1525
+ // stable median.
1526
+ if loop_run > Duration :: from_millis ( 100 ) && summ. median_abs_dev_pct < 1.0 &&
1527
+ summ. median - summ5. median < summ5. median_abs_dev {
1528
+ return summ5;
1529
+ }
1519
1530
1520
- // If we've run for 100ms and seem to have converged to a
1521
- // stable median.
1522
- if loop_run > Duration :: from_millis ( 100 ) && summ. median_abs_dev_pct < 1.0 &&
1523
- summ. median - summ5. median < summ5. median_abs_dev {
1524
- return summ5;
1525
- }
1531
+ total_run = total_run + loop_run;
1532
+ // Longest we ever run for is 3s.
1533
+ if total_run > Duration :: from_secs ( 3 ) {
1534
+ return summ5;
1535
+ }
1526
1536
1527
- total_run = total_run + loop_run;
1528
- // Longest we ever run for is 3s.
1529
- if total_run > Duration :: from_secs ( 3 ) {
1537
+ // If we overflow here just return the results so far. We check a
1538
+ // multiplier of 10 because we're about to multiply by 2 and the
1539
+ // next iteration of the loop will also multiply by 5 (to calculate
1540
+ // the summ5 result)
1541
+ n = match n. checked_mul ( 10 ) {
1542
+ Some ( _) => n * 2 ,
1543
+ None => {
1530
1544
return summ5;
1531
1545
}
1532
-
1533
- // If we overflow here just return the results so far. We check a
1534
- // multiplier of 10 because we're about to multiply by 2 and the
1535
- // next iteration of the loop will also multiply by 5 (to calculate
1536
- // the summ5 result)
1537
- n = match n. checked_mul ( 10 ) {
1538
- Some ( _) => n * 2 ,
1539
- None => return summ5,
1540
- } ;
1541
- }
1546
+ } ;
1542
1547
}
1543
1548
}
1544
1549
1545
1550
pub mod bench {
1546
1551
use std:: cmp;
1547
- use std :: time :: Duration ;
1548
- use super :: { Bencher , BenchSamples } ;
1552
+ use stats ;
1553
+ use super :: { Bencher , BenchSamples , BenchMode } ;
1549
1554
1550
1555
pub fn benchmark < F > ( f : F ) -> BenchSamples
1551
1556
where F : FnMut ( & mut Bencher )
1552
1557
{
1553
1558
let mut bs = Bencher {
1554
- iterations : 0 ,
1555
- dur : Duration :: new ( 0 , 0 ) ,
1559
+ mode : BenchMode :: Auto ,
1560
+ summary : None ,
1556
1561
bytes : 0 ,
1557
1562
} ;
1558
1563
1559
- let ns_iter_summ = bs. auto_bench ( f) ;
1564
+ return match bs. bench ( f) {
1565
+ Some ( ns_iter_summ) => {
1566
+ let ns_iter = cmp:: max ( ns_iter_summ. median as u64 , 1 ) ;
1567
+ let mb_s = bs. bytes * 1000 / ns_iter;
1560
1568
1561
- let ns_iter = cmp:: max ( ns_iter_summ. median as u64 , 1 ) ;
1562
- let mb_s = bs. bytes * 1000 / ns_iter;
1563
-
1564
- BenchSamples {
1565
- ns_iter_summ : ns_iter_summ,
1566
- mb_s : mb_s as usize ,
1567
- }
1569
+ BenchSamples {
1570
+ ns_iter_summ : ns_iter_summ,
1571
+ mb_s : mb_s as usize ,
1572
+ }
1573
+ }
1574
+ None => {
1575
+ // iter not called, so no data.
1576
+ // FIXME: error in this case?
1577
+ let samples: & mut [ f64 ] = & mut [ 0.0_f64 ; 1 ] ;
1578
+ BenchSamples {
1579
+ ns_iter_summ : stats:: Summary :: new ( samples) ,
1580
+ mb_s : 0 ,
1581
+ }
1582
+ }
1583
+ } ;
1568
1584
}
1569
1585
1570
1586
pub fn run_once < F > ( f : F )
1571
- where F : FnOnce ( & mut Bencher )
1587
+ where F : FnMut ( & mut Bencher )
1572
1588
{
1573
1589
let mut bs = Bencher {
1574
- iterations : 0 ,
1575
- dur : Duration :: new ( 0 , 0 ) ,
1590
+ mode : BenchMode :: Single ,
1591
+ summary : None ,
1576
1592
bytes : 0 ,
1577
1593
} ;
1578
- bs. bench_n ( 1 , f) ;
1594
+ bs. bench ( f) ;
1579
1595
}
1580
1596
}
1581
1597
@@ -1585,6 +1601,8 @@ mod tests {
1585
1601
TestDescAndFn , TestOpts , run_test, MetricMap , StaticTestName , DynTestName ,
1586
1602
DynTestFn , ShouldPanic } ;
1587
1603
use std:: sync:: mpsc:: channel;
1604
+ use bench;
1605
+ use Bencher ;
1588
1606
1589
1607
#[ test]
1590
1608
pub fn do_not_run_ignored_tests ( ) {
@@ -1880,4 +1898,34 @@ mod tests {
1880
1898
m1. insert_metric ( "in-both-want-upwards-and-improved" , 1000.0 , -10.0 ) ;
1881
1899
m2. insert_metric ( "in-both-want-upwards-and-improved" , 2000.0 , -10.0 ) ;
1882
1900
}
1901
+
1902
+ #[ test]
1903
+ pub fn test_bench_once_no_iter ( ) {
1904
+ fn f ( _: & mut Bencher ) { }
1905
+ bench:: run_once ( f) ;
1906
+ }
1907
+
1908
+ #[ test]
1909
+ pub fn test_bench_once_iter ( ) {
1910
+ fn f ( b : & mut Bencher ) {
1911
+ b. iter ( || {
1912
+ } )
1913
+ }
1914
+ bench:: run_once ( f) ;
1915
+ }
1916
+
1917
+ #[ test]
1918
+ pub fn test_bench_no_iter ( ) {
1919
+ fn f ( _: & mut Bencher ) { }
1920
+ bench:: benchmark ( f) ;
1921
+ }
1922
+
1923
+ #[ test]
1924
+ pub fn test_bench_iter ( ) {
1925
+ fn f ( b : & mut Bencher ) {
1926
+ b. iter ( || {
1927
+ } )
1928
+ }
1929
+ bench:: benchmark ( f) ;
1930
+ }
1883
1931
}
0 commit comments