Last active
March 2, 2017 22:30
-
-
Save m039/bdd4f5cf300378e10e01466da43614cb to your computer and use it in GitHub Desktop.
C# structs and classes as wrappers, performance benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
namespace VoidSharedProject | |
{ | |
// Results: | |
// 1: 00:00:47.3721840 | |
// 2: 00:01:14.6771620 | |
// 3: 00:00:10.9821360 | |
// 4: 00:00:20.9437850 | |
// 5: 00:00:42.9371950 | |
// 6: 00:00:24.0811930 | |
// Main thoughts are: | |
// - A wrapper is twice expensive than an extension method and an extension method is twice expensive as a normal one. | |
// - Obviously structures are better than classes as wrappers. | |
// - Second use of a wrapper is very cheap, like an extension method. | |
public class A | |
{ | |
private int bb = 14; | |
public int b() | |
{ | |
return bb; | |
} | |
} | |
public interface IA | |
{ | |
int BB(); | |
} | |
public struct RefStruct<T> | |
{ | |
internal T r; | |
public RefStruct(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public struct AWrapper : IA | |
{ | |
internal A a; | |
public AWrapper(A a) | |
{ | |
this.a = a; | |
} | |
public int BB() | |
{ | |
return a.b(); | |
} | |
public static implicit operator AWrapper(A a) | |
{ | |
return new AWrapper(a); | |
} | |
} | |
public class RefClass<T> | |
{ | |
internal T r; | |
public RefClass(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public static class Extensions | |
{ | |
public static RefStruct<T> ToStruct<T>(this T a) | |
{ | |
return new RefStruct<T>(a); | |
} | |
public static RefClass<T> ToClass<T>(this T a) | |
{ | |
return new RefClass<T>(a); | |
} | |
public static int GetB(this RefStruct<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this RefClass<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this A a) | |
{ | |
return a.b(); | |
} | |
} | |
public class Test | |
{ | |
public static int T() | |
{ | |
const int count = 100000000; | |
Stopwatch stopWatch; | |
int sum = 0; | |
var a = new A(); | |
// 0, warm up | |
for (var i = 0; i < count / 100; i++) { | |
sum += 1; | |
} | |
// 1 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("1: " + stopWatch.Elapsed); | |
// 2 | |
sum = 0; | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("2: " + stopWatch.Elapsed); | |
// 3 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("3: " + stopWatch.Elapsed); | |
// 4 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("4: " + stopWatch.Elapsed); | |
// 5 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("5: " + stopWatch.Elapsed); | |
// 6 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
var w = (AWrapper)a; | |
w.BB(); | |
w.BB(); | |
w.BB(); | |
w.BB(); | |
w.BB(); | |
w.BB(); | |
w.BB(); | |
w.BB(); | |
w.BB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("6: " + stopWatch.Elapsed); | |
return 1; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define JUNK | |
using System; | |
using System.Diagnostics; | |
namespace SharedProject | |
{ | |
#if JUNK | |
// With a lot of junk, results: | |
// 1: 00:01:18.2174800 | |
// 2: 00:01:25.3262710 | |
// 3: 00:01:07.2275850 (100%) | |
// 4: 00:01:06.4023880 | |
// 5: 00:01:12.6649500 | |
public class A | |
{ | |
private int bb = 14; | |
public int b1 = 14; | |
public int b32 = 14; | |
public int b4 = 14; | |
public int b5 = 14; | |
public int b6 = 14; | |
public int b7 = 14; | |
public int[] bs = new int[100]; | |
public int[] b2s = new int[100]; | |
public int b() | |
{ | |
return bb; | |
} | |
} | |
#else | |
// Without junk, results: | |
// 1: 00:00:29.5859890 | |
// 2: 00:00:40.9515950 | |
// 3: 00:00:16.3016150 (100%) | |
// 4: 00:00:18.7555140 | |
// 5: 00:00:28.0705080 | |
public class A | |
{ | |
private int bb = 14; | |
public int b() | |
{ | |
return bb; | |
} | |
} | |
#endif | |
public interface IA | |
{ | |
int BB(); | |
} | |
public struct RefStruct<T> | |
{ | |
internal T r; | |
public RefStruct(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public struct AWrapper : IA | |
{ | |
internal A a; | |
public AWrapper(A a) | |
{ | |
this.a = a; | |
} | |
public int BB() | |
{ | |
return a.b(); | |
} | |
public static implicit operator AWrapper(A a) | |
{ | |
return new AWrapper(a); | |
} | |
} | |
public class RefClass<T> | |
{ | |
internal T r; | |
public RefClass(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public static class Extensions | |
{ | |
public static RefStruct<T> ToStruct<T>(this T a) | |
{ | |
return new RefStruct<T>(a); | |
} | |
public static RefClass<T> ToClass<T>(this T a) | |
{ | |
return new RefClass<T>(a); | |
} | |
public static int GetB(this RefStruct<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this RefClass<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this A a) | |
{ | |
return a.b(); | |
} | |
} | |
public class Test | |
{ | |
public static int T() | |
{ | |
const int count = 100000000; | |
Stopwatch stopWatch; | |
int sum = 0; | |
// 0, warm up | |
for (var i = 0; i < count / 100; i++) { | |
sum += 1; | |
} | |
// 1 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
new A().ToStruct().GetB(); | |
new A().ToStruct().GetB(); | |
new A().ToStruct().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("1: " + stopWatch.Elapsed); | |
// 2 | |
sum = 0; | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
new A().ToClass().GetB(); | |
new A().ToClass().GetB(); | |
new A().ToClass().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("2: " + stopWatch.Elapsed); | |
// 3 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
new A().b(); | |
new A().b(); | |
new A().b(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("3: " + stopWatch.Elapsed); | |
// 4 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
new A().GetB(); | |
new A().GetB(); | |
new A().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("4: " + stopWatch.Elapsed); | |
// 5 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
((AWrapper)new A()).BB(); | |
((AWrapper)new A()).BB(); | |
((AWrapper)new A()).BB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("5: " + stopWatch.Elapsed); | |
return 1; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
namespace VoidSharedProject | |
{ | |
// 1: 00:00:15.7232990 | |
// 2: 00:00:27.3071830 | |
// 3: 00:00:04.3568800 | |
// 4: 00:00:07.7676690 | |
// 5: 00:00:16.3462180 | |
public class A | |
{ | |
private int bb = 14; | |
public int b() | |
{ | |
return bb; | |
} | |
} | |
public interface IA | |
{ | |
int BB(); | |
} | |
public struct RefStruct<T> | |
{ | |
internal T r; | |
public RefStruct(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public struct AWrapper : IA | |
{ | |
internal A a; | |
public AWrapper(A a) | |
{ | |
this.a = a; | |
} | |
public int BB() | |
{ | |
return a.b(); | |
} | |
public static implicit operator AWrapper(A a) | |
{ | |
return new AWrapper(a); | |
} | |
} | |
public class RefClass<T> | |
{ | |
internal T r; | |
public RefClass(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public static class Extensions | |
{ | |
public static RefStruct<T> ToStruct<T>(this T a) | |
{ | |
return new RefStruct<T>(a); | |
} | |
public static RefClass<T> ToClass<T>(this T a) | |
{ | |
return new RefClass<T>(a); | |
} | |
public static int GetB(this RefStruct<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this RefClass<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this A a) | |
{ | |
return a.b(); | |
} | |
} | |
public class Test | |
{ | |
public static int T() | |
{ | |
const int count = 100000000; | |
Stopwatch stopWatch; | |
int sum = 0; | |
var a = new A(); | |
// 0, warm up | |
for (var i = 0; i < count / 100; i++) { | |
sum += 1; | |
} | |
// 1 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("1: " + stopWatch.Elapsed); | |
// 2 | |
sum = 0; | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("2: " + stopWatch.Elapsed); | |
// 3 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.b(); | |
a.b(); | |
a.b(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("3: " + stopWatch.Elapsed); | |
// 4 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("4: " + stopWatch.Elapsed); | |
// 5 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("5: " + stopWatch.Elapsed); | |
return 1; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
namespace VoidSharedProject | |
{ | |
// Results: | |
// 1: 00:00:47.3503060 | |
// 2: 00:01:33.3255210 | |
// 3: 00:00:11.7213860 | |
// 4: 00:00:22.9640250 | |
// 5: 00:00:46.8433780 | |
public class A | |
{ | |
private int bb = 14; | |
public int b() | |
{ | |
return bb; | |
} | |
} | |
public interface IA | |
{ | |
int BB(); | |
} | |
public struct RefStruct<T> | |
{ | |
internal T r; | |
public RefStruct(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public struct AWrapper : IA | |
{ | |
internal A a; | |
public AWrapper(A a) | |
{ | |
this.a = a; | |
} | |
public int BB() | |
{ | |
return a.b(); | |
} | |
public static implicit operator AWrapper(A a) | |
{ | |
return new AWrapper(a); | |
} | |
} | |
public class RefClass<T> | |
{ | |
internal T r; | |
public RefClass(T r) | |
{ | |
this.r = r; | |
} | |
} | |
public static class Extensions | |
{ | |
public static RefStruct<T> ToStruct<T>(this T a) | |
{ | |
return new RefStruct<T>(a); | |
} | |
public static RefClass<T> ToClass<T>(this T a) | |
{ | |
return new RefClass<T>(a); | |
} | |
public static int GetB(this RefStruct<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this RefClass<A> ia) | |
{ | |
return ia.r.b(); | |
} | |
public static int GetB(this A a) | |
{ | |
return a.b(); | |
} | |
} | |
public class Test | |
{ | |
public static int T() | |
{ | |
const int count = 100000000; | |
Stopwatch stopWatch; | |
int sum = 0; | |
var a = new A(); | |
// 0, warm up | |
for (var i = 0; i < count / 100; i++) { | |
sum += 1; | |
} | |
// 1 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
a.ToStruct().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("1: " + stopWatch.Elapsed); | |
// 2 | |
sum = 0; | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
a.ToClass().GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("2: " + stopWatch.Elapsed); | |
// 3 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
a.b(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("3: " + stopWatch.Elapsed); | |
// 4 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
a.GetB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("4: " + stopWatch.Elapsed); | |
// 5 | |
stopWatch = new Stopwatch(); | |
stopWatch.Start(); | |
for (var i = 0; i < count; i++) { | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
((AWrapper)a).BB(); | |
} | |
stopWatch.Stop(); | |
Console.WriteLine("5: " + stopWatch.Elapsed); | |
return 1; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment