Skip to content

Commit 4fc15e5

Browse files
committed
Add pmap/pfilter for data parallelism
Add Rayon Add pmap and pfilter native functions Add helper functions (abs, even?, odd?, positive?, negative?)
1 parent a92d103 commit 4fc15e5

File tree

4 files changed

+269
-1
lines changed

4 files changed

+269
-1
lines changed

Cargo.lock

Lines changed: 52 additions & 0 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,12 +4,12 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
# Cranelift JIT compilation
87
cranelift = "0.116"
98
cranelift-jit = "0.116"
109
cranelift-module = "0.116"
1110
cranelift-native = "0.116"
1211
cranelift-codegen = "0.116"
12+
rayon = "1.10"
1313

1414
[dev-dependencies]
1515

lisp/test_parallel.lisp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
(println "Testing parallel map and filter...")
2+
3+
;; Note: pmap and pfilter currently only support native functions
4+
;; We'll use built-in functions like abs, even?, odd?, etc.
5+
6+
;; Test 1: pmap with abs
7+
(println "\n=== Test 1: pmap with abs ===")
8+
(def nums (list -5 3 -2 8 -1 10))
9+
(println "Input:" nums)
10+
(def absolutes (pmap abs nums))
11+
(println "Absolute values:" absolutes)
12+
13+
;; Test 2: pfilter - even numbers
14+
(println "\n=== Test 2: pfilter - even numbers ===")
15+
(def nums2 (list 1 2 3 4 5 6 7 8 9 10))
16+
(println "Input:" nums2)
17+
(def evens (pfilter even? nums2))
18+
(println "Evens:" evens)
19+
20+
;; Test 3: pfilter - odd numbers
21+
(println "\n=== Test 3: pfilter - odd numbers ===")
22+
(def nums3 (list 1 2 3 4 5 6 7 8 9 10))
23+
(println "Input:" nums3)
24+
(def odds (pfilter odd? nums3))
25+
(println "Odds:" odds)
26+
27+
;; Test 4: pfilter - positive numbers
28+
(println "\n=== Test 4: pfilter - positive numbers ===")
29+
(def mixed (list -5 -2 0 1 3 -1 7 10))
30+
(println "Input:" mixed)
31+
(def positives (pfilter positive? mixed))
32+
(println "Positives:" positives)
33+
34+
;; Test 5: pfilter - negative numbers
35+
(println "\n=== Test 5: pfilter - negative numbers ===")
36+
(println "Input:" mixed)
37+
(def negatives (pfilter negative? mixed))
38+
(println "Negatives:" negatives)
39+
40+
;; Test 6: Combine pmap and pfilter
41+
(println "\n=== Test 6: Combined - filter evens then abs ===")
42+
(def nums4 (list -8 -6 -4 -2 0 1 2 3 4 5))
43+
(println "Input:" nums4)
44+
(def evens4 (pfilter even? nums4))
45+
(println "After pfilter (evens):" evens4)
46+
(def absolutes4 (pmap abs evens4))
47+
(println "After pmap (abs):" absolutes4)
48+
49+
;; Test 7: Large list performance test
50+
(println "\n=== Test 7: Performance test (100 elements) ===")
51+
;; Create a list of 100 numbers (mix of positive and negative)
52+
(def large-list (list -50 -49 -48 -47 -46 -45 -44 -43 -42 -41 -40 -39 -38 -37 -36 -35 -34 -33 -32 -31 -30 -29 -28 -27 -26 -25 -24 -23 -22 -21 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50))
53+
(println "Processing 100 elements with pmap abs...")
54+
(def large-abs (pmap abs large-list))
55+
(println "First 3 results:" (car large-abs) (car (cdr large-abs)) (car (cdr (cdr large-abs))))
56+
(println "Length of result:" (length large-abs))
57+
58+
(println "\n=== Test 8: pfilter on large list ===")
59+
(println "Filtering 100 elements for evens...")
60+
(def large-evens (pfilter even? large-list))
61+
(println "Number of evens:" (length large-evens))
62+
(println "First 3 evens:" (car large-evens) (car (cdr large-evens)) (car (cdr (cdr large-evens))))
63+
64+
(println "\n=== Test 9: pfilter then pmap on large list ===")
65+
(def large-positives (pfilter positive? large-list))
66+
(println "Number of positives:" (length large-positives))
67+
(def large-positives-abs (pmap abs large-positives))
68+
(println "After pmap abs, first 3:" (car large-positives-abs) (car (cdr large-positives-abs)) (car (cdr (cdr large-positives-abs))))
69+
70+
(println "\n=== All parallel tests passed! ===")

src/vm.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,58 @@ pub fn standard_vm() -> VM {
15161516
Ok(Value::bool(!args[0].is_truthy()))
15171517
}));
15181518

