19

特定のメソッドのパフォーマンス測定を行いたいのですが、完了するまでにかかる時間を平均したいと思います。(これはC#Winformsアプリケーションですが、この質問は他のフレームワークにも当てはまる可能性があります。)

メソッドの開始時にリセットし、終了時に停止するストップウォッチがあります。最後の10個の値をリストまたは配列に格納したいと思います。新しい値を追加するたびに、最も古い値がリストから削除されます。

定期的に、保存されているすべての値を平均化する別のメソッドを呼び出します。

この構成は循環バッファーであると考えるのは正しいですか?

最適なパフォーマンスでこのようなバッファを作成するにはどうすればよいですか?今私は次のものを持っています:

List<long> PerfTimes = new List<long>(10);

// ...

private void DoStuff()
{
    MyStopWatch.Restart();
    // ...
    MyStopWatch.Stop();
    PerfTimes.Add(MyStopWatch.ElapsedMilliseconds);
    if (PerfTimes.Count > 10) PerfTimes.RemoveAt(0);
}

これはどういうわけか非効率に思えますが、おそらくそうではありません。

提案?

4

8 に答える 8

22

カスタム コレクションを作成できます。

class SlidingBuffer<T> : IEnumerable<T>
{
    private readonly Queue<T> _queue;
    private readonly int _maxCount;

    public SlidingBuffer(int maxCount)
    {
        _maxCount = maxCount;
        _queue = new Queue<T>(maxCount);
    }

    public void Add(T item)
    {
        if (_queue.Count == _maxCount)
            _queue.Dequeue();
        _queue.Enqueue(item);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _queue.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

現在のソリューションは機能しますが、 a の最初の項目を削除するList<T>とコストがかかるため、非効率的です。

于 2011-06-17T22:54:34.420 に答える
10
private int ct = 0;
private long[] times = new long[10];

void DoStuff ()
{
   ...
   times[ct] = MyStopWatch.ElapsedMilliseconds;
   ct = (ct + 1) % times.Length; // Wrap back around to 0 when we reach the end.
}

これは単純な円形構造です。これには、他のソリューションにあるリンク リスト ノードの配列のコピーやガベージ コレクションは必要ありません。

于 2011-06-17T23:33:00.213 に答える
3

最適なパフォーマンスを得るには、おそらくリストではなく long の配列を使用できます。

ある時点で、ダウンロード時間推定器を実装するために同様の要件があり、最後の 1N秒間の速度を格納するために循環バッファーを使用しました。

全体のダウンロード速度には関心がありませんでした。最近のアクティビティに基づいて予想されるおおよその時間でしたが、数値がいたるところで跳ね上がるほど最近ではありませんでした (それを計算する最後の秒)。

時間枠全体に関心がなかった理由は、ダウンロードが 30 分で 1M/s になり、次の 10 分間で 10M/s に切り替わる可能性があるためです。最初の 30 分間は、ダウンロードが非常に高速であるにもかかわらず、平均速度が大幅に低下します。

各セルが 1 秒間にダウンロードされる量を保持する循環バッファーを作成しました。循環バッファーのサイズは 300 で、5 分間の履歴データを許容し、すべてのセルはゼロに初期化されました。あなたの場合、必要なセルは 10 個だけです。

また、合計 (バッファー内のすべてのエントリの合計なので、最初はゼロ) とカウント (最初は明らかにゼロ) も維持しました。

毎秒、最後の 1 秒以降にダウンロードされたデータの量を把握し、次のようにします。

  • 合計から現在のセルを減算します。
  • 現在の数値をそのセルに入れ、セル ポインターを進めます。
  • その現在の数字を合計に追加します。
  • まだ 300 になっていない場合は、カウントを増やします。
  • 合計/カウントに基づいて、ユーザーに表示される数値を更新します。

基本的に、疑似コードで:

def init (sz):
    buffer = new int[sz]
    for i = 0 to sz - 1:
        buffer[i] = 0 
    total = 0
    count = 0
    index = 0
    maxsz = sz

def update (kbps):
    total = total - buffer[index] + kbps   # Adjust sum based on deleted/inserted values.
    buffer[index] = kbps                   # Insert new value.
    index = (index + 1) % maxsz            # Update pointer.
    if count < maxsz:                      # Update count.
        count = count + 1
    return total / count                   # Return average.

これは、独自の要件に簡単に適応できるはずです。合計は、情報を「キャッシュ」する優れた機能であり、コードをさらに高速化する可能性があります。つまり、合計または平均を計算する必要がある場合は、データが変化したときにのみ、必要最小限の計算を使用して計算できます。

別の方法は、要求されたときに 10 個の数値すべてを合計する関数で、別の値をバッファーにロードするときに単一の減算/加算よりも遅くなります。

于 2011-06-17T22:55:06.983 に答える
1

代わりに Queue データ構造の使用を検討することをお勧めします。単純な線形リストを使用できますが、完全に非効率的です。円形配列を使用できますが、その場合は常にサイズを変更する必要があります。したがって、キューを使用することをお勧めします。

于 2011-06-17T22:57:03.173 に答える
0

私には問題ないようです。代わりに LinkedList を使用するのはどうですか? List を使用する場合、最初の項目を削除すると、他のすべての項目を 1 つの項目に戻す必要があります。LinkedList を使用すると、リスト内の任意の場所にアイテムをほとんどコストをかけずに追加または削除できます。ただし、10 項目しか話していないので、これがどの程度の違いになるかはわかりません。

リンクされたリストのトレードオフは、リストのランダムな要素に効率的にアクセスできないことです。これは、リンクされたリストは、必要なものに到達するまで、各項目を渡しながらリストに沿って基本的に「歩く」必要があるためです。ただし、シーケンシャル アクセスの場合は、リンクされたリストで問題ありません。

于 2011-06-17T22:49:44.913 に答える