Last active
June 13, 2017 23:03
-
-
Save ayende/23853ab31706b334982ead1318efff1a to your computer and use it in GitHub Desktop.
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
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.IO.MemoryMappedFiles; | |
namespace FileAnalyzer | |
{ | |
public static unsafe class NativeRecord | |
{ | |
private static readonly int[] DaysToMonth365 = | |
{ | |
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 | |
}; | |
private static readonly int[] DaysToMonth366 = | |
{ | |
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 | |
}; | |
public static void Parse(byte* buffer, out int id, out int duration) | |
{ | |
duration = DiffTimesInSecond(buffer + 20, buffer); | |
id = ParseInt(buffer + 40, 8); | |
} | |
private static int DiffTimesInSecond(byte* start, byte* end) | |
{ | |
if ((*(long*) start == *(long*) end) && | |
(*(int*) (start + sizeof(long)) == *(int*) (end + sizeof(long)))) | |
{ | |
// same day, just compare the times | |
var startTime = ParseInt(start + 11, 2)*3600 + ParseInt(start + 14, 2)*60 + ParseInt(start + 17, 2); | |
var endTime = ParseInt(end + 11, 2)*3600 + ParseInt(end + 14, 2)*60 + ParseInt(end + 17, 2); | |
return endTime - startTime; | |
} | |
return UnlikelyFullDateDiff(start, end); | |
} | |
private static int UnlikelyFullDateDiff(byte* start, byte* end) | |
{ | |
return ParseDateInSeconds(end) - ParseDateInSeconds(start); | |
} | |
private static int ParseDateInSeconds(byte* buffer) | |
{ | |
var year = ParseInt(buffer, 4); | |
var month = ParseInt(buffer + 5, 2); | |
var day = ParseInt(buffer + 8, 2); | |
var hour = ParseInt(buffer + 11, 2); | |
var min = ParseInt(buffer + 14, 2); | |
var sec = ParseInt(buffer + 17, 2); | |
var leap = (year%4 == 0) && ((year%100 != 0) || (year%400 == 0)); | |
var days = leap ? DaysToMonth366 : DaysToMonth365; | |
var y = year - 1; | |
var n = y*365 + y/4 - y/100 + y/400 + days[month - 1] + day - 1; | |
var totalSeconds = hour*3600 + min*60 + sec; | |
return n*24*60*60 + totalSeconds; | |
} | |
private static int ParseInt(byte* buffer, int size) | |
{ | |
var val = 0; | |
for (var i = 0; i < size; i++) | |
{ | |
val *= 10; | |
val += buffer[i] - '0'; | |
} | |
return val; | |
} | |
} | |
internal unsafe class Program | |
{ | |
private static void Increment(ref int[] array, int id, int value) | |
{ | |
if (id < array.Length) | |
{ | |
array[id] += value; | |
return; | |
} | |
UnlikelyGrowArray(ref array, id, value); | |
} | |
private static void UnlikelyGrowArray(ref int[] array, int id, int value) | |
{ | |
var size = array.Length*2; | |
while (id >= size) | |
size *= 2; | |
Array.Resize(ref array, size); | |
Increment(ref array, id, value); | |
} | |
private static void Main(string[] args) | |
{ | |
AppDomain.MonitoringIsEnabled = true; | |
var sp = Stopwatch.StartNew(); | |
var stats = new int[256]; | |
using (var mmf = MemoryMappedFile.CreateFromFile(args[0])) | |
{ | |
using (var accessor = mmf.CreateViewAccessor()) | |
{ | |
byte* buffer = null; | |
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref buffer); | |
var len = new FileInfo(args[0]).Length; | |
var entries = len/50; | |
for (var i = 0; i < entries; i++) | |
{ | |
int id; | |
int duration; | |
NativeRecord.Parse(buffer, out id, out duration); | |
buffer += 50; | |
Increment(ref stats, id, duration); | |
} | |
} | |
} | |
WriteOutput(stats); | |
Console.WriteLine($"Took: {sp.ElapsedMilliseconds:#,#} ms and allocated " + | |
$"{AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize/1024:#,#} kb " + | |
$"with peak working set of {Process.GetCurrentProcess().PeakWorkingSet64/1024:#,#} kb"); | |
} | |
private static void WriteOutput(int[] stats) | |
{ | |
var tempBuffer = new byte[21]; | |
tempBuffer[10] = (byte) ' '; | |
tempBuffer[13] = (byte) ':'; | |
tempBuffer[16] = (byte) ':'; | |
tempBuffer[19] = (byte) '\r'; | |
tempBuffer[20] = (byte) '\n'; | |
using (var output = File.Create("summary.txt")) | |
{ | |
for (var i = 0; i < stats.Length; i++) | |
{ | |
var value = stats[i]; | |
if (value == 0) | |
continue; | |
WriteFormattedInt(i, tempBuffer, 0, 10); | |
var ts = TimeSpan.FromSeconds(value); | |
WriteFormattedTime(ts, tempBuffer, 11); | |
output.Write(tempBuffer, 0, tempBuffer.Length); | |
} | |
} | |
} | |
private static void WriteFormattedInt(int id, byte[] temp, int pos, int numberOfDigits) | |
{ | |
var i = pos + numberOfDigits - 1; | |
do | |
{ | |
temp[i--] = (byte) (id%10 + '0'); | |
} while ((id /= 10) > 0); | |
while (i >= pos) | |
temp[i--] = (byte) '0'; | |
} | |
private static void WriteFormattedTime(TimeSpan ts, byte[] temp, int pos) | |
{ | |
var hours = ts.Hours; | |
temp[pos] = (byte) (hours/10 + '0'); | |
temp[pos + 1] = (byte) (hours%10 + '0'); | |
var min = ts.Minutes; | |
temp[pos + 3] = (byte) (min/10 + '0'); | |
temp[pos + 4] = (byte) (min%10 + '0'); | |
var sec = ts.Seconds; | |
temp[pos + 5] = (byte) (sec/10 + '0'); | |
temp[pos + 6] = (byte) (sec%10 + '0'); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment