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

发布于 2024-07-06 21:32:33 字数 1194 浏览 8 评论 0原文

如果您阅读过我的其他问题,您就会知道我这个周末已经组装了 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,即使我增加了复杂性,这样我就不必调整它。

有任何想法吗?

If you had read my other question, you'll know I've spent this weekend putting together a 6502 CPU emulator as a programming exercise.

The CPU emulator is mostly complete, and seems to be fairly accurate from my limited testing, however it is running incredibly fast, and I want to throttle it down to the actual clock speed of the machine.

My current test loop is this:

    // 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;                                        
        }
    }

As you can tell, each opcode takes a specific amount of time to complete, so I do not run the next instruction until I count down the CPU Cycle clock. This provides proper timing between opcodes, its just that the entire thing runs way to fast.

The targeted CPU speed is 1.79mhz, however I'd like whatever solution to the clock issue to keep the speed at 1.79mhz even as I add complexity, so I don't have to adjust it up.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

此岸叶落 2024-07-13 21:32:33

许多年前,我编写了一个 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;
}

希望这可以帮助。

I wrote a Z80 emulator many years ago, and to do cycle accurate execution, I divided the clock rate into a number of small blocks and had the core execute that many clock cycles. In my case, I tied it to the frame rate of the game system I was emulating. Each opcode knew how many cycles it took to execute and the core would keep running opcodes until the specified number of cycles had been executed. I had an outer run loop that would run the cpu core, and run other parts of the emulated system and then sleep until the start time of the next iteration.

EDIT: Adding example of run loop.

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

    return n;
}

Hope this helps.

绻影浮沉 2024-07-13 21:32:33

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

它是很久以前写的,当时显示视频意味着只需以足够高的速度交换静止帧,但苹果公司的人决定他们需要一个完整的时间管理框架。 该设计乍一看似乎过度设计,但它让它们能够处理截然不同的速度要求并保持它们紧密同步。

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

由于现代 CPU 的不确定性非常大,而且多任务操作系统甚至可能会冻结几毫秒(虚拟内存!),因此如果您落后于计划,或者如果您可以小睡几微秒,则应该留意。

Take a look at the original quicktime documentation for inspiration.

It was written a long time ago, when displaying video meant just swapping still frames at high enough speed, but the Apple guys decided they needed a full time-management framework. The design at first looks overengineered, but it let them deal with widely different speed requirements and keep them tightly synchronized.

you're fortunate that 6502 has deterministic time behaviour, the exact time each instruction takes is well documented; but it's not constant. some instructions take 2 cycles, other 3. Just like frames in QuickTime, a video doesn't have a 'frames per second' parameter, each frame tells how long it wants to be in screen.

Since modern CPU's are so non-deterministic, and multitasking OS's can even freeze for a few miliseconds (virtual memory!), you should keep a tab if you're behind schedule, or if you can take a few microseconds nap.

绝不放开 2024-07-13 21:32:33

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

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

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

然后您还可以进行睡眠,直到视频更新完成,此时您开始下一个 CPU 模拟周期。

如果您正在模拟某些特定的东西,那么您只需要查找 fps 速率和处理器速度即可获得大致正确的结果。

编辑:如果您没有任何外部时序要求,那么模拟器尽可能快地运行是正常的。 有时这是预期的效果,有时则不是:)

As jfk says, the most common way to do this is tie the cpu speed to the vertical refresh of the (emulated) video output.

Pick a number of cycles to run per video frame. This will often be machine-specific but you can calculate it by something like :

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

Then you also get to do a sleep until the video update is hit, at which point you start the next n cycles of CPU emulation.

If you're emulating something in particular then you just need to look up the fps rate and processor speed to get this approximately right.

EDIT: If you don't have any external timing requirements then it is normal for an emulator to just run as fast as it possibly can. Sometimes this is a desired effect and sometimes not :)

蝶舞 2024-07-13 21:32:33

我会使用时钟周期来计算时间,然后让它们睡眠时间差。 当然,要做到这一点,你需要一个高分辨率的时钟。 他们这样做会在旋转循环中使 CPU 达到峰值。

I would use the clock cycles to calculate time and them sleep the difference in time. Of course, to do this, you need a high-resolution clock. They way you are doing it is going to spike the CPU in spinning loops.

携余温的黄昏 2024-07-13 21:32:33

是的,正如之前所说,大多数时候您不需要 CPU 模拟器来以与真实设备相同的速度模拟指令。 用户感知的是计算的输出(即音频和视频输出),因此您只需与此类输出同步,这并不意味着您必须具有精确的 CPU 模拟速度。

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

在像 Windows 或 Linux 这样的多任务操作系统上,即使在执行时间上也试图做到周期精确是没有意义的,因为模拟器指令时间(对于老式 80 年代的 CPU 通常为 1uS)和现代操作系统的调度时隙是可比的。

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

Yes, as said before most of the time you don't need a CPU emulator to emulate instructions at the same speed of the real thing. What user perceive is the output of the computation (i.e. audio and video outputs) so you only need to be in sync with such outputs which doesn't mean you must have necessarily an exact CPU emulation speed.

In other words, if the frame rate of the video input is, let's say, 50Hz, then let the CPU emulator run as fast as it can to draw the screen but be sure to output the screen frames at the correct rate (50Hz). From an external point of view your emulator is emulating at the correct speed.

Trying to be cycle exact even in the execution time is a non-sense on a multi-tasking OS like Windows or Linux because the emulator instruction time (tipically 1uS for vintage 80s CPUs) and the scheduling time slot of the modern OS are comparable.

Trying to output something at a 50Hz rate is a much simpler task you can do very good on any modern machine

哭泣的笑容 2024-07-13 21:32:33

如果实现了音频仿真,并且音频输出与系统/CPU 时钟相关,则可以使用另一种选择。 我尤其知道 8 位 Apple ][ 计算机就是这种情况。

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

Another option is available if audio emulation is implemented, and if audio output is tied to the system/CPU clock. In particular I know that this is the case with the 8-bit Apple ][ computers.

Usually sound is generated in buffers of a fixed size (which is a fixed time), so operation (generation of data etc) of these buffers can be tied to CPU throughput via synchronization primitives.

辞慾 2024-07-13 21:32:33

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

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

代码开头是这样的:(我认为)

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

 [email protected] / (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:
 * 
 * 
 * [email protected] 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;
            }
        }
    }
}

Once你有某种类型的外行时钟实现,你可以升级到像计时器这样的东西

/// <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;
        }
    }

I am in the process of making something a little more general use case based, such as the ability to convert time to an estimated amount of instructions and vice versa.

The project homepage is @ http://net7mma.codeplex.com

The code starts like this: (I think)

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

 [email protected] / (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:
 * 
 * 
 * [email protected] 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;
            }
        }
    }
}

Once you have some type of layman clock implementation you advance to something like a 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();
    }

}

Then you can really replicate some logic using something like

 /// <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; }
}

Finally, create something semi useful e.g. a Bus and then perhaps a virtual screen to emit data to the bus...

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;
    }

Here is how I tested the 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;
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文