233 lines
4.7 KiB
Plaintext
233 lines
4.7 KiB
Plaintext
// actor_patterns.cm — Actor concurrency benchmarks
|
|
// Message passing, fan-out/fan-in, mailbox throughput.
|
|
// These use structured benchmarks with setup/run/teardown.
|
|
|
|
// Note: actor benchmarks are measured differently from pure compute.
|
|
// Each iteration sends messages and waits for results, so they're
|
|
// inherently slower but test real concurrency costs.
|
|
|
|
// Simple ping-pong: two actors sending messages back and forth
|
|
// Since we can't create real actors from a module, we simulate
|
|
// the message-passing patterns with function call overhead that
|
|
// mirrors what the actor dispatch does.
|
|
|
|
// Simulate message dispatch overhead
|
|
function make_mailbox() {
|
|
return {
|
|
queue: [],
|
|
delivered: 0
|
|
}
|
|
}
|
|
|
|
function send(mailbox, msg) {
|
|
push(mailbox.queue, msg)
|
|
return null
|
|
}
|
|
|
|
function receive(mailbox) {
|
|
if (length(mailbox.queue) == 0) return null
|
|
mailbox.delivered++
|
|
return mailbox.queue[]
|
|
}
|
|
|
|
function drain(mailbox) {
|
|
var count = 0
|
|
while (length(mailbox.queue) > 0) {
|
|
mailbox.queue[]
|
|
count++
|
|
}
|
|
return count
|
|
}
|
|
|
|
// Ping-pong: simulate two actors exchanging messages
|
|
function ping_pong(rounds) {
|
|
var box_a = make_mailbox()
|
|
var box_b = make_mailbox()
|
|
var i = 0
|
|
var msg = null
|
|
|
|
send(box_a, {type: "ping", val: 0})
|
|
|
|
for (i = 0; i < rounds; i++) {
|
|
// A receives and sends to B
|
|
msg = receive(box_a)
|
|
if (msg) {
|
|
send(box_b, {type: "pong", val: msg.val + 1})
|
|
}
|
|
// B receives and sends to A
|
|
msg = receive(box_b)
|
|
if (msg) {
|
|
send(box_a, {type: "ping", val: msg.val + 1})
|
|
}
|
|
}
|
|
|
|
return box_a.delivered + box_b.delivered
|
|
}
|
|
|
|
// Fan-out: one sender, N receivers
|
|
function fan_out(n_receivers, messages_per) {
|
|
var receivers = []
|
|
var i = 0
|
|
var j = 0
|
|
for (i = 0; i < n_receivers; i++) {
|
|
push(receivers, make_mailbox())
|
|
}
|
|
|
|
// Send messages to all receivers
|
|
for (j = 0; j < messages_per; j++) {
|
|
for (i = 0; i < n_receivers; i++) {
|
|
send(receivers[i], {seq: j, data: j * 17})
|
|
}
|
|
}
|
|
|
|
// All receivers drain
|
|
var total = 0
|
|
for (i = 0; i < n_receivers; i++) {
|
|
total += drain(receivers[i])
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
// Fan-in: N senders, one receiver
|
|
function fan_in(n_senders, messages_per) {
|
|
var inbox = make_mailbox()
|
|
var i = 0
|
|
var j = 0
|
|
|
|
// Each sender sends messages
|
|
for (i = 0; i < n_senders; i++) {
|
|
for (j = 0; j < messages_per; j++) {
|
|
send(inbox, {sender: i, seq: j, data: i * 100 + j})
|
|
}
|
|
}
|
|
|
|
// Receiver processes all
|
|
var total = 0
|
|
var msg = null
|
|
msg = receive(inbox)
|
|
while (msg) {
|
|
total += msg.data
|
|
msg = receive(inbox)
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
// Pipeline: chain of processors
|
|
function pipeline(stages, items) {
|
|
var boxes = []
|
|
var i = 0
|
|
var j = 0
|
|
var msg = null
|
|
|
|
for (i = 0; i <= stages; i++) {
|
|
push(boxes, make_mailbox())
|
|
}
|
|
|
|
// Feed input
|
|
for (i = 0; i < items; i++) {
|
|
send(boxes[0], {val: i})
|
|
}
|
|
|
|
// Process each stage
|
|
for (j = 0; j < stages; j++) {
|
|
msg = receive(boxes[j])
|
|
while (msg) {
|
|
send(boxes[j + 1], {val: msg.val * 2 + 1})
|
|
msg = receive(boxes[j])
|
|
}
|
|
}
|
|
|
|
// Drain output
|
|
var total = 0
|
|
msg = receive(boxes[stages])
|
|
while (msg) {
|
|
total += msg.val
|
|
msg = receive(boxes[stages])
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
// Request-response pattern (simulate RPC)
|
|
function request_response(n_requests) {
|
|
var client_box = make_mailbox()
|
|
var server_box = make_mailbox()
|
|
var i = 0
|
|
var req = null
|
|
var resp = null
|
|
var total = 0
|
|
|
|
for (i = 0; i < n_requests; i++) {
|
|
// Client sends request
|
|
send(server_box, {id: i, payload: i * 3, reply_to: client_box})
|
|
|
|
// Server processes
|
|
req = receive(server_box)
|
|
if (req) {
|
|
send(req.reply_to, {id: req.id, result: req.payload * 2 + 1})
|
|
}
|
|
|
|
// Client receives response
|
|
resp = receive(client_box)
|
|
if (resp) {
|
|
total += resp.result
|
|
}
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
return {
|
|
// Ping-pong: 10K rounds
|
|
ping_pong_10k: function(n) {
|
|
var i = 0
|
|
var x = 0
|
|
for (i = 0; i < n; i++) {
|
|
x += ping_pong(10000)
|
|
}
|
|
return x
|
|
},
|
|
|
|
// Fan-out: 100 receivers, 100 messages each
|
|
fan_out_100x100: function(n) {
|
|
var i = 0
|
|
var x = 0
|
|
for (i = 0; i < n; i++) {
|
|
x += fan_out(100, 100)
|
|
}
|
|
return x
|
|
},
|
|
|
|
// Fan-in: 100 senders, 100 messages each
|
|
fan_in_100x100: function(n) {
|
|
var i = 0
|
|
var x = 0
|
|
for (i = 0; i < n; i++) {
|
|
x += fan_in(100, 100)
|
|
}
|
|
return x
|
|
},
|
|
|
|
// Pipeline: 10 stages, 1000 items
|
|
pipeline_10x1k: function(n) {
|
|
var i = 0
|
|
var x = 0
|
|
for (i = 0; i < n; i++) {
|
|
x += pipeline(10, 1000)
|
|
}
|
|
return x
|
|
},
|
|
|
|
// Request-response: 5K requests
|
|
rpc_5k: function(n) {
|
|
var i = 0
|
|
var x = 0
|
|
for (i = 0; i < n; i++) {
|
|
x += request_response(5000)
|
|
}
|
|
return x
|
|
}
|
|
}
|