Created
November 18, 2013 15:40
-
-
Save carbonrobot/7529910 to your computer and use it in GitHub Desktop.
Performance of Buffer.BlockCopy on multidimensional arrays
This file contains hidden or 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
// Begin linqpad script | |
void Main() | |
{ | |
var sourceIndex = 3; | |
var source = new double[5,1000]; | |
for(int i = 0; i < source.GetLength(1); i++){ | |
source[sourceIndex, i] = i + 1; | |
} | |
// for loop | |
Action<double[,]> func1 = (s) => { | |
var length = s.GetLength(1); | |
var target0 = new double[length]; | |
var target1 = new double[length]; | |
var target2 = new double[length]; | |
var target3 = new double[length]; | |
var target4 = new double[length]; | |
for(int i = 0; i < length; i++){ | |
target0[i] = s[0, i]; | |
target1[i] = s[1, i]; | |
target2[i] = s[2, i]; | |
target3[i] = s[3, i]; | |
target4[i] = s[4, i]; | |
} | |
}; | |
// block copy | |
Action<double[,]> func2 = (s) => { | |
var length = s.GetLength(1); | |
var size = sizeof(double); | |
var byteLength = size * length; | |
var target0 = new double[length]; | |
var target1 = new double[length]; | |
var target2 = new double[length]; | |
var target3 = new double[length]; | |
var target4 = new double[length]; | |
Buffer.BlockCopy(source, byteLength * 0, target0, 0, byteLength); | |
Buffer.BlockCopy(source, byteLength * 1, target0, 0, byteLength); | |
Buffer.BlockCopy(source, byteLength * 2, target0, 0, byteLength); | |
Buffer.BlockCopy(source, byteLength * 3, target0, 0, byteLength); | |
Buffer.BlockCopy(source, byteLength * 4, target0, 0, byteLength); | |
}; | |
Benchmark | |
.For(1000) | |
.Execute("For Loop", () => func1(source)) | |
.Execute("Buffer ", () => func2(source)) | |
.Report(4).Dump(); | |
} | |
public static class Benchmark | |
{ | |
public static BenchmarkActionScheduler For(int count) | |
{ | |
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); | |
return new BenchmarkActionScheduler(count); | |
} | |
} | |
public class BenchmarkActionScheduler | |
: IBenchmarkActionScheduler | |
{ | |
private readonly int count; | |
private readonly IDictionary<string, Action> actionDictionary = | |
new Dictionary<string, Action>(); | |
private int actionIndex; | |
internal BenchmarkActionScheduler(int count) | |
{ | |
this.count = count; | |
} | |
public IBenchmarkActionScheduler Execute(Action action) | |
{ | |
return Execute((actionIndex++).ToString(), action); | |
} | |
public IBenchmarkActionScheduler Execute(string name, Action action) | |
{ | |
actionDictionary.Add(name, action); | |
return this; | |
} | |
IEnumerable<Report> IBenchmarkActionScheduler.Report(int? digits) | |
{ | |
var reportList = new List<Report>(); | |
foreach (var action in actionDictionary) | |
{ | |
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); | |
var stopwatch = Stopwatch.StartNew(); | |
var localAction = action; | |
Enumerable | |
.Range(0, count) | |
.Iterate(() => localAction.Value()); | |
reportList | |
.Add(new Report(action.Key, stopwatch.ElapsedMilliseconds)); | |
} | |
var maxTime = reportList.Max(x => x.Milliseconds); | |
foreach (var report in reportList) | |
{ | |
var percentage = (report.Milliseconds * 100) / (double)maxTime; | |
if (digits.HasValue) | |
{ | |
percentage = Math.Round(percentage, digits.Value); | |
} | |
report.Percentage = percentage; | |
report.TimesFaster = Math.Round((double)maxTime / (double)report.Milliseconds, digits.Value); | |
} | |
return reportList; | |
} | |
} | |
public interface IBenchmarkActionScheduler | |
{ | |
IEnumerable<Report> Report(int? digits = null); | |
IBenchmarkActionScheduler Execute(Action action); | |
IBenchmarkActionScheduler Execute(string name, Action action); | |
} | |
public class Report | |
{ | |
public Report(string operationName, long milliseconds) | |
{ | |
OperationName = operationName; | |
Milliseconds = milliseconds; | |
} | |
public string OperationName { get; private set; } | |
public long Milliseconds { get; private set; } | |
public double Percentage { get; internal set; } | |
public double TimesFaster { get; internal set; } | |
} | |
public static class EnumerableExensions | |
{ | |
public static void Iterate<T>(this IEnumerable<T> collection) | |
{ | |
var enumerator = collection.GetEnumerator(); | |
while (enumerator.MoveNext()) | |
{ | |
} | |
} | |
public static void Iterate<T>(this IEnumerable<T> collection, Action action) | |
{ | |
var enumerator = collection.GetEnumerator(); | |
while (enumerator.MoveNext()) | |
{ | |
action(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment