simgo icon indicating copy to clipboard operation
simgo copied to clipboard

Simulation speed issue

Open JO-WTF opened this issue 1 year ago • 3 comments

I created the bank renege example model respectively with simpy and simgo.

What suprised me is that the run time of both models are quite similar, Python even faster for 10%.

I never used Go before, so wondering what the cause may be. Do you have any idea?

simpy code:

import time
import random

import simpy

RANDOM_SEED = 42
N_COUNTERS = 50
N_CUSTOMERS = 1000000  # Total number of customers
MEAN_ARRIVAL_INTERVAL = 10.0  # Generate new customers roughly every x seconds
PATIENCE = 16
MEAN_TIME_IN_BANK = 12.0


def source(env, number, interval, counter):
    """Source generates customers randomly"""
    for i in range(number):
        c = customer(env, f"Customer{i:02d}", counter, time_in_bank=MEAN_TIME_IN_BANK)
        env.process(c)
        t = random.expovariate(1.0 / interval)
        yield env.timeout(t)


def customer(env, name, counter, time_in_bank):
    """Customer arrives, is served and leaves."""

    with counter.request() as req:
        # Wait for the counter or abort at the end of our tether
        results = yield req | env.timeout(PATIENCE)

        if req in results:
            # We got to the counter
            tib = random.expovariate(1.0 / time_in_bank)
            yield env.timeout(tib)
        # else:
        # We reneged


# Setup and start the simulation
# print("Bank renege")
random.seed(RANDOM_SEED)
env = simpy.Environment()

# Start processes and run
counter = simpy.Resource(env, capacity=N_COUNTERS)
env.process(source(env, N_CUSTOMERS, MEAN_ARRIVAL_INTERVAL, counter))


start_time = time.time()
env.run()
end_time = time.time()
print("Elapsed time:", end_time - start_time)
print(env.now)

Python 3.12.1 output:

Elapsed time: 16.356430292129517

simgo code:

package main

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/fschuetz04/simgo"
)

const (
	RandomSeed          = 42
	NCounters           = 50
	NCustomers          = 1000000
	MeanArrivalInterval = 10
	MaxWaitTime         = 16
	MeanTimeInBank      = 12
)

func customerSource(proc simgo.Process) {
	counters := NewResource(proc, NCounters)

	for id := 1; id <= NCustomers; id++ {
		proc.ProcessReflect(customer, id, counters)
		delay := rand.ExpFloat64() * MeanArrivalInterval
		proc.Wait(proc.Timeout(delay))
	}
}

func customer(proc simgo.Process, id int, counters *Resource) {

	request := counters.Request()
	timeout := proc.Timeout(MaxWaitTime)
	proc.Wait(proc.AnyOf(request, timeout))

	if !request.Triggered() {
		request.Abort()
		return
	}

	delay := rand.ExpFloat64() * MeanTimeInBank
	proc.Wait(proc.Timeout(delay))

	counters.Release()
}

func timeCost() func() {
	start := time.Now()
	return func() {
		tc := time.Since(start)
		fmt.Printf("time cost = %v\n", tc)
	}
}

func runSim(sim *simgo.Simulation) {
	defer timeCost()()
	sim.Run()
	fmt.Printf("%.f\n", sim.Now())
}

func main() {
	rand.Seed(RandomSeed)

	sim := simgo.Simulation{}

	sim.Process(customerSource)

	runSim(&sim)
}

Golang go version go1.22.2 linux/amd64 output:

time cost = 18.406199488s

JO-WTF avatar Apr 10 '24 02:04 JO-WTF

On my machine, both versions take around 6.5 seconds, but the Python version is indeed a bit faster on average.

Unfortunately, Go doesn't offer Coroutines, so instead I use one Goroutine per process. The Goroutines are synchronized via channels, so effectively only one runs at any given time. You might find https://research.swtch.com/coro interesting. In the case of the bank example, one process per customer is created, so the Go version needs 1 million Goroutines. Creating them and constantly switching between them of course impacts performance quite a bit.

Compared to Go, Python offers Generators, which are used to make SimPy work without creating a thread or something similar per process. Even if Python is much slower than Go in principle, this leads to the similar performance for this example.

If you want better performance right now, you might want to try the C++ version of this library: https://github.com/fschuetz04/simcpp20. On my machine, the same bank example with 1 million customers takes about 0.5 seconds :)

fschuetz04 avatar Apr 12 '24 06:04 fschuetz04

Thanks for your reply.

I tried simcpp20 actually. However, not every simpy functionality is implemented in simcpp20 yet (store, container for example), so it seems there are a lot work to do before it can be applied to a project to replace simpy.

JO-WTF avatar May 25 '24 01:05 JO-WTF