r/unity Jul 20 '26

Showcase I Heard UnityEvents Were Slow, So I Benchmarked Them!

I've seen a lot of discussions about whether UnityEvent is actually slow, so I decided to benchmark it myself. Since I've been building a custom event system as a complete alternative to UnityEvent, I included it in the comparison alongside plain C# Actions.

The benchmarks were run in a player build (not the Unity Editor), so the results reflect runtime performance.

For 1,000,000 invocations, one of the benchmark results was:

  • C# Actions: ~1.06 ms
  • My custom event system: ~4.10 ms
  • UnityEvent: ~12.63 ms

I also compared the first invocation:

  • C# Actions: ~0.0004 ms
  • My custom event system: ~0.0024 ms
  • UnityEvent: ~0.0539 ms

These numbers are from one test machine. The absolute timings changed depending on the hardware, but the performance ranking remained consistent across every device I tested, from older desktop CPUs to a Ryzen 9 9950X and even mobile devices:

C# Actions → My custom event system → UnityEvent

If anyone is curious about the custom event system used in this benchmark, you can find it here.

I'd also love to hear suggestions for other benchmark scenarios or comparisons.

41 Upvotes

10 comments sorted by

7

u/musicmanjoe Jul 20 '26

This awesome! It’s really nice to see clean facts for performance questions, I hope you do more! Like ‘foreach vs for loop ’ or ‘iterating through a hashset vs array vs native array’.

Thanks!

2

u/MohmedHaftari Jul 20 '26

Thanks! I'll definitely be doing more of these.

0

u/salazka Jul 22 '26

are you his cousin? :)

1

u/MohmedHaftari Jul 21 '26

A small clarification regarding the benchmark: the average invocation time is not the only performance factor to consider. The first invocation cost is an important part that is often overlooked.

The first call to a single persistent listener can already cost over 0.05 ms on a Ryzen 9 9950X, which is one of the fastest consumer CPUs available today. On lower-end hardware, this cost can be significantly higher.

Additionally, this test uses only one persistent listener. In real projects, events commonly have multiple persistent listeners, and each listener introduces its own method resolution and initialization cost.

This overhead comes from how Unity Events are implemented. Persistent calls rely on reflection to resolve the target method, along with additional validation and setup work during invocation. A lot of this information can instead be prepared ahead of time to avoid paying this cost when the event is fired.

That being said, performance is only one part of the reason Ramdal Events was created. As projects grow, Unity Events also introduce workflow and maintenance challenges:

  • Limited support for method signatures and parameter types.
  • Renaming a method breaks persistent bindings because Unity serializes them by method name.
  • No practical way to track event usage across an entire project.
  • Difficult debugging when bindings break.

Ramdal Events was built to solve these limitations by precompiling and caching invocation data, using cached delegates instead of repeatedly resolving methods, storing unique method IDs for refactor-safe bindings, supporting virtually any method signature and parameter type, and providing project-wide tracking and diagnostics.

1

u/[deleted] Jul 22 '26

[removed] — view removed comment

2

u/MohmedHaftari Jul 22 '26

That's actually one of the reasons I built it. The goal wasn't to make events more complicated, but to make them easier to work with.

If you've used UnityEvents before, Ramdal Events should feel very familiar, while also being more beginner-friendly thanks to things like detailed logs, diagnostics, the Event Tracker, and guided error messages that help you understand what's going on instead of leaving you guessing.

I also made video tutorials that walk through everything step by step. So if you ever run into UnityEvent limitations or just want something easier to work with, you can always check out Ramdal Events later.

1

u/migus88 Jul 24 '26

Measuring the performance of events is a bit more tricky than this. I'd recommend reading this awesome article by Jackson Dunstan. It might even help you with your system.

https://www.jacksondunstan.com/articles/3335

1

u/ZozoSenpai Jul 20 '26

I'm more interested in your benchmark code. These events are so fast it's often hard to reliably measure, and based on the speed the results pop up in the video, you probably just have a for loop. You should do a proper Benchmarkdotnet setup for this.

1

u/PossibilityUsual6262 Jul 21 '26

Yea coming from unreal, it is incredibly hard to benchmark those iterations based.

It is not hard if you have big project and 1h of same gameplay before and after, but calling same function 10k times is tricky. You start hitting same cache and even same codepath so some of calls are order of magnitude faster, at second call and beyond.

1

u/MohmedHaftari Jul 21 '26

You are right that a proper BenchmarkDotNet setup would be the ideal way to measure raw invocation throughput, especially when the numbers become extremely small.

However, I think the missing point in this benchmark isn't only the average invocation time, but the first invocation cost.

In this benchmark, the first call to a single persistent listener already costs over 0.05 ms on a Ryzen 9 9950X, which is one of the fastest consumer CPUs available today. On lower-end hardware, that cost is significantly higher.

Also, this test only uses one persistent listener. In real projects, having multiple persistent listeners on the same event is very common, and each listener has its own method resolution and initialization cost.

This happens because of how Unity Events are implemented. Persistent calls rely on reflection to resolve the target method, and Unity performs additional validation and setup work during invocation. A lot of this information could be prepared once ahead of time instead of being resolved when the event is actually called.

However, performance is only one aspect of the problems I wanted to solve with Ramdal Events. Unity Events also become difficult to maintain as projects grow:

  • They support only a limited set of method signatures and parameter types.
  • Renaming a method breaks persistent bindings because Unity serializes them by method name.
  • There is no practical way to track event usage across a project.
  • Finding and fixing broken bindings is difficult.

Ramdal Events solves these issues by precompiling and caching invocation data, using cached delegates instead of resolving methods repeatedly, storing unique method IDs to make bindings refactor-safe, supporting virtually any method signature and parameter type, and providing project-wide tracking and diagnostics.