1519+
// Math helper functions
1520+
vm.define_global("abs", native("abs", |args| {
1521+
if args.len() != 1 { return Err("abs expects 1 argument".to_string()); }
1522+
if let Some(i) = args[0].as_int() {
1523+
Ok(Value::int(i.abs()))
1524+
} else if let Some(f) = args[0].as_float() {
1525+
Ok(Value::float(f.abs()))
1526+
} else {
1527+
Err("abs expects a number".to_string())
1528+
}
1529+
}));
1530+
1531+
vm.define_global("even?", native("even?", |args| {
1532+
if args.len() != 1 { return Err("even? expects 1 argument".to_string()); }
1533+
if let Some(i) = args[0].as_int() {
1534+
Ok(Value::bool(i % 2 == 0))
1535+
} else {
1536+
Err("even? expects an integer".to_string())
1537+
}
1538+
}));
1539+
1540+
vm.define_global("odd?", native("odd?", |args| {
1541+
if args.len() != 1 { return Err("odd? expects 1 argument".to_string()); }
1542+
if let Some(i) = args[0].as_int() {
1543+
Ok(Value::bool(i % 2 != 0))
1544+
} else {
1545+
Err("odd? expects an integer".to_string())
1546+
}
1547+
}));
1548+
1549+
vm.define_global("positive?", native("positive?", |args| {
1550+
if args.len() != 1 { return Err("positive? expects 1 argument".to_string()); }
1551+
if let Some(i) = args[0].as_int() {
1552+
Ok(Value::bool(i > 0))
1553+
} else if let Some(f) = args[0].as_float() {
1554+
Ok(Value::bool(f > 0.0))
1555+
} else {
1556+
Err("positive? expects a number".to_string())
1557+
}
1558+
}));
1559+
1560+
vm.define_global("negative?", native("negative?", |args| {
1561+
if args.len() != 1 { return Err("negative? expects 1 argument".to_string()); }
1562+
if let Some(i) = args[0].as_int() {
1563+
Ok(Value::bool(i < 0))
1564+
} else if let Some(f) = args[0].as_float() {
1565+
Ok(Value::bool(f < 0.0))
1566+
} else {
1567+
Err("negative? expects a number".to_string())
1568+
}
1569+
}));
1570+
15191571
// List operations
15201572
vm.define_global("list", native("list", |args| Ok(Value::list(args.to_vec()))));
15211573

@@ -2242,6 +2294,100 @@ pub fn standard_vm() -> VM {
22422294
}
22432295
}));
22442296

2297+
vm.define_global("pmap", native("pmap", |args| {
2298+
use rayon::prelude::*;
2299+
2300+
if args.len() != 2 {
2301+
return Err("pmap expects 2 arguments (function list)".to_string());
2302+
}
2303+
2304+
// Get the function
2305+
let func = &args[0];
2306+
2307+
// Get the list
2308+
let items = args[1].as_list()
2309+
.ok_or_else(|| "pmap expects a list as second argument".to_string())?;
2310+
2311+
// Check if function is a native function
2312+
let native_func = func.as_native_function()
2313+
.ok_or_else(|| "pmap currently only supports native functions".to_string())?;
2314+
2315+
// Convert items to SharedValue for thread-safe processing
2316+
let shared_items: Result<Vec<_>, String> = items.iter()
2317+
.map(|v| v.make_shared())
2318+
.collect();
2319+
let shared_items = shared_items?;
2320+
2321+
// Process in parallel using Rayon
2322+
let results: Result<Vec<_>, String> = shared_items
2323+
.par_iter()
2324+
.map(|shared_val| {
2325+
// Convert back to Rc-based Value for function call
2326+
let val = Value::from_shared(shared_val);
2327+
2328+
// Apply function
2329+
(native_func.func)(&[val])
2330+
})
2331+
.collect();
2332+
2333+
let results = results?;
2334+
2335+
// Return as list
2336+
Ok(Value::list(results))
2337+
}));
2338+
2339+
vm.define_global("pfilter", native("pfilter", |args| {
2340+
use rayon::prelude::*;
2341+
2342+
if args.len() != 2 {
2343+
return Err("pfilter expects 2 arguments (predicate list)".to_string());
2344+
}
2345+
2346+
// Get the predicate function
2347+
let pred = &args[0];
2348+
2349+
// Get the list
2350+
let items = args[1].as_list()
2351+
.ok_or_else(|| "pfilter expects a list as second argument".to_string())?;
2352+
2353+
// Check if predicate is a native function
2354+
let native_pred = pred.as_native_function()
2355+
.ok_or_else(|| "pfilter currently only supports native functions".to_string())?;
2356+
2357+
// Convert items to SharedValue for thread-safe processing
2358+
let shared_items: Result<Vec<_>, String> = items.iter()
2359+
.map(|v| v.make_shared())
2360+
.collect();
2361+
let shared_items = shared_items?;
2362+
2363+
// Filter in parallel using Rayon
2364+
let results: Result<Vec<_>, String> = shared_items
2365+
.par_iter()
2366+
.filter_map(|shared_val| {
2367+
// Convert back to Rc-based Value for function call
2368+
let val = Value::from_shared(shared_val);
2369+
2370+
// Apply predicate
2371+
match (native_pred.func)(&[val.clone()]) {
2372+
Ok(result) => {
2373+
// Check if result is truthy (not nil and not false)
2374+
if result.is_nil() || (result.as_bool() == Some(false)) {
2375+
None
2376+
} else {
2377+
Some(Ok(val))
2378+
}
2379+
}
2380+
Err(e) => Some(Err(e)),
2381+
}
2382+
})
2383+
.collect();
2384+
2385+
let results = results?;
2386+
2387+
// Return as list
2388+
Ok(Value::list(results))
2389+
}));
2390+
22452391
vm
22462392
}
22472393

0 commit comments

Comments
 (0)