2

555 タイマー IC によって生成されたパルスの周波数とパルス幅を計算するプログラムを開発している間、PC パラレル ポート経由で PC に到達します。コードを実行するたびに異なる値が表示されることに気付いたので、ループとタイマーの精度をテストし始めました。次のコードを実行したところ、不正確であることがわかりました (間違っている可能性があります。間違っている場合は訂正してください!)。

タイマーの場合:

    int sec = 0;
    private void button2_Click(object sender, EventArgs e)
    {
        sec = DateTime.Now.Second;
        i = 0;
        timer1.Enabled = true;
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        if (sec == DateTime.Now.Second)
        {
            i++;
        }
        else
        {
            timer1.Enabled = false;
            MessageBox.Show(i.ToString(),"Timer Output");
        }
    }

出力: 同じはずですが:

ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力

ループの場合:

    private void button1_Click(object sender, EventArgs e)
    {
        i = 0;
        CheckForIllegalCrossThreadCalls = false;
        Thread t1 = new Thread(LoopTest);
        t1.Start();
    }

    void LoopTest()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        this.Cursor = Cursors.WaitCursor;
        while (true)
        {
            if (sw.ElapsedMilliseconds != 1000)
            {
                i++;
            }
            else
            {
                break;
            }
        }
        sw.Stop();
        this.Cursor = Cursors.Default;
        MessageBox.Show(i.ToString(), "Loop Output");
    }

出力: 同じはずですが:

ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力

ループとタイマーを正確にするにはどうすればよいですか?それを行う方法はありますか? それとも、ハードで複雑な C コードと DOS に行かなければなりませんか?

この質問で間違った値を取得する基本的な理由だと思います:パラレルポートの入力周波数を数える - C#

4

4 に答える 4

16

1)DateTime.Nowパフォーマンス測定には使用せず、 を使用してくださいStopWatch

2)「OUTPUT: Should be same, but ..

なぜ彼らはすべきですか?非 RTOS (リアルタイム オペレーティング システム) でマネージ/JIT されたコードを実行しています。OS がそのように感じた場合、コードはいつでもバウンスされる可能性があります。この環境で同じコードを N 回実行すると、常に同じ結果が得られると考えるに至った理由は何ですか?

3) Windows のタイマーの分解能は ~15ms です。非常に正確なタイミングを得るための最善の策は、それをサポートするシステム (CPU) 上の HighPerformanceTimer API です。タイマーの間隔さえ示していません。

ここで多くの変数を考慮に入れておらず、誤った仮定に基づいて予測を行っています。このコードを何回測定していますか? 初めてコンパイルするのに必要な時間を考慮していますか?リリースモードで実行していますか? VSを通して?バックグラウンドで多くのタスクが実行されていますか? 私は続けることができました。

于 2012-07-17T21:32:57.720 に答える
12

慎重に実装することで、ほとんどの Windows プラットフォームでわずか数マイクロ秒の精度で期間を測定できます。次の事実に注意してください。

  1. Windows はリアルタイム OS ではありません。ここでは問題ありません。

  2. プロセス/スレッドの優先順位利用REALTIME_PRIORITY_CLASSします。呼び出し元のスレッドがビジー状態の間、これらの優先度によってシステムがロックされる可能性があるため、安全なコードを用意してください。(Process.PriorityClass と Thread.Priority は、必要なレベルまで優先度を上げることができません。)THREAD_PRIORITY_TIME_CRITICAL

  3. マルチメディア タイマーを使用して、システムの割り込み周期と時間更新間隔を増やすことができます。(これらのマルチメディア タイマー関数をラップするさまざまな.NET プロジェクトがあります。)

  4. マルチコア システムでは、コアの選択も精度に影響します。タイマー イベントを待機しているスレッドを強制的に Processor0 で待機させると有利です。SetThreadAffinityMask関数を使用すると、スレッドを特定の CPU にバインドできます。(.Net アプリケーションについては、Thread.ProcessorAffinityを参照してください。)

  5. QueryPerformanceCounterQueryPerformanceFrequencyを、高頻度の時間測定のリソースとして使用します。(.Net アプリケーションについては、「 QueryPerfCounter ラッパー クラスの作成」を参照してください)

  6. パフォーマンス カウンターの周波数の値が調整されていることを確認します。によって返されるQueryPerformanceFrequency値は、オフセットと熱ドリフトによって、観測された値から逸脱します。これにより、多くのユーザーのエラーが発生する可能性があります。このような調整を行う方法については、 Windows タイムスタンプ プロジェクトを参照してください。

