Skip to content

Simple bus implementation#64

Open
ziollek wants to merge 2 commits intoasaskevich:masterfrom
ziollek:simple-bus
Open

Simple bus implementation#64
ziollek wants to merge 2 commits intoasaskevich:masterfrom
ziollek:simple-bus

Conversation

@ziollek
Copy link

@ziollek ziollek commented Feb 28, 2025

Introduction

I've prepared a simplified implementation of an event bus that exposes interfaces very similar to those of the original Bus. This eliminates the need to use reflection in Publish.

Why?

The main goal was to provide a more efficient solution that does not rely entirely on reflection. However, this approach comes with limitations related to how generic types are handled in Go. Specifically, it allows subscribing with callbacks that accept only a single parameter of the type associated with a particular instance of SimpleBus.

How to use it

func main() {
	bus := EventBus.NewSimpleBus[string]()
	ref := bus.Subscribe("topic", func(arg string) {
		fmt.Printf("Hello %s!\n", arg)
	})
	bus.Publish("topic", "world")
        bus.Unsubscribe("topic", ref)
}

Key differences:

  • Subscription methods do not return errors since type safety is ensured by Go, they return SubscriptionRef that allow Unsubscribe without engaging reflection
  • When calling Publish, there is no lock held for the entire duration of executing listener callbacks.
if subscribers, ok := bus.fetchSubscribers(topic); ok {
	fire := make(chan *genericHandler[T], len(*subscribers))
	bus.Lock()
	for _, subscriber := range *subscribers {
		fire <- subscriber
		if subscriber.once {
			bus.handlers[topic].delete(subscriber)
		}
	}
	bus.Unlock()
	close(fire)
	// calling the callbacks will not block the whole bus
	for subscriber := range fire {
		subscriber.Call(arg, &bus.wg)
	}
}

Performance analysis

To simplify performance comparisons, benchmarks have been added to the test files. Below are the results from my workstation:

Benchmark using a single core:

% GOMAXPROCS=1 go test -bench=.
2
goos: darwin
goarch: amd64
pkg: github.com/ziollek/EventBus
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkSynchronousPublishing        	 2877297	       406.9 ns/op
BenchmarkAsynchronousPublishing       	  548457	      2101 ns/op
BenchmarkSimpleSynchronousPublishing  	 3939492	       305.5 ns/op
BenchmarkSimpleAsynchronousPublishing 	 1000000	      1141 ns/op

Benchmark using a two cores:

GOMAXPROCS=2 go test -bench=.
2
goos: darwin
goarch: amd64
pkg: github.com/ziollek/EventBus
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkSynchronousPublishing-2          	 3146671	       379.3 ns/op
BenchmarkAsynchronousPublishing-2         	 1050337	      1148 ns/op
BenchmarkSimpleSynchronousPublishing-2    	 5079916	       246.0 ns/op
BenchmarkSimpleAsynchronousPublishing-2   	 2124786	       548.2 ns/op

Signed-off-by: tomasz.ziolkowski <tomasz.ziolkowski@allegro.pl>
@ziollek ziollek force-pushed the simple-bus branch 3 times, most recently from b67d772 to 9ecadbd Compare March 1, 2025 15:52
Signed-off-by: tomasz.ziolkowski <tomasz.ziolkowski@allegro.pl>
@asaskevich
Copy link
Owner

Hi, @ziollek! Thank you! I'll take a look into it and merge once I'm done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants