//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // Note: // using System; using System.Runtime.CompilerServices; using Microsoft.Singularity; using Microsoft.Singularity.Directory; using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.Io; using Microsoft.Singularity.Channels; using System.Threading; using Microsoft.Contracts; using Microsoft.SingSharp.Reflection; using Microsoft.Singularity.Applications; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; [assembly: Transform(typeof(ApplicationResourceTransform))] namespace Microsoft.Singularity.Applications.Benchmarks.DiskRWNull { [ConsoleCategory(HelpMessage="Null Disk r/w performance test", DefaultAction=true)] internal class Parameters { [InputEndpoint("data")] public readonly TRef Stdin; [OutputEndpoint("data")] public readonly TRef Stdout; [StringParameter( "device", Mandatory=true, Position=0 , HelpMessage="Device to read/write")] internal string deviceName; [LongParameter( "b", Default=0, HelpMessage="Set block size to bytes.")] internal long blockBytes; [LongParameter( "g", Default=1, HelpMessage="Repeat test n times.")] internal long repeatCount; [LongParameter( "l", Default=0, HelpMessage="Fix disk size limit at megabytes.")] internal long diskLimitMB; [LongParameter( "m", Default=0, HelpMessage="Perform test with megabytes of data.")] internal long dataMB; [LongParameter( "n", Default=0, HelpMessage="Perform test with blocks of data.")] internal long blockCount; [LongParameter( "p", Default=0, HelpMessage="Pause seconds between iterations")] internal long pauseSecs; [BoolParameter( "r", Default=false, HelpMessage="Perform disk read test.")] internal bool doRead ; [BoolParameter( "w", Default=false, HelpMessage="Perform disk write test.")] internal bool doWrite; [BoolParameter( "x", Default=false, HelpMessage="Perform test with random I/O offsets.")] internal bool randomIO; reflective internal Parameters(); internal int AppMain() { return DiskBenchmark.AppMain(this); } } contract Hack { message Hak(); state HACK : one { Hak? -> Hak! -> HACK; } } public class DiskBenchmark { static TRef himp; static TRef hexp; private const uint BytesPerSector = 512; private const uint SectorsPerMB = 1024 * 1024 / BytesPerSector; private static ulong GetRandomOffset(SRandom! rng, ulong diskSectorCount, ulong blockBytes) { ulong maxBlock = diskSectorCount * 512 / blockBytes; ulong offset = 0; for (int i = 0; i < 64; i += 30) { offset ^= (ulong)(rng.Next() << i); } offset &= 0x7fffffffffffffff; offset %= maxBlock; return offset * blockBytes; } private static void DisplayPerf(bool didRead, bool didRandom, TimeSpan elapsed, ulong blockCount, ulong blockBytes, ulong nextOffset) { double delta = (double)elapsed.Ticks / TimeSpan.TicksPerSecond; double ops = (double)blockCount / delta; Console.Write("{0}{1}.{2} ", didRandom ? "R" : "S", didRead ? "R" : "W", blockBytes); Console.WriteLine("Ops/s: {0:f2} MB/s: {1:f2} Elapsed: {2:f2} Check: {3:x}", ops, ops * blockBytes / (1024.0 * 1024.0), delta, nextOffset); } public static int DoDevice(string! deviceName, int runNumber, ulong blockCount, ulong blockBytes, ulong diskLimitMB, bool doRandom, bool doRead) { for (int i = 0; i < 4000; i++) { Hack.Imp! imp; Hack.Exp! exp; Hack.NewChannel(out imp, out exp); delete imp; delete exp; } ulong diskSectorCount = 4000000; // Constrain disk size if user requested if (diskLimitMB != 0 && diskSectorCount > diskLimitMB * SectorsPerMB) { diskSectorCount = diskLimitMB * SectorsPerMB; } diskLimitMB = diskSectorCount / SectorsPerMB; // Constrain block count if it overflows available blocks if (blockCount * blockBytes > diskSectorCount * BytesPerSector) { blockCount = diskSectorCount * BytesPerSector / blockBytes; } if (runNumber == 0) { Console.WriteLine("# Type: {0} {1}", doRandom ? "Random" : "Sequential", doRead ? "Read" : "Write"); Console.WriteLine("# Disk: {0} MB", diskSectorCount / SectorsPerMB); Console.WriteLine("# Limit: {0} MB", diskLimitMB); Console.WriteLine("# Work: {0:f2} MB", blockCount * blockBytes / (1024.0 * 1024.0)); Console.WriteLine("# Ops: {0}", blockCount); } byte[] in ExHeap buffer = new [ExHeap] byte [blockBytes]; SRandom rng = new SRandom(); ulong diskPos = 0; if (doRandom) { diskPos = GetRandomOffset(rng, diskSectorCount, blockBytes); } long startIrqCount = ProcessService.GetKernelInterruptCount(); long startSwitchCount = ProcessService.GetContextSwitchCount(); long startKernelGcCount = ProcessService.GetKernelGcCount(); int startGcCount; long startGcMillis; long startGcBytes; GC.PerformanceCounters(out startGcCount, out startGcMillis, out startGcBytes); Hack.Imp! imph = himp.Acquire(); Hack.Exp! exph = hexp.Acquire(); /// ESet eset = new ESet(); eset.Add(exph); /// TimeSpan startTime = ProcessService.GetUpTime(); for (ulong i = 0; i < blockCount; i++) { byte[]! in ExHeap outBuffer; if (doRead) { //ProcessService.GetUpTime(); //imph.SendHak(); //switch receive { case exph.Hak(): exph.SendHak(); break; case unsatisfiable: throw new System.Exception(); } //imph.RecvHak(); /// imph.SendHak(); switch receive { case ep.Hak() in eset: ep.SendHak(); eset.Add(ep); break; case unsatisfiable: throw new System.Exception(); } imph.RecvHak(); /// //imph.SendHak(); exph.RecvHak(); exph.SendHak(); imph.RecvHak(); outBuffer = buffer; } else { outBuffer = buffer; } buffer = outBuffer; if (doRandom) { diskPos = GetRandomOffset(rng, diskSectorCount, blockBytes); } else { diskPos += blockBytes; } } TimeSpan endTime = ProcessService.GetUpTime(); /// imph.SendHak(); switch receive { case ep.Hak() in eset: ep.SendHak(); hexp.Release(ep); break; case unsatisfiable: throw new System.Exception(); } imph.RecvHak(); himp.Release(imph); eset.Dispose(); /// //hexp.Release(exph); //himp.Release(imph); delete buffer; DisplayPerf(doRead, doRandom, endTime - startTime, blockCount, blockBytes, diskPos); int endGcCount; long endGcMillis; long endGcBytes; GC.PerformanceCounters(out endGcCount, out endGcMillis, out endGcBytes); Console.WriteLine("[AppGC :- cnt {0} bytes {1} Kern: ints {2} swi {3} gcs {4}/{5}]", endGcCount - startGcCount, endGcBytes - startGcBytes, ProcessService.GetKernelInterruptCount() - startIrqCount, ProcessService.GetContextSwitchCount() - startSwitchCount, ProcessService.GetKernelGcCount() - startKernelGcCount, ProcessService.GetKernelGcCount()); return 0; } public static bool ParseNumber(string! arg, string! name, out ulong value) { // arg should look like "[-/][A-z]:[0-9]" if (arg.Length >= 4) { try { value = UInt64.Parse(arg.Substring(3)); return true; } catch (FormatException) { } catch (OverflowException) { } } Console.WriteLine("Could not parse {0}", name); value = 0; return false; } internal static int AppMain(Parameters! config) { string deviceName = config.deviceName; ulong blockBytes = (ulong) config.blockBytes; ulong blockCount = (ulong) config.blockCount; ulong diskLimitMB = (ulong) config.diskLimitMB; ulong repeatCount = (ulong) config.repeatCount; ulong pauseSecs = (ulong) config.pauseSecs; ulong dataMB = (ulong) config.dataMB; bool doWrite = config.doRead; bool doRead = config.doWrite; bool randomIO = config.randomIO; bool diskReadTest = false; Hack.Imp! imp; Hack.Exp! exp; Hack.NewChannel(out imp, out exp); himp = new TRef(imp); hexp = new TRef(exp); if (blockBytes < 512 || (blockBytes & (blockBytes - 1)) != 0) { Console.WriteLine("Block size must be a power of two greater than or equal to 512."); return -1; } if (doRead == doWrite) { Console.WriteLine("Need to specify either read or write test."); return -1; } else if (doRead) { diskReadTest = true; } if (dataMB != 0) { blockCount = dataMB * 1024 * 1024 / blockBytes; } for (int run = 0; run < (int)repeatCount; run++) { if (run != 0) System.Threading.Thread.Sleep(1000 * (int)pauseSecs); Tracing.Log(Tracing.Debug, "Starting diskrw run {0}", (UIntPtr) run); if (DoDevice(deviceName, run, blockCount, blockBytes, diskLimitMB, randomIO, diskReadTest) != 0) { return -1; } Tracing.Log(Tracing.Debug, "Ending diskrw run {0}", (UIntPtr) run); } return 0; } } }