そして: はい、ハードコーディングが必要になる場合があります。しかし、マイクロ秒のタイミングは、Windows プラットフォームで非常に信頼性が高く観察できます。

注: Windows はリアルタイム オペレーティング システムではありません。しかし、Windows で利用できるタイマーは非常に正確です。彼らはやるべきことを正確に行い、非常に高い精度でそれを行います。Windows タイマーとその精度について多くの不満があるのは、その動作が基盤となるハードウェアに大きく依存しているという事実です。これが、ドキュメントに多くの欠陥がある理由でもあります。個々のタイム サービス機能を見つけるために、ハードウェアを診断することを強くお勧めします。残念ながら、これにより、どのプログラムにも、プラットフォームに依存しない余分なコード行がいくつか含まれます。

于 2012-07-18T08:44:18.523 に答える
2

まず第一に、button2_click が呼び出された時点が現在の秒のどのくらいの時間かわかりません。したがって、MessageBox が表示されるまでの残り時間は基本的にランダムです。

第 2 に、ループが一定時間内に取得する CPU サイクル数は、精度とは関係ありません。

特定のスレッドが取得するサイクル数は、システムで他に何が起こっているかによって異なります。システムが一連のサイクル全体を別のプロセスに移動する必要があると判断した場合、スレッドはしばらくの間「枯渇」します。

あなたが本当にやろうとしていることを詳しく説明して、誰かがアドバイスをくれるかもしれません。

于 2012-07-17T21:34:20.873 に答える
1

Ed の答えは正しいです。Windows のタイマーは正確ではありません。確かに、ハードウェアで生成された信号を測定するには正確ではありません。

しかし、周波数とパルス幅を測定する必要がある場合は、100 ほどのサンプルを取得して平均化します。おそらく次のようになります:

private StopWatch _Sw = new StopWatch();
private List<TimeSpan> _Samples = new List<TimeSpan>();
private Timer _Timer = new Timer(TimerTick, TimeSpan.FromSeconds(1));
private const int RequiredSamples = 100;

private void StartSampling()
{
    // You can change this next line to PriorityClass.RealTime if you're careful.
    System.Diagnostics.Process.GetCurrentProcess().BasePriority 
                              = PriorityClass.High;
    _Samples.Capacity = RequiredSamples;
    _Timer.Start();
    _Sw.Start();
    Hook555Timer(On555Pulse);
}

private void On555Pulse(object sender, EventArgs e)
{
    _Sample.Add(_Sw.Elapsed);
}

private void TimerTick(object sender, EventArgs e)
{
    if (_Samples.Count > RequiredSamples)
    {
        System.Diagnostics.Process.GetCurrentProcess().BasePriority 
                                    = PriorityClass.Normal;
        _Timer.Stop();
        _Sw.Stop();
        UnHook555Timer(On555Pulse);

        // You can now use the time between each TimeSpan 
        // in _Samples to determine statistics about your timer.
        // Eg: Min / Max duration, average and median duration.
        var durations = _Samples
                .Zip(_Samples.Skip(1), 
                    (a,b) => new { First = a, Second = b } )
                .Select(pair => pair.Second.Subtract(pair.First));
        var minTime = durations.Min(ts => ts.TotalMilliseconds);
        var maxTime = durations.Max(ts => ts.TotalMilliseconds);
        var averageTime = durations.Average(ts => ts.TotalMilliseconds);
        // I don't think LINQ has a Median() aggregate out of the box.
        // Some comment about "an exercise for the reader" goes here.
        var medianTime = durations.Median(ts => ts.TotalMilliseconds);

        var frequency = _Samples.Last()
                                 .Subtract(_Samples.First())
                                      .TotalSeconds / _Samples.Count;
    }
}

(注: コードはメモ帳に書かれており、さらに変更しないと動作しない可能性があります)

私の精度は、StopWatchではなく によって決定されるようになりました(システムでは よりも正確ではない可能性があることにTimer注意してください。 プロパティとプロパティを確認してください)。また、サンプリング中にプロセスの優先順位を上げて、他のプロセスが私のプロセスを横取りするのを最小限に抑えています。StopwatchTimerFrequencyIsHighResolution

それでも、Windows は RTOS ではないため、できることは、多くのサンプルを取得し、いくつかの統計を使用して、答えの概算を取得することです。

于 2012-07-17T23:15:05.533 に答える