Skip to content

Commit 1ac2572

Browse files
authored
Merge pull request #1286 from rylev/ctfe-stress-5
Create new ctfe-stress-5 which builds in half the time as ctfe-stress-4
2 parents 6d4f0e2 + 450cd8b commit 1ac2572

File tree

5 files changed

+125
-0
lines changed

5 files changed

+125
-0
lines changed

collector/benchmarks/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ compiler in interesting ways.
6363
caused [poor performance](https://github.com/rust-lang/rust/issues/32278) in
6464
the past.
6565
- **ctfe-stress-4**: A stress test for compile-time function evaluation.
66+
- **ctfe-stress-5**: A stress test for compile-time function evaluation.
6667
- **deeply-nested-multi**: A small program containing multiple examples
6768
([one](https://github.com/rust-lang/rust/issues/38528),
6869
[two](https://github.com/rust-lang/rust/issues/72408),

collector/benchmarks/ctfe-stress-5/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "ctfe-stress-5"
3+
version = "0.1.0"
4+
authors = ["Ralf Jung <[email protected]>"]
5+
6+
[workspace]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"category": "secondary"
3+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#![allow(dead_code)]
2+
#![feature(const_eval_limit)]
3+
#![const_eval_limit = "10000000"]
4+
use std::mem::MaybeUninit;
5+
6+
// Try to make CTFE actually do a lot of computation, without producing a big result.
7+
// The const fn expressions evaluated here take a dummy u32 argument because otherwise
8+
// const fn memoisation is able to eliminate a lot of the work.
9+
// And without support for loops.
10+
11+
macro_rules! const_repeat {
12+
// Base case: Use 16 at the end to avoid function calls at the leafs as much as possible.
13+
([16] $e: expr, $T: ty) => {{
14+
$e; $e; $e; $e;
15+
$e; $e; $e; $e;
16+
$e; $e; $e; $e;
17+
$e; $e; $e; $e
18+
}};
19+
([1] $e: expr, $T: ty) => {{
20+
$e
21+
}};
22+
// Recursive case: Take a 16
23+
([16 $($n: tt)*] $e: expr, $T: ty) => {{
24+
const fn e(_: u32) -> $T { const_repeat!([$($n)*] $e, $T) }
25+
e(0); e(0); e(0); e(0);
26+
e(0); e(0); e(0); e(0);
27+
e(0); e(0); e(0); e(0);
28+
e(0); e(0); e(0); e(0)
29+
}};
30+
// Recursive case: Take a 8
31+
([8 $($n: tt)*] $e: expr, $T: ty) => {{
32+
const fn e(_: u32) -> $T { const_repeat!([$($n)*] $e, $T) }
33+
e(0); e(0); e(0); e(0);
34+
e(0); e(0); e(0); e(0)
35+
}};
36+
// Recursive case: Take a 4
37+
([4 $($n: tt)*] $e: expr, $T: ty) => {{
38+
const fn e(_: u32) -> $T { const_repeat!([$($n)*] $e, $T) }
39+
e(0); e(0); e(0); e(0)
40+
}};
41+
// Recursive case: Take a 2
42+
([2 $($n: tt)*] $e: expr, $T: ty) => {{
43+
const fn e(_: u32) -> $T { const_repeat!([$($n)*] $e, $T) }
44+
e(0); e(0)
45+
}};
46+
}
47+
macro_rules! expensive_static {
48+
($name: ident : $T: ty = $e : expr; $count: tt) => {
49+
pub static $name: $T = const_repeat!($count $e, $T);
50+
};
51+
}
52+
53+
pub trait Trait: Sync {}
54+
impl Trait for u32 {}
55+
56+
const fn inc(i: i32) -> i32 {
57+
i + 1
58+
}
59+
60+
// The numbers in the brackets are iteration counts. E.g., [4 16 16] means
61+
// 4 * 16 * 16 = 2^(2+4+4) = 2^10 iterations.
62+
expensive_static!(CAST: usize = 42i32 as u8 as u64 as i8 as isize as usize; [4 8 16 16 16]);
63+
expensive_static!(CONST_FN: i32 = inc(42); [8 16 16 16]);
64+
expensive_static!(FIELDS: &'static i32 = &("bar", 42, "foo", 3.14).1; [4 8 16 16 16]);
65+
expensive_static!(FORCE_ALLOC: i32 = *****(&&&&&5); [4 8 16 16 16]);
66+
expensive_static!(CHECKED_INDEX: u8 = b"foomp"[3]; [4 8 16 16 16]);
67+
expensive_static!(OPS: i32 = ((((10 >> 1) + 3) * 7) / 2 - 12) << 4; [4 8 16 16 16]);
68+
expensive_static!(RELOCATIONS : &'static str = "hello"; [4 8 16 16 16]);
69+
expensive_static!(UNSIZE_SLICE: &'static [u8] = b"foo"; [4 8 16 16 16 16]);
70+
expensive_static!(UNSIZE_TRAIT: &'static dyn Trait = &42u32; [4 8 16 16 16 16]);
71+
72+
// copying all these zeros and the corresponding definedness bits can be expensive and is probably
73+
// prone to regressions.
74+
// 24 is an exponent that makes the repeat expression take less than two seconds to compute
75+
const FOO: [i32; 1 << 24] = [0; 1 << 24];
76+
77+
// Try CTFE that operate on values that contain largely uninitialized memory, not requiring any
78+
// particular representation in MIR.
79+
type LargeUninit = MaybeUninit<[u8; 1 << 23]>;
80+
81+
// copying uninitialized bytes could also be expensive and could be optimized independently, so
82+
// track regressions here separately. It should also be less costly to compose new values
83+
// containing largly undef bytes.
84+
const BAR: LargeUninit = MaybeUninit::uninit();
85+
86+
// Check the evaluation time of passing through a function.
87+
const fn id<T>(val: T) -> T {
88+
val
89+
}
90+
const ID: LargeUninit = id(MaybeUninit::uninit());
91+
92+
const fn build() -> LargeUninit {
93+
MaybeUninit::uninit()
94+
}
95+
const BUILD: LargeUninit = build();
96+
97+
// Largely uninitialized memory but initialized with tag at the start, in both cases.
98+
const NONE: Option<LargeUninit> = None;
99+
const SOME: Option<LargeUninit> = Some(MaybeUninit::uninit());
100+
101+
// A large uninit surrounded by initialized bytes whose representation is surely computed.
102+
const SURROUND: (u8, LargeUninit, u8) = (0, MaybeUninit::uninit(), 0);
103+
const SURROUND_ID: (u8, LargeUninit, u8) = id((0, MaybeUninit::uninit(), 0));
104+
105+
// Check actual codegen for these values.
106+
pub static STATIC_BAR: LargeUninit = MaybeUninit::uninit();
107+
pub static STATIC_NONE: Option<LargeUninit> = None;
108+
pub static STATIC_SOME: Option<LargeUninit> = Some(MaybeUninit::uninit());

0 commit comments

Comments
 (0)