CPU仿真并锁定到特定的时钟速度

时间:2008-09-21 22:50:45

标签: low-level emulation 6502 clockrates

如果您已经阅读了我的其他question,那么您将知道我本周末花了6502 CPU模拟器作为编程练习。

CPU模拟器大部分都是完整的,并且在我的有限测试中似乎相当准确,但运行速度非常快,我想将其降低到机器的实际时钟速度。

我目前的测试循环是:

    // Just loop infinitely.
    while (1 == 1)
    {                
        CPU.ClockCyclesBeforeNext--;

        if (CPU.ClockCyclesBeforeNext <= 0)
        {
            // Find out how many clock cycles this instruction will take
            CPU.ClockCyclesBeforeNext = CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].CpuCycles;

            // Run the instruction
            CPU.ExecuteInstruction(CPU.Memory[CPU.PC]);

            // Debugging Info
            CPU.DumpDebug();
            Console.WriteLine(CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength);

            // Move to next instruction
            CPU.PC += 1 + CPU.OpcodeMapper.Map[CPU.Memory[CPU.PC]].ArgumentLength;                                        
        }
    }

正如您所知,每个操作码都需要一段特定的时间才能完成,因此在计算CPU周期时钟之前,我不会运行下一条指令。这提供了操作码之间的适当时序,只是整个事情都在快速运行。

目标CPU速度是1.79mhz,但是我想要时钟问题的任何解决方案来保持速度为1.79mhz,即使我增加了复杂性,所以我不需要调整它。

有什么想法吗?

7 个答案:

答案 0 :(得分:13)

我多年前写了一个Z80仿真器,并且为了循环精确执行,我将时钟速率分成许多小块,并且核心执行了很多时钟周期。在我的情况下,我把它绑定到我正在模拟的游戏系统的帧速率。每个操作码都知道执行了多少个周期,并且核心将继续运行操作码,直到执行了指定的周期数。我有一个外部运行循环,它将运行cpu核心,并运行模拟系统的其他部分,然后休眠直到下一次迭代的开始时间。

编辑:添加运行循环的示例。

int execute_run_loop( int cycles )
{
    int n = 0;
    while( n < cycles )
    {
        /* Returns number of cycles executed */
        n += execute_next_opcode();
    }

    return n;
}

希望这有帮助。

答案 1 :(得分:8)

查看原始的quicktime文档以获取灵感。

这是很久以前写的,当时显示的视频只是以足够高的速度交换静止帧,但苹果公司认为他们需要一个完整的时间管理框架。设计起初看起来过度设计,但它让它们处理不同的速度要求并使它们保持紧密同步。

幸运的是,6502具有确定性的时间行为,每个指令所用的确切时间都有详细记录;但它并不是一成不变的。一些指令需要2个周期,其他3.就像QuickTime中的帧一样,视频没有“每秒帧数”参数,每帧都表示它想要在屏幕上显示多长时间。

由于现代CPU是如此不确定,并且多任务操作系统甚至可以冻结几毫秒(虚拟内存!),如果您落后于计划,或者如果您可以休息几毫秒,则应该保留一个选项卡。

答案 2 :(得分:4)

正如jfk所说,最常见的方法是将cpu速度与(模拟)视频输出的垂直刷新联系起来

选择每个视频帧运行一些周期。这通常是特定于机器的,但您可以通过以下方式计算它:

cycles = clock speed in Hz / required frames-per-second

然后你也可以进行睡眠,直到视频更新被点击,此时你开始接下来的n个CPU仿真周期。

如果您特别模仿某些内容,那么您只需要查看fps速率和处理器速度即可实现这一点。

编辑:如果您没有任何外部时序要求,那么仿真器尽可能快地运行是正常的。有时这是一种理想的效果,有时不会:)

答案 3 :(得分:3)

我会使用时钟周期来计算时间,然后让他们在时间上睡觉。当然,要做到这一点,你需要一个高分辨率的时钟。他们这样做的方式是在旋转循环中将CPU加速。

答案 4 :(得分:2)

是的,正如大多数时候所说的那样,你不需要CPU模拟器以与真实物体相同的速度模拟指令。用户感知的是计算的输出(即音频和视频输出),因此您只需要与这些输出同步,这并不意味着您必须具有精确的CPU仿真速度。 / p>

换句话说,如果视频输入的帧速率是50Hz,那么让CPU模拟器以尽可能快的速度运行以绘制屏幕,​​但一定要以正确的速率输出屏幕帧( 50赫兹)。从外部角度来看,您的模拟器正在以正确的速度进行模拟。

在Windows或Linux等多任务操作系统上尝试即使在执行时也能完全循环是没有意义的,因为仿真器指令时间(老式80s CPU的简单1uS)和现代操作系统的调度时隙是可比较的。

尝试以50Hz的速率输出内容是一项更简单的任务,你可以在任何现代机器上做得很好

答案 5 :(得分:0)

如果实现音频仿真,并且音频输出与系统/ CPU时钟相关联,则可以使用另一个选项。特别是我知道这就是8位Apple的情况[计算机。

通常声音是在固定大小的缓冲区(固定时间)中生成的,因此这些缓冲区的操作(生成数据等)可以通过同步原语与CPU吞吐量相关联。

答案 6 :(得分:0)

我正在制作一些更通用的用例,例如将时间转换为估计数量的指令的能力,反之亦然。

项目主页为@ http://net7mma.codeplex.com

代码开头如下:(我认为)

    #region Copyright
/*
This file came from Managed Media Aggregation, You can always find the latest version @ https://net7mma.codeplex.com/

 Julius.Friedman@gmail.com / (SR. Software Engineer ASTI Transportation Inc. http://www.asti-trans.com)

Permission is hereby granted, free of charge, 
 * to any person obtaining a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, 
 * including without limitation the rights to :
 * use, 
 * copy, 
 * modify, 
 * merge, 
 * publish, 
 * distribute, 
 * sublicense, 
 * and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * 
 * JuliusFriedman@gmail.com should be contacted for further details.

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 * 
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 * TORT OR OTHERWISE, 
 * ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 * v//
 */
#endregion
namespace Media.Concepts.Classes
{
    //Windows.Media.Clock has a fairly complex but complete API

    /// <summary>
    /// Provides a clock with a given offset and calendar.
    /// </summary>
    public class Clock : Media.Common.BaseDisposable
    {
        static bool GC = false;

        #region Fields

        /// <summary>
        /// Indicates when the clock was created
        /// </summary>
        public readonly System.DateTimeOffset Created;

        /// <summary>
        /// The calendar system of the clock
        /// </summary>
        public readonly System.Globalization.Calendar Calendar;

        /// <summary>
        /// The amount of ticks which occur per update of the <see cref="System.Environment.TickCount"/> member.
        /// </summary>
        public readonly long TicksPerUpdate;

        /// <summary>
        /// The amount of instructions which occured when synchronizing with the system clock.
        /// </summary>
        public readonly long InstructionsPerClockUpdate;

        #endregion

        #region Properties

        /// <summary>
        /// The TimeZone offset of the clock from UTC
        /// </summary>
        public System.TimeSpan Offset { get { return Created.Offset; } }

        /// <summary>
        /// The average amount of operations per tick.
        /// </summary>
        public long AverageOperationsPerTick { get { return InstructionsPerClockUpdate / TicksPerUpdate; } }

        /// <summary>
        /// The <see cref="System.TimeSpan"/> which represents <see cref="TicksPerUpdate"/> as an amount of time.
        /// </summary>
        public System.TimeSpan SystemClockResolution { get { return System.TimeSpan.FromTicks(TicksPerUpdate); } }

        /// <summary>
        /// Return the current system time in the TimeZone offset of this clock
        /// </summary>
        public System.DateTimeOffset Now { get { return System.DateTimeOffset.Now.ToOffset(Offset).Add(new System.TimeSpan((long)(AverageOperationsPerTick / System.TimeSpan.TicksPerMillisecond))); } }

        /// <summary>
        /// Return the current system time in the TimeZone offset of this clock converter to UniversalTime.
        /// </summary>
        public System.DateTimeOffset UtcNow { get { return Now.ToUniversalTime(); } }

        //public bool IsUtc { get { return Offset == System.TimeSpan.Zero; } }

        //public bool IsDaylightSavingTime { get { return Created.LocalDateTime.IsDaylightSavingTime(); } }

        #endregion

        #region Constructor

        /// <summary>
        /// Creates a clock using the system's current timezone and calendar.
        /// The system clock is profiled to determine it's accuracy
        /// <see cref="System.DateTimeOffset.Now.Offset"/>
        /// <see cref="System.Globalization.CultureInfo.CurrentCulture.Calendar"/>
        /// </summary>
        public Clock(bool shouldDispose = true)
            : this(System.DateTimeOffset.Now.Offset, System.Globalization.CultureInfo.CurrentCulture.Calendar, shouldDispose)
        {
            try { if (false == GC && System.Runtime.GCSettings.LatencyMode != System.Runtime.GCLatencyMode.NoGCRegion) GC = System.GC.TryStartNoGCRegion(0); }
            catch { }
            finally
            {

                System.Threading.Thread.BeginCriticalRegion();

                //Sample the TickCount
                long ticksStart = System.Environment.TickCount,
                    ticksEnd;

                //Continually sample the TickCount. while the value has not changed increment InstructionsPerClockUpdate
                while ((ticksEnd = System.Environment.TickCount) == ticksStart) ++InstructionsPerClockUpdate; //+= 4; Read,Assign,Compare,Increment

                //How many ticks occur per update of TickCount
                TicksPerUpdate = ticksEnd - ticksStart;

                System.Threading.Thread.EndCriticalRegion();
            }
        }

        /// <summary>
        /// Constructs a new clock using the given TimeZone offset and Calendar system
        /// </summary>
        /// <param name="timeZoneOffset"></param>
        /// <param name="calendar"></param>
        /// <param name="shouldDispose">Indicates if the instace should be diposed when Dispose is called.</param>
        public Clock(System.TimeSpan timeZoneOffset, System.Globalization.Calendar calendar, bool shouldDispose = true)
        {
            //Allow disposal
            ShouldDispose = shouldDispose;

            Calendar = System.Globalization.CultureInfo.CurrentCulture.Calendar;

            Created = new System.DateTimeOffset(System.DateTime.Now, timeZoneOffset);
        }

        #endregion

        #region Overrides

        public override void Dispose()
        {

            if (false == ShouldDispose) return;

            base.Dispose();

            try
            {
                if (System.Runtime.GCSettings.LatencyMode == System.Runtime.GCLatencyMode.NoGCRegion)
                {
                    System.GC.EndNoGCRegion();

                    GC = false;
                }
            }
            catch { }
        }

        #endregion

        //Methods or statics for OperationCountToTimeSpan? (Estimate)
        public void NanoSleep(int nanos)
        {
            Clock.NanoSleep((long)nanos);
        }

        public static void NanoSleep(long nanos)
        {
            System.Threading.Thread.BeginCriticalRegion(); 

            NanoSleep(ref nanos); 

            System.Threading.Thread.EndCriticalRegion();
        }

        static void NanoSleep(ref long nanos)
        {
            try
            {
                unchecked
                {
                    while (Common.Binary.Clamp(--nanos, 0, 1) >= 2)
                    { 
                        /* if(--nanos % 2 == 0) */
                            NanoSleep(long.MinValue); //nanos -= 1 + (ops / (ulong)AverageOperationsPerTick);// *10;
                    }
                }
            }
            catch
            {
                return;
            }
        }
    }
}

一旦你有了某种类型的外行时钟实现,你就会进入Timer

之类的东西
/// <summary>
/// Provides a Timer implementation which can be used across all platforms and does not rely on the existing Timer implementation.
/// </summary>
public class Timer : Common.BaseDisposable
{
    readonly System.Threading.Thread m_Counter; // m_Consumer, m_Producer

    internal System.TimeSpan m_Frequency;

    internal ulong m_Ops = 0, m_Ticks = 0;

    bool m_Enabled;

    internal System.DateTimeOffset m_Started;

    public delegate void TickEvent(ref long ticks);

    public event TickEvent Tick;

    public bool Enabled { get { return m_Enabled; } set { m_Enabled = value; } }

    public System.TimeSpan Frequency { get { return m_Frequency; } }

    internal ulong m_Bias;

    //

    //Could just use a single int, 32 bits is more than enough.

    //uint m_Flags;

    //

    readonly internal Clock m_Clock = new Clock();

    readonly internal System.Collections.Generic.Queue<long> Producer;

    void Count()
    {

        System.Threading.Thread Event = new System.Threading.Thread(new System.Threading.ThreadStart(() =>
        {
            System.Threading.Thread.BeginCriticalRegion();
            long sample;
        AfterSample:
            try
            {
            Top:
                System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;

                while (m_Enabled && Producer.Count >= 1)
                {
                    sample = Producer.Dequeue();

                    Tick(ref sample);
                }

                System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;

                if (false == m_Enabled) return;

                while (m_Enabled && Producer.Count == 0) if(m_Counter.IsAlive) m_Counter.Join(0);  //++m_Ops;

                goto Top;
            }
            catch { if (false == m_Enabled) return; goto AfterSample; }
            finally { System.Threading.Thread.EndCriticalRegion(); }
        }))
        {
            IsBackground = false,
            Priority = System.Threading.ThreadPriority.AboveNormal
        };

        Event.TrySetApartmentState(System.Threading.ApartmentState.MTA);

        Event.Start();

        Approximate:

        ulong approximate = (ulong)Common.Binary.Clamp((m_Clock.AverageOperationsPerTick / (Frequency.Ticks + 1)), 1, ulong.MaxValue);

        try
        {
            m_Started = m_Clock.Now;

            System.Threading.Thread.BeginCriticalRegion();

            unchecked
            {
            Start:

                if (IsDisposed) return;

                switch (++m_Ops)
                {
                    default:
                        {
                            if (m_Bias + ++m_Ops >= approximate)
                            {
                                System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;

                                Producer.Enqueue((long)m_Ticks++);

                                ulong x = ++m_Ops / approximate;

                                while (1 > --x /*&& Producer.Count <= m_Frequency.Ticks*/) Producer.Enqueue((long)++m_Ticks);

                                m_Ops = (++m_Ops * m_Ticks) - (m_Bias = ++m_Ops / approximate);

                                System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;
                            }

                            if(Event != null) Event.Join(m_Frequency);

                            goto Start;
                        }
                }
            }
        }
        catch (System.Threading.ThreadAbortException) { if (m_Enabled) goto Approximate; System.Threading.Thread.ResetAbort(); }
        catch (System.OutOfMemoryException) { if ((ulong)Producer.Count > approximate) Producer.Clear(); if (m_Enabled) goto Approximate; }
        catch { if (m_Enabled) goto Approximate; }
        finally
        {
            Event = null;

            System.Threading.Thread.EndCriticalRegion();
        }
    }

    public Timer(System.TimeSpan frequency)
    {
        Producer = new System.Collections.Generic.Queue<long>((int)(m_Frequency = frequency).Ticks * 10);

        m_Counter = new System.Threading.Thread(new System.Threading.ThreadStart(Count))
        {
            IsBackground = false,
            Priority = System.Threading.ThreadPriority.AboveNormal
        };

        m_Counter.TrySetApartmentState(System.Threading.ApartmentState.MTA);

        Tick = delegate { m_Ops += 1 + m_Bias; };
    }

    public void Start()
    {
        if (m_Enabled) return;

        m_Enabled = true;

        m_Counter.Start();

        var p = System.Threading.Thread.CurrentThread.Priority;

        System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;

        while (m_Ops == 0) m_Counter.Join(0); //m_Clock.NanoSleep(0);

        System.Threading.Thread.CurrentThread.Priority = p;

    }

    public void Stop()
    {
        m_Enabled = false;
    }

    void Change(System.TimeSpan interval, System.TimeSpan dueTime)
    {
        m_Enabled = false;

        m_Frequency = interval;

        m_Enabled = true;
    }

    delegate void ElapsedEvent(object sender, object args);

    public override void Dispose()
    {
        if (IsDisposed) return;            

        base.Dispose();

        Stop();

        try { m_Counter.Abort(m_Frequency); }
        catch (System.Threading.ThreadAbortException) { System.Threading.Thread.ResetAbort(); }
        catch { }

        Tick = null;

        //Producer.Clear();
    }

}

然后你可以使用像

这样的东西来复制一些逻辑
 /// <summary>
/// Provides a completely managed implementation of <see cref="System.Diagnostics.Stopwatch"/> which expresses time in the same units as <see cref="System.TimeSpan"/>.
/// </summary>
public class Stopwatch : Common.BaseDisposable
{
    internal Timer Timer;

    long Units;

    public bool Enabled { get { return Timer != null && Timer.Enabled; } }

    public double ElapsedMicroseconds { get { return Units * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(Timer.Frequency); } }

    public double ElapsedMilliseconds { get { return Units * Timer.Frequency.TotalMilliseconds; } }

    public double ElapsedSeconds { get { return Units * Timer.Frequency.TotalSeconds; } }

    //public System.TimeSpan Elapsed { get { return System.TimeSpan.FromMilliseconds(ElapsedMilliseconds / System.TimeSpan.TicksPerMillisecond); } }

    public System.TimeSpan Elapsed
    {
        get
        {
            switch (Units)
            {
                case 0: return System.TimeSpan.Zero;
                default:
                    {
                        System.TimeSpan taken = System.DateTime.UtcNow - Timer.m_Started;

                        return taken.Add(new System.TimeSpan(Units * Timer.Frequency.Ticks));

                        //System.TimeSpan additional = new System.TimeSpan(Media.Common.Extensions.Math.MathExtensions.Clamp(Units, 0, Timer.Frequency.Ticks));

                        //return taken.Add(additional);
                    }
            }



            //////The maximum amount of times the timer can elapse in the given frequency
            ////double maxCount = (taken.TotalMilliseconds / Timer.Frequency.TotalMilliseconds) / ElapsedMilliseconds;

            ////if (Units > maxCount)
            ////{
            ////    //How many more times the event was fired than needed
            ////    double overage = (maxCount - Units);

            ////    additional = new System.TimeSpan(System.Convert.ToInt64(Media.Common.Extensions.Math.MathExtensions.Clamp(Units, overage, maxCount)));

            ////    //return taken.Add(new System.TimeSpan((long)Media.Common.Extensions.Math.MathExtensions.Clamp(Units, overage, maxCount)));
            ////}
            //////return taken.Add(new System.TimeSpan(Units));


        }
    }

    public void Start()
    {
        if (Enabled) return;

        Units = 0;

        //Create a Timer that will elapse every OneTick //`OneMicrosecond`
        Timer = new Timer(Media.Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick);

        //Handle the event by incrementing count
        Timer.Tick += Count;

        Timer.Start();
    }

    public void Stop()
    {
        if (false == Enabled) return;

        Timer.Stop();

        Timer.Dispose();           
    }

    void Count(ref long count) { ++Units; }
}

最后,创造一些半有用的东西,例如:一个总线,然后可能是一个虚拟屏幕,用于向总线发送数据......

public abstract class Bus : Common.CommonDisposable
    {
        public readonly Timer Clock = new Timer(Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick);

        public Bus() : base(false) { Clock.Start(); }
    }

    public class ClockedBus : Bus
    {
        long FrequencyHz, Maximum, End;

        readonly Queue<byte[]> Input = new Queue<byte[]>(), Output = new Queue<byte[]>();

        readonly double m_Bias;

        public ClockedBus(long frequencyHz, double bias = 1.5)
        {
            m_Bias = bias;

            cache = Clock.m_Clock.InstructionsPerClockUpdate / 1000;

            SetFrequency(frequencyHz);

            Clock.Tick += Clock_Tick;

            Clock.Start();
        }

        public void SetFrequency(long frequencyHz)
        {
            FrequencyHz = frequencyHz;

            //Clock.m_Frequency = new TimeSpan(Clock.m_Clock.InstructionsPerClockUpdate / 1000); 

            //Maximum = System.TimeSpan.TicksPerSecond / Clock.m_Clock.InstructionsPerClockUpdate;

            //Maximum = Clock.m_Clock.InstructionsPerClockUpdate / System.TimeSpan.TicksPerSecond;

            Maximum = cache / (cache / FrequencyHz);

            Maximum *= System.TimeSpan.TicksPerSecond;

            Maximum = (cache / FrequencyHz);

            End = Maximum * 2;

            Clock.m_Frequency = new TimeSpan(Maximum);

            if (cache < frequencyHz * m_Bias) throw new Exception("Cannot obtain stable clock");

            Clock.Producer.Clear();
        }

        public override void Dispose()
        {
            ShouldDispose = true;

            Clock.Tick -= Clock_Tick;

            Clock.Stop();

            Clock.Dispose();

            base.Dispose();
        }

        ~ClockedBus() { Dispose(); }

        long sample = 0, steps = 0, count = 0, avg = 0, cache = 1;

        void Clock_Tick(ref long ticks)
        {
            if (ShouldDispose == false && false == IsDisposed)
            {
                //Console.WriteLine("@ops=>" + Clock.m_Ops + " @ticks=>" + Clock.m_Ticks + " @Lticks=>" + ticks + "@=>" + Clock.m_Clock.Now.TimeOfDay + "@=>" + (Clock.m_Clock.Now - Clock.m_Clock.Created));

                steps = sample;

                sample = ticks;

                ++count;

                System.ConsoleColor f = System.Console.ForegroundColor;

                if (count <= Maximum)
                {
                    System.Console.BackgroundColor = ConsoleColor.Yellow;

                    System.Console.ForegroundColor = ConsoleColor.Green;

                    Console.WriteLine("count=> " + count + "@=>" + Clock.m_Clock.Now.TimeOfDay + "@=>" + (Clock.m_Clock.Now - Clock.m_Clock.Created) + " - " + DateTime.UtcNow.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt"));

                    avg = Maximum / count;

                    if (Clock.m_Clock.InstructionsPerClockUpdate / count > Maximum)
                    {
                        System.Console.ForegroundColor = ConsoleColor.Red;

                        Console.WriteLine("---- Over InstructionsPerClockUpdate ----" + FrequencyHz);
                    }
                }
                else if (count >= End)
                {
                    System.Console.BackgroundColor = ConsoleColor.Black;

                    System.Console.ForegroundColor = ConsoleColor.Blue;

                    avg = Maximum / count;

                    Console.WriteLine("avg=> " + avg + "@=>" + FrequencyHz);

                    count = 0;
                }
            }
        }

        //Read, Write at Frequency

    }
public class VirtualScreen
    {
        TimeSpan RefreshRate;    
        bool VerticalSync;    
        int Width, Height;            
        Common.MemorySegment DisplayMemory, BackBuffer, DisplayBuffer;
    }

以下是我测试StopWatch

的方法
internal class StopWatchTests
    {
        public void TestForOneMicrosecond()
        {
            System.Collections.Generic.List<System.Tuple<bool, System.TimeSpan, System.TimeSpan>> l = new System.Collections.Generic.List<System.Tuple<bool, System.TimeSpan, System.TimeSpan>>();

            //Create a Timer that will elapse every `OneMicrosecond`
            for (int i = 0; i <= 250; ++i) using (Media.Concepts.Classes.Stopwatch sw = new Media.Concepts.Classes.Stopwatch())
            {
                var started = System.DateTime.UtcNow;

                System.Console.WriteLine("Started: " + started.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt"));

                //Define some amount of time
                System.TimeSpan sleepTime = Media.Common.Extensions.TimeSpan.TimeSpanExtensions.OneMicrosecond;

                System.Diagnostics.Stopwatch testSw = new System.Diagnostics.Stopwatch();

                //Start
                testSw.Start();

                //Start
                sw.Start();

                while (testSw.Elapsed.Ticks < sleepTime.Ticks - (Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick + Common.Extensions.TimeSpan.TimeSpanExtensions.OneTick).Ticks)
                    sw.Timer.m_Clock.NanoSleep(0); //System.Threading.Thread.SpinWait(0);

                //Sleep the desired amount
                //System.Threading.Thread.Sleep(sleepTime);

                //Stop
                testSw.Stop();

                //Stop
                sw.Stop();

                var finished = System.DateTime.UtcNow;

                var taken = finished - started;

                var cc = System.Console.ForegroundColor;

                System.Console.WriteLine("Finished: " + finished.ToString("MM/dd/yyyy hh:mm:ss.ffffff tt"));

                System.Console.WriteLine("Sleep Time: " + sleepTime.ToString());

                System.Console.WriteLine("Real Taken Total: " + taken.ToString());

                if (taken > sleepTime) 
                {
                    System.Console.ForegroundColor = System.ConsoleColor.Red;
                    System.Console.WriteLine("Missed by: " + (taken - sleepTime));
                }
                else
                {
                    System.Console.ForegroundColor = System.ConsoleColor.Green;
                    System.Console.WriteLine("Still have: " + (sleepTime - taken));
                }

                System.Console.ForegroundColor = cc;

                System.Console.WriteLine("Real Taken msec Total: " + taken.TotalMilliseconds.ToString());

                System.Console.WriteLine("Real Taken sec Total: " + taken.TotalSeconds.ToString());

                System.Console.WriteLine("Real Taken μs Total: " + Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(taken).ToString());

                System.Console.WriteLine("Managed Taken Total: " + sw.Elapsed.ToString());

                System.Console.WriteLine("Diagnostic Taken Total: " + testSw.Elapsed.ToString());

                System.Console.WriteLine("Diagnostic Elapsed Seconds  Total: " + ((testSw.ElapsedTicks / (double)System.Diagnostics.Stopwatch.Frequency)));

                //Write the rough amount of time taken in  micro seconds
                System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedMicroseconds + "μs");

                //Write the rough amount of time taken in  micro seconds
                System.Console.WriteLine("Diagnostic Time Estimated Taken: " + Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TotalMicroseconds(testSw.Elapsed) + "μs");

                System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedMilliseconds);

                System.Console.WriteLine("Diagnostic Time Estimated Taken: " + testSw.ElapsedMilliseconds);

                System.Console.WriteLine("Managed Time Estimated Taken: " + sw.ElapsedSeconds);

                System.Console.WriteLine("Diagnostic Time Estimated Taken: " + testSw.Elapsed.TotalSeconds);

                if (sw.Elapsed < testSw.Elapsed)
                {
                    System.Console.WriteLine("Faster than Diagnostic StopWatch");
                    l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(true, sw.Elapsed, testSw.Elapsed));
                }
                else if (sw.Elapsed > testSw.Elapsed)
                {
                    System.Console.WriteLine("Slower than Diagnostic StopWatch");
                    l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(false, sw.Elapsed, testSw.Elapsed));
                }
                else
                {
                    System.Console.WriteLine("Equal to Diagnostic StopWatch");
                    l.Add(new System.Tuple<bool, System.TimeSpan, System.TimeSpan>(true, sw.Elapsed, testSw.Elapsed));
                }
            }

            int w = 0, f = 0;

            var cc2 = System.Console.ForegroundColor;

            foreach (var t in l)
            {
                if (t.Item1)
                {
                    System.Console.ForegroundColor = System.ConsoleColor.Green;
                    ++w; System.Console.WriteLine("Faster than Diagnostic StopWatch by: " + (t.Item3 - t.Item2));
                }
                else
                {
                    System.Console.ForegroundColor = System.ConsoleColor.Red;
                    ++f; System.Console.WriteLine("Slower than Diagnostic StopWatch by: " + (t.Item2 - t.Item3));
                }
            }

            System.Console.ForegroundColor = System.ConsoleColor.Green;
            System.Console.WriteLine("Wins = " + w);

            System.Console.ForegroundColor = System.ConsoleColor.Red;
            System.Console.WriteLine("Loss = " + f);

            System.Console.ForegroundColor = cc2;
        }
    }
相关问题