// Copyright (c) 2008 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using JpLabs.TweakedEvents;
using JpLabs.TweakedEvents.Weak;
namespace TweakedEventsBenchmark
{
class Program
{
public static void Main(string[] args)
{
TypeSafetyProblem();
TestCollectingListener();
TestAttachAnonymousMethod();
Console.WriteLine();
PerformanceTest();
if (Debugger.IsAttached) {
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
Console.WriteLine();
}
}
class EventArgs1 : EventArgs { public float Num = 1; }
class EventArgs2 : EventArgs { public int Num = 0; }
static void TypeSafetyProblem()
{
Console.WriteLine("TypeSafetyProblem");
Console.Write("This should cause an exception: ");
try {
TweakedEvent<EventHandler<EventArgs2>> weakEvent = new WeakEvent<EventHandler<EventArgs2>>();
//weakEvent += ((sender, e) => Console.WriteLine(e.Num.ToString()));
TweakedEvent.Add(ref weakEvent, ((sender, e) => Console.WriteLine(e.Num.ToString())));
// this call is problematic because Raise isn't typesafe
// FastSmartWeakEvent will do a runtime check. It's possible to remove that check to improve
// performance, but that would blow a hole into the .NET type system if anyone calls Raise with
// an EventArgs instance not compatible with the delegate signature.
weakEvent.Raise(null, new EventArgs1());
Console.WriteLine("No exception -> we blew a hole into the .NET type system!");
} catch (InvalidCastException ex) {
Trace.WriteLine(ex);
Console.WriteLine("Got exception as expected!");
}
Console.WriteLine();
}
static void TestCollectingListener()
{
Console.WriteLine("TestCollectingListener");
{
IEventSource source = new SmartEventSource();
EventListener r = new EventListener(source);
r.Attach();
source.RaiseEvent();
GC.KeepAlive(r);
r = null;
GC.Collect();
GC.WaitForPendingFinalizers();
source.RaiseEvent();
}
Console.WriteLine("With fast:");
{
IEventSource source = new FastSmartEventSource();
EventListener r = new EventListener(source);
r.Attach();
source.RaiseEvent();
GC.KeepAlive(r);
r = null;
GC.Collect();
GC.WaitForPendingFinalizers();
source.RaiseEvent();
}
Console.WriteLine();
}
static void TestAttachAnonymousMethod()
{
Console.WriteLine("TestAttachAnonymousMethod");
try {
IEventSource source = new WeakEventSource();
string text = "Hi";
source.Event += delegate {
Console.WriteLine(text);
};
Console.WriteLine("Attaching an anonymous method that captures local variables should result in an exception!");
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
Console.WriteLine();
}
static void PerformanceTest()
{
Console.WriteLine("Invoking Events");
{
Program p = new Program();
NormalEventSource source = new NormalEventSource();
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
SpeedTest(
"Normal (strong) event",
500000,
callCount => {
for (int i = 0; i < callCount; i++) {
source.RaiseEvent();
}
}
);
}
{
Program p = new Program();
IEventSource source = new SmartEventSource();
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
SpeedTest(
"Simple smart weak event",
10000,
callCount => {
for (int i = 0; i < callCount; i++) {
source.RaiseEvent();
}
}
);
}
{
Program p = new Program();
IEventSource source = new FastSmartEventSource();
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
SpeedTest(
"FastSmartWeakEvent",
500000,
callCount => {
for (int i = 0; i < callCount; i++) {
source.RaiseEvent();
}
}
);
}
{
Program p = new Program();
IEventSource source = new WeakEventSource();
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
SpeedTest(
"WeakEvent (by JP)",
1000000,
callCount => {
for (int i = 0; i < callCount; i++) {
source.RaiseEvent();
}
}
);
}
Console.WriteLine();
{
Program p = new Program();
IEventSource source = new FastSmartEventSource();
SpeedTest(
"FastSmartWeakEvent '+='",
5000,
callCount => {
for (int i = 0; i < callCount; i++) {
if (i % 2 == 0) source.Event += StaticOnEvent;
else source.Event += p.OnEvent;
}
}
);
}
{
Program p = new Program();
IEventSource source = new WeakEventSource();
SpeedTest(
"WeakEvent '+='",
5000,
callCount => {
for (int i = 0; i < callCount; i++) {
if (i % 2 == 0) source.Event += StaticOnEvent;
else source.Event += p.OnEvent;
}
}
);
}
{
Program p = new Program();
IEventSource source = new WeakEventSource();
SpeedTest(
"WeakEvent instantiation only",
5000,
callCount => {
for (int i = 0; i < callCount; i++) {
if (i % 2 == 0) new WeakEventEntry<EventHandler>(StaticOnEvent);
else new WeakEventEntry<EventHandler>(p.OnEvent);
}
}
);
}
Console.WriteLine();
{
Program p = new Program();
IEventSource source = new SyncedEventSource();
/*
EventWaitHandle handle = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(
(state) => {
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
handle.Set();
}
);
handle.WaitOne();
handle.Close();
/* /
Thread t = new Thread(
() => {
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
}
);
t.Start();
t.Join();
//*/
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
SpeedTest(
"Synced event",
25000,
callCount => {
for (int i = 0; i < callCount; i++) {
source.RaiseEvent();
}
}
);
}
{
Program p = new Program();
IEventSource source = new WeakSyncedEventSource();
source.Event += StaticOnEvent;
source.Event += p.OnEvent;
SpeedTest(
"Weak & Synced event",
25000,
callCount => {
for (int i = 0; i < callCount; i++) {
source.RaiseEvent();
}
}
);
}
}
static void SpeedTest(string text, int callCount, Action<int> a)
{
Console.Write(text + "...");
Stopwatch w = new Stopwatch();
w.Start();
a(callCount);
w.Stop();
//Console.WriteLine((callCount / w.Elapsed.TotalSeconds).ToString("f0").PadLeft(35 - text.Length) + " calls per second");
double kiloCallsPerSec = (callCount / w.Elapsed.TotalSeconds) / 1000;
Console.WriteLine(kiloCallsPerSec.ToString("f0").PadLeft(35 - text.Length) + " thousand calls per second");
}
static void StaticOnEvent(object sender, EventArgs e)
{
}
void OnEvent(object sender, EventArgs e)
{
}
static void StaticOnSpecialEvent(object sender, AsyncCompletedEventArgs e)
{
}
void OnSpecialEvent(object sender, AsyncCompletedEventArgs e)
{
}
}
}