Skip to content

Commit 6013229

Browse files
committed
Add more challenges
1 parent bb8dca1 commit 6013229

File tree

12 files changed

+1441
-7
lines changed

12 files changed

+1441
-7
lines changed

Cargo.lock

Lines changed: 823 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ version = "0.1.0"
44
edition = "2021"
55

66
[workspace]
7-
members = [ "challenges/simple-parser" , "challenges/wc-command"]
7+
members = [ "challenges/branch-prediction", "challenges/simple-cas", "challenges/simple-parser" , "challenges/wc-command"]
88

99
[dependencies]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "branch-prediction"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
rust-coding-challenges = { path = "../../" }
8+
rand = "0.8"
9+
rand_chacha = "0.3"
10+
11+
# Optional: for more detailed benchmarking
12+
criterion = { version = "0.5", optional = true }
13+
14+
[dev-dependencies]
15+
criterion = "0.5"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# branch-prediction
2+
3+
## Given By
4+
5+
## Topics
6+
7+
## Main Functions
8+
9+
## Example Output
10+
11+
## Usage Example
12+

challenges/branch-prediction/src/lib.rs

Whitespace-only changes.
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// #[cfg(test)]
2+
// mod tests {
3+
// use super::*;
4+
// use branch-prediction::solve;
5+
6+
// #[test]
7+
// fn test_solve() {
8+
// // Call the solve function and check for expected results
9+
// let result = solve();
10+
// assert!(result.is_ok());
11+
// // Add more assertions based on expected behavior
12+
// }
13+
// }

challenges/simple-cas/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "simple-cas"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
bincode = "1.3.3"
8+
hex = "0.4.3"
9+
rust-coding-challenges = { path = "../../" }
10+
serde = { version = "1.0.219", features = ["derive"] }
11+
sha2 = "0.10.9"
12+
tempfile = "3.20.0"
13+
tokio = { version = "1.46.1", features = ["fs", "macros", "rt-multi-thread"] }

challenges/simple-cas/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# simple-cas
2+
3+
## Given By namvdo
4+
5+
## Topics
6+
7+
## Main Functions
8+
9+
## Example Output
10+
11+
## Usage Example
12+

0 commit comments

Comments
 (0)