-
Notifications
You must be signed in to change notification settings - Fork 1
add DoubleDispatchObject #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
add DoubleDispatchObject #1
Conversation
|
This is awesome! Thanks for submitting it, and I'll be very interested in seeing the results. I'll review it this weekend. Thanks again! |
|
Sure, thank you Will.
Cyril
…On Fri, Apr 19, 2019, 7:11 AM Will Fuqua ***@***.***> wrote:
This is awesome! Thanks for submitting it, and I'll be very interested in
seeing the results. I'll review it this weekend. Thanks again!
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAIBUUTSREITTBI7ZMQKMR3PRGSE7ANCNFSM4HHA7VIQ>
.
|
|
@waf I'm curious too, to see the figures you get on whatever box you happen to run; here, on my snail cheap laptop, it looks like I consistently get runtime perf in the same ballpark as with the dynamic keyword, for shape sample set sizes of 1000, 10000, 100000, and up... I wasn't expecting it to be faster than your boilerplate dictionary dispatch pattern anyway (since my impl itself uses dicos internally plus extra overhead). Keep me posted and thank you for this neat little benchmark repo again. CJ |
* add classic space object collision example unit test
|
Thanks for your work on this! It's a very cool approach. I've run the benchmarks on a desktop machine, and included the results below. It's a little bit slower than the Dynamic Dispatch, but not much (less than 2x). SummaryBenchmarkDotNet=v0.10.5, OS=Windows 10.0.17763
Processor=Intel Core i5-4440 CPU 3.10GHz (Haswell), ProcessorCount=4
Frequency=10000000 Hz, Resolution=100.0000 ns, Timer=UNKNOWN
dotnet cli version=3.0.100-preview3-010431
[Host] : .NET Core 4.6.26614.01, 64bit RyuJIT
DefaultJob : .NET Core 4.6.26614.01, 64bit RyuJIT
DetailsDispatchBenchmark.DictionaryDispatch: DefaultJob DispatchBenchmark.DoubleDispatch: DefaultJob DispatchBenchmark.DynamicDispatch: DefaultJob DispatchBenchmark.IfElsePatternDispatch: DefaultJob DispatchBenchmark.SwitchCasePatternDispatch: DefaultJob DispatchBenchmark.VisitorDispatch: DefaultJob Total time: 00:02:40 (160.33 sec) |
|
Hi Will,
Thank you for confirming, yes, that looks about the same as what I get here/same ratios overall.
I was interested in investigating in the smallest boilerplate code as
possible to support it, and the least intrusive also (inheritance is
optional, simple composition pattern thanks to the EnsureThreadSafe helper
being the preferred way I'd recommend).
Here are the few limitations by design &/or implementation I can think of
and that I haven't documented yet:
No support for static methods (not that it would make much sense anyway).
Currently works on closed types signatures only (one can't leave generic
parameters open at class or method level and rely on double dispatch on
those) - doesn't mean one cannot use generic classes or methods, but those
must be closed somehow, eg, MyMethod<string>(string input) if we want the
double dispatch to be honored.
Haven't tested the contra / covariance compatibility rules on parameters
that are delegate types (that may or may not work, dunno yet).
My only remaining foreseeable item is to generalize it up to 2 or 3
parameters after "this", if that doesn't degrade the perf too much.
Multiple dispatch use cases beyond "this" + 3 parameters are rare,
reportedly.
Thanks again,
Cyril
…On Wed, Apr 24, 2019, 1:26 AM Will Fuqua ***@***.***> wrote:
Thanks for your work on this! It's a very cool approach. I've run the
benchmarks on a desktop machine, and included the results below. It's a
little bit slower than the Dynamic Dispatch, but not much (less than 2x).
Summary
BenchmarkDotNet=v0.10.5, OS=Windows 10.0.17763Processor=Intel Core i5-4440 CPU 3.10GHz (Haswell), ProcessorCount=4Frequency=10000000 Hz, Resolution=100.0000 ns, Timer=UNKNOWN
dotnet cli version=3.0.100-preview3-010431
[Host] : .NET Core 4.6.26614.01, 64bit RyuJIT
DefaultJob : .NET Core 4.6.26614.01, 64bit RyuJIT
Method Mean Error StdDev
DictionaryDispatch 50.78 us 0.6614 us 0.6187 us
DoubleDispatch 167.94 us 3.3153 us 5.3536 us
DynamicDispatch 95.55 us 1.5377 us 1.4384 us
IfElsePatternDispatch 17.16 us 0.3175 us 0.2969 us
SwitchCasePatternDispatch 18.23 us 0.3628 us 0.5648 us
VisitorDispatch 20.28 us 0.4034 us 0.3962 us Details
DispatchBenchmark.DictionaryDispatch: DefaultJob
Runtime = .NET Core 4.6.26614.01, 64bit RyuJIT; GC = Concurrent Workstation
Mean = 50.7788 us, StdErr = 0.1597 us (0.31%); N = 15, StdDev = 0.6187 us
Min = 49.7234 us, Q1 = 50.2453 us, Median = 50.9514 us, Q3 = 51.2449 us,
Max = 51.5330 us
IQR = 0.9996 us, LowerFence = 48.7459 us, UpperFence = 52.7444 us
ConfidenceInterval = [50.1174 us; 51.4402 us] (CI 99.9%), Margin = 0.6614
us (1.30% of Mean)
Skewness = -0.49, Kurtosis = 1.71
DispatchBenchmark.DoubleDispatch: DefaultJob
Runtime = .NET Core 4.6.26614.01, 64bit RyuJIT; GC = Concurrent Workstation
Mean = 167.9357 us, StdErr = 0.9181 us (0.55%); N = 34, StdDev = 5.3536 us
Min = 158.4995 us, Q1 = 163.8331 us, Median = 167.2446 us, Q3 = 171.9586
us, Max = 177.3887 us
IQR = 8.1255 us, LowerFence = 151.6448 us, UpperFence = 184.1469 us
ConfidenceInterval = [164.6204 us; 171.2510 us] (CI 99.9%), Margin =
3.3153 us (1.97% of Mean)
Skewness = 0.24, Kurtosis = 1.98
DispatchBenchmark.DynamicDispatch: DefaultJob
Runtime = .NET Core 4.6.26614.01, 64bit RyuJIT; GC = Concurrent Workstation
Mean = 95.5455 us, StdErr = 0.3714 us (0.39%); N = 15, StdDev = 1.4384 us
Min = 93.0045 us, Q1 = 94.8510 us, Median = 95.5781 us, Q3 = 96.8496 us,
Max = 98.0467 us
IQR = 1.9985 us, LowerFence = 91.8533 us, UpperFence = 99.8473 us
ConfidenceInterval = [94.0078 us; 97.0832 us] (CI 99.9%), Margin = 1.5377
us (1.61% of Mean)
Skewness = -0.14, Kurtosis = 2.05
DispatchBenchmark.IfElsePatternDispatch: DefaultJob
Runtime = .NET Core 4.6.26614.01, 64bit RyuJIT; GC = Concurrent Workstation
Mean = 17.1626 us, StdErr = 0.0767 us (0.45%); N = 15, StdDev = 0.2969 us
Min = 16.7695 us, Q1 = 16.8991 us, Median = 17.1342 us, Q3 = 17.3110 us,
Max = 17.6739 us
IQR = 0.4119 us, LowerFence = 16.2812 us, UpperFence = 17.9288 us
ConfidenceInterval = [16.8452 us; 17.4801 us] (CI 99.9%), Margin = 0.3175
us (1.85% of Mean)
Skewness = 0.49, Kurtosis = 1.92
DispatchBenchmark.SwitchCasePatternDispatch: DefaultJob
Runtime = .NET Core 4.6.26614.01, 64bit RyuJIT; GC = Concurrent Workstation
Mean = 18.2285 us, StdErr = 0.0998 us (0.55%); N = 32, StdDev = 0.5648 us
Min = 17.0220 us, Q1 = 17.8195 us, Median = 18.1454 us, Q3 = 18.6187 us,
Max = 19.3200 us
IQR = 0.7992 us, LowerFence = 16.6206 us, UpperFence = 19.8175 us
ConfidenceInterval = [17.8657 us; 18.5912 us] (CI 99.9%), Margin = 0.3628
us (1.99% of Mean)
Skewness = 0.25, Kurtosis = 2.32
DispatchBenchmark.VisitorDispatch: DefaultJob
Runtime = .NET Core 4.6.26614.01, 64bit RyuJIT; GC = Concurrent Workstation
Mean = 20.2826 us, StdErr = 0.0991 us (0.49%); N = 16, StdDev = 0.3962 us
Min = 19.6451 us, Q1 = 20.0352 us, Median = 20.2013 us, Q3 = 20.5714 us,
Max = 21.1188 us
IQR = 0.5362 us, LowerFence = 19.2308 us, UpperFence = 21.3757 us
ConfidenceInterval = [19.8792 us; 20.6860 us] (CI 99.9%), Margin = 0.4034
us (1.99% of Mean)
Skewness = 0.46, Kurtosis = 2.24
Total time: 00:02:40 (160.33 sec)
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAIBUUW7GCAID4NRCZ3G5WDPR7VP7ANCNFSM4HHA7VIQ>
.
|
|
@waf On this:
eventually did last night (and good news, it works) but couldn't get the time to prepare a worthy commit.. Will do soon, with added corresponding unit tests. I'm also considering at least one possibly meaningful use case to support static methods in combination with surrogates, or if only those bound to functions : eg, sth along the lines of (hypothetical), var absoluteValue = default(object).CreateSurrogate(typeof(Math), nameof(Math.Abs), default(object)); var a1 = (int)absoluteValue(-123); // valid cast, as it returns 123 (System.Int32) var a2 = (decimal)absoluteValue(-499.99m); // valid cast, as it returns 499.99m (System.Decimal) or we might even be able to avoid the boxing of at least the result's value type, if with enough facility provided by CreateSurrogate, eg, var absoluteValue = default(decimal).CreateSurrogate(typeof(Math), nameof(Math.Abs), default(object)); // (let the generic extension method give the type hint about TResult at compile time) decimal a3 = absoluteValue(-499.99m); overloaded static functions of that sort alike Math.Abs can be frequent in some APIs / frameworks / libraries. And although it seems like a moot point to use surrogates instead of binding early (compile time) to the best overload of the static method, the idea is... it becomes relevant when one wants to find that best overload (depending on the actual argument's runtime type) among inherited or declared or bargained static methods of a class type only discovered at runtime, eg, var mathFunctions = useV2 ? typeof(OurLib.MathV2) : typeof(System.Math); var absoluteValue = default(object).CreateSurrogate(mathFunctions, nameof(Math.Abs), ...); "Hey, I just want to get the absolute value of a numeric type I can't know in advance, using one out of a bunch of static methods of a math-related class I can't know in advance either - but I also want the best overload that matches the number I will actually be passing in, at runtime, thanks" etc, etc. I'd probably go for double dispatching only to the declared or inherited public static ones, though, not the protected or protected internal's. |
…orresp advanced sample / test, also featuring closed generic types / method signatures
…meters Sample / Test
No description provided.