1+ use std:: time:: Instant ;
2+ use rand:: prelude:: * ;
3+
4+ #[ derive( Debug , Clone ) ]
5+ struct Product {
6+ price : f64 ,
7+ in_stock : bool ,
8+ is_premium : bool ,
9+ }
10+
11+ struct EcommerceProcessor {
12+ predictable_products : Vec < Product > ,
13+ random_products : Vec < Product > ,
14+ }
15+
16+ impl EcommerceProcessor {
17+ fn new ( size : usize ) -> Self {
18+ let mut rng = thread_rng ( ) ;
19+
20+ // Create random products
21+ let mut random_products: Vec < Product > = ( 0 ..size)
22+ . map ( |_| Product {
23+ price : rng. gen_range ( 10.0 ..500.0 ) ,
24+ in_stock : rng. gen_bool ( 0.7 ) , // 70% in stock
25+ is_premium : rng. gen_bool ( 0.3 ) , // 30% premium
26+ } )
27+ . collect ( ) ;
28+
29+ // Create predictable version: sort by price (low to high)
30+ let mut predictable_products = random_products. clone ( ) ;
31+ predictable_products. sort_by ( |a, b| a. price . partial_cmp ( & b. price ) . unwrap ( ) ) ;
32+
33+ // Shuffle the random version
34+ random_products. shuffle ( & mut rng) ;
35+
36+ Self {
37+ predictable_products,
38+ random_products,
39+ }
40+ }
41+
42+ // Process products with PREDICTABLE branch patterns
43+ fn process_predictable_data ( & self ) -> f64 {
44+ let mut total_revenue = 0.0 ;
45+
46+ // Since data is sorted by price, branches are very predictable:
47+ // - Early products: price < 100 (mostly true)
48+ // - Middle products: 100 <= price < 300 (mostly true)
49+ // - Late products: price >= 300 (mostly true)
50+ for product in & self . predictable_products {
51+ if product. price < 100.0 { // PREDICTABLE: mostly true early on
52+ if product. in_stock {
53+ total_revenue += product. price ;
54+ if product. is_premium {
55+ total_revenue += 5.0 ; // premium bonus
56+ }
57+ }
58+ } else if product. price < 300.0 { // PREDICTABLE: mostly true in middle
59+ if product. in_stock {
60+ total_revenue += product. price * 1.1 ; // markup
61+ if product. is_premium {
62+ total_revenue += 15.0 ;
63+ }
64+ }
65+ } else { // PREDICTABLE: mostly true at end
66+ if product. in_stock {
67+ total_revenue += product. price * 1.2 ; // higher markup
68+ if product. is_premium {
69+ total_revenue += 25.0 ;
70+ }
71+ }
72+ }
73+ }
74+
75+ total_revenue
76+ }
77+
78+ // Process products with UNPREDICTABLE branch patterns
79+ fn process_unpredictable_data ( & self ) -> f64 {
80+ let mut total_revenue = 0.0 ;
81+
82+ // Same exact logic, but data is shuffled randomly
83+ // Branches are now unpredictable - each condition could be true/false
84+ for product in & self . random_products {
85+ if product. price < 100.0 { // UNPREDICTABLE: random true/false
86+ if product. in_stock {
87+ total_revenue += product. price ;
88+ if product. is_premium {
89+ total_revenue += 5.0 ;
90+ }
91+ }
92+ } else if product. price < 300.0 { // UNPREDICTABLE: random true/false
93+ if product. in_stock {
94+ total_revenue += product. price * 1.1 ;
95+ if product. is_premium {
96+ total_revenue += 15.0 ;
97+ }
98+ }
99+ } else { // UNPREDICTABLE: random true/false
100+ if product. in_stock {
101+ total_revenue += product. price * 1.2 ;
102+ if product. is_premium {
103+ total_revenue += 25.0 ;
104+ }
105+ }
106+ }
107+ }
108+
109+ total_revenue
110+ }
111+ }
112+
113+ fn main ( ) {
114+ const PRODUCT_COUNT : usize = 100_000 ;
115+ const ITERATIONS : usize = 100_000 ;
116+
117+ println ! ( "Branch Prediction Demo: E-commerce Order Processing" ) ;
118+ println ! ( "==================================================" ) ;
119+ println ! ( "Processing {} products, {} times each" , PRODUCT_COUNT , ITERATIONS ) ;
120+ println ! ( ) ;
121+
122+ // Setup
123+ print ! ( "Setting up test data... " ) ;
124+ let processor = EcommerceProcessor :: new ( PRODUCT_COUNT ) ;
125+ println ! ( "Done!" ) ;
126+ println ! ( ) ;
127+
128+ // Test 1: Predictable branches (sorted data)
129+ println ! ( "🎯 Test 1: Predictable Branch Pattern" ) ;
130+ println ! ( " Data: Products sorted by price (low → high)" ) ;
131+ println ! ( " Branch behavior: Price checks are predictable" ) ;
132+ let start = Instant :: now ( ) ;
133+
134+ let predictable_revenue = processor. process_predictable_data ( ) ;
135+ for _ in 0 ..ITERATIONS {
136+ let _ = processor. process_predictable_data ( ) ;
137+ }
138+
139+ let predictable_time = start. elapsed ( ) ;
140+ println ! ( " Revenue: ${:.2}" , predictable_revenue) ;
141+ println ! ( " Result: {:?} total" , predictable_time) ;
142+ println ! ( " Average: {:.2}μs per iteration" ,
143+ predictable_time. as_micros( ) as f64 / ITERATIONS as f64 ) ;
144+ println ! ( ) ;
145+
146+ // Test 2: Unpredictable branches (random data)
147+ println ! ( "🎲 Test 2: Unpredictable Branch Pattern" ) ;
148+ println ! ( " Data: Same products, randomly shuffled" ) ;
149+ println ! ( " Branch behavior: Price checks are unpredictable" ) ;
150+ let start = Instant :: now ( ) ;
151+
152+ let unpredictable_revenue = processor. process_unpredictable_data ( ) ;
153+ for _ in 0 ..ITERATIONS {
154+ let _ = processor. process_unpredictable_data ( ) ;
155+ }
156+
157+ let unpredictable_time = start. elapsed ( ) ;
158+ println ! ( " Revenue: ${:.2}" , unpredictable_revenue) ;
159+ println ! ( " Result: {:?} total" , unpredictable_time) ;
160+ println ! ( " Average: {:.2}μs per iteration" ,
161+ unpredictable_time. as_micros( ) as f64 / ITERATIONS as f64 ) ;
162+ println ! ( ) ;
163+
164+ // Analysis
165+ let predictable_ms = predictable_time. as_nanos ( ) as f64 / 1_000_000.0 ;
166+ let unpredictable_ms = unpredictable_time. as_nanos ( ) as f64 / 1_000_000.0 ;
167+ let slowdown = unpredictable_ms / predictable_ms;
168+ let penalty_percent = ( slowdown - 1.0 ) * 100.0 ;
169+
170+ println ! ( "📊 Performance Analysis" ) ;
171+ println ! ( "=======================" ) ;
172+ println ! ( "Predictable data: {:.1}ms" , predictable_ms) ;
173+ println ! ( "Unpredictable data: {:.1}ms" , unpredictable_ms) ;
174+ println ! ( "Slowdown factor: {:.2}x" , slowdown) ;
175+ println ! ( "Performance penalty: {:.1}%" , penalty_percent) ;
176+ println ! ( ) ;
177+
178+ println ! ( "💡 What This Demonstrates" ) ;
179+ println ! ( "=========================" ) ;
180+ if penalty_percent > 5.0 {
181+ println ! ( "✓ Clear branch misprediction penalty observed!" ) ;
182+ println ! ( " The CPU's branch predictor struggles with random data" ) ;
183+ println ! ( " Same algorithm + same data = different performance" ) ;
184+ } else {
185+ println ! ( "⚠ Small difference - try increasing PRODUCT_COUNT or ITERATIONS" ) ;
186+ }
187+ println ! ( ) ;
188+
189+ println ! ( "🏪 Real-World E-commerce Impact" ) ;
190+ println ! ( "===============================" ) ;
191+ let orders_per_sec_predictable = ( ITERATIONS as f64 / predictable_time. as_secs_f64 ( ) ) as u64 ;
192+ let orders_per_sec_unpredictable = ( ITERATIONS as f64 / unpredictable_time. as_secs_f64 ( ) ) as u64 ;
193+
194+ println ! ( "With predictable customer data: {} orders/second" , orders_per_sec_predictable) ;
195+ println ! ( "With unpredictable customer data: {} orders/second" , orders_per_sec_unpredictable) ;
196+
197+ if orders_per_sec_predictable > orders_per_sec_unpredictable {
198+ let lost_capacity = orders_per_sec_predictable - orders_per_sec_unpredictable;
199+ println ! ( "Lost processing capacity: {} orders/second" , lost_capacity) ;
200+ println ! ( ) ;
201+ println ! ( "💰 Business Impact:" ) ;
202+ println ! ( " If each order = $50 average:" ) ;
203+ println ! ( " Lost revenue potential = ${}/second" , lost_capacity * 50 ) ;
204+ println ! ( " That's ${}/hour from branch mispredictions!" , lost_capacity * 50 * 3600 ) ;
205+ }
206+ }
207+
208+ #[ cfg( test) ]
209+ mod tests {
210+ use super :: * ;
211+
212+ #[ test]
213+ fn test_same_results_different_performance ( ) {
214+ let processor = EcommerceProcessor :: new ( 1000 ) ;
215+
216+ // Both methods should produce the same total (same data, different order)
217+ let predictable_result = processor. process_predictable_data ( ) ;
218+ let unpredictable_result = processor. process_unpredictable_data ( ) ;
219+
220+ // Results should be very close (allowing for floating point precision)
221+ assert ! ( ( predictable_result - unpredictable_result) . abs( ) < 0.01 ,
222+ "Results should be the same regardless of data order" ) ;
223+ }
224+
225+ #[ test]
226+ fn test_performance_difference ( ) {
227+ let processor = EcommerceProcessor :: new ( 5000 ) ;
228+ let iterations = 100 ;
229+
230+ let start = Instant :: now ( ) ;
231+ for _ in 0 ..iterations {
232+ let _ = processor. process_predictable_data ( ) ;
233+ }
234+ let predictable_time = start. elapsed ( ) ;
235+
236+ let start = Instant :: now ( ) ;
237+ for _ in 0 ..iterations {
238+ let _ = processor. process_unpredictable_data ( ) ;
239+ }
240+ let unpredictable_time = start. elapsed ( ) ;
241+
242+ println ! ( "Predictable: {:?}" , predictable_time) ;
243+ println ! ( "Unpredictable: {:?}" , unpredictable_time) ;
244+
245+ // We expect some performance difference due to branch prediction
246+ // Even a 5% difference is significant and measurable
247+ let ratio = unpredictable_time. as_nanos ( ) as f64 / predictable_time. as_nanos ( ) as f64 ;
248+ println ! ( "Performance ratio: {:.3}" , ratio) ;
249+
250+ assert ! ( ratio > 1.01 , "Expected measurable performance difference due to branch misprediction" ) ;
251+ }
252+ }
0 commit comments