2

これに似た方法を使用して、10進値のコレクションを要約する方法があります...

Dim _amountCollection as New List(Of Decimal)
_amountCollection.Add(145.12D)
_amountCollection.Add(11.32D)
_amountCollection.Add(6547.07D)

Dim _totalAmount as Decimal

For Each _individualAmount as Decimal in _amountCollection
  _totalAmount += _individualAmount
Next 

実際のコードでは、通常、金額コレクションにはより多くのメンバーがあり、合計操作で合計する必要がある少なくとも50の個別の金額コレクションがあります。

この合計量の再計算は頻繁に呼び出され(少なくとも1回、コレクションコンテンツの変更ごとに再度)、合計操作時間の2〜5%を消費するものとしてプロファイルトレースに表示されます。この合計操作を高速化する方法を誰かが知っているかどうか、またはこれが最も高速であるかどうかを確認したいと思います。

この場合、金額を再計算する必要があるため、キャッシングは現実的ではありません。

****編集**RavadreとJoelの場合-合計金額はクラスレベルで保存されます(各金額のコレクションと合計はクラスインスタンスに含まれます)

何か案は?

4

9 に答える 9

3

List(Of Decimal) をサブクラス化し、TotalSum プロパティを公開してみてください。Add() が呼び出されるたびに、挿入された値だけインクリメントします。これにより、合計を取得するためにリストを反復処理する必要がなくなります。

編集: 試した後、List のメソッドが仮想ではないため、IList(Of Decimal) を実装することをお勧めします。これに沿ったもの(はい、C#だと知っています...)

public class DecimalSumList : IList<decimal>
{
    readonly IList<decimal> _allValues = new List<decimal>();
    decimal _totalSum;
    public decimal TotalSum { get { return _totalSum; } }
    public void Add(decimal item)
    {
        _totalSum += item;
        _allValues.Add(item);
    }

    public void CopyTo(decimal[] array, int arrayIndex)
    {
        _allValues.CopyTo(array, arrayIndex);
    }

    bool ICollection<decimal>.Remove(decimal item)
    {
        _totalSum -= item;
        return _allValues.Remove(item);
    }

    public int Count
    {
        get { return _allValues.Count; }
    }

    public bool IsReadOnly
    {
        get { throw new NotImplementedException(); }
    }

    public void Remove(decimal item)
    {
        _totalSum -= item;
        _allValues.Remove(item);
    }
    public void Clear()
    {
        _totalSum = 0;
        _allValues.Clear();
    }

    public bool Contains(decimal item)
    {
        return _allValues.Contains(item);
    }

    public IEnumerator<decimal> GetEnumerator()
    {
        return _allValues.GetEnumerator();
    }

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

    public int IndexOf(decimal item)
    {
        return _allValues.IndexOf(item);
    }

    public void Insert(int index, decimal item)
    {
        _totalSum += item;
        _allValues.Insert(index,item);
    }

    public void RemoveAt(int index)
    {
        _totalSum -= _allValues[index];
        _allValues.RemoveAt(index);
    }

    public decimal this[int index]
    {
        get { return _allValues[index]; }
        set { _allValues[index] = value; }
    }
}
于 2009-08-06T14:11:23.893 に答える
2

数字のリストを順番に追加するよりも速く追加することをどのように期待するかは、私には明らかではありません!

ただし、decimals を使用して演算を行うと、浮動小数点型を使用するよりもはるかに遅くなる可能性があるため、実際に精度が必要ない場合は、切り替えることをお勧めします。

于 2009-08-06T14:05:51.483 に答える
2

これらが通貨値である場合は、スケーリングされた整数の使用を検討してください。小数点以下 2 桁の場合は、ドルの代わりにペニーの # を格納し、結果を出力する必要があるときに 100 で割ります。小数点以下 5 桁の場合、変数は元の数値 * 100000 を内部的に使用します。これにより、特に除算を行う場合に、パフォーマンスと精度の両方が向上するはずです。

于 2009-08-06T17:11:53.860 に答える
1

どうtotalAmount = amountCollection.Sum()ですか?

于 2009-08-06T14:04:02.530 に答える
1

合計操作で合計する必要がある個別の金額コレクションが少なくとも 50 あります。

個々のコレクションの合計をキャッシュするのはどうですか? その後、コレクションが変更されたときに、そのコレクションとキャッシュされた合計を合計するだけで済みます。

于 2009-08-06T14:09:30.323 に答える
1

合計プロファイル トレース時間の 2 ~ 5% は、それほど多くはありません。

このメソッドの実行時間を半分に短縮したとしても、合計実行時間のせいぜい数パーセントしか節約できません。

あなたの最善の策は、おそらく最初に他の場所を探すことです.

于 2009-08-06T14:11:40.710 に答える
0

これは、List(Of T).

Sum他の人は、拡張メソッドを使用することを提案しています-その実装は問題に対する現在の解決策と同じであるため、これはパフォーマンスを向上させません:

<Extension> _
Public Shared Function Sum(ByVal source As IEnumerable(Of Decimal)) As Decimal
    If (source Is Nothing) Then
        Throw Error.ArgumentNull("source")
    End If
    Dim num As Decimal = 0
    Dim num2 As Decimal
    For Each num2 In source
        num = (num + num2)
    Next
    Return num
End Function
于 2009-08-06T14:02:45.153 に答える
0

あなたの他の選択肢はforループを使用しています。列挙子を作成する必要はありませんが、実際には(私が知る限り)大幅なブーストはありません。

結果をキャッシュできないのはなぜですか? 総量をキャッシュする場合、要素の 1 つを変更するたびに、同じ値を使用して量を変更します (最終的には、同期のために 10 回の変更ごとにすべて計算します)。したがって、要素の 1 つが 2.5 から 3.6 に変更された場合、合計金額に 1.1 を追加するだけです。あなたの場合、なぜこれがうまくいかないのですか?

于 2009-08-06T14:05:50.110 に答える
0

statenjason が提案した IList/IDictionary のバージョンを実装したくない場合は、ラッパーを作成できます。

Public Class TotalDictionaryWrapper
        Private innerDictionary As IDictionary(Of Integer, Decimal)
        Private m_total As Decimal = 0D
        
        Public Sub New(ByVal dictionary As IDictionary(Of Integer, Decimal))
            Me.innerDictionary = dictionary
        End Sub
        
        Public ReadOnly Property Total() As [Decimal]
            Get
                Return Me.m_total
            End Get
        End Property
        
        Public Sub Add(ByVal key As Integer, ByVal value As Decimal)
            Me.innerDictionary.Add(key, value)
            m_total += value
        End Sub
        
        Public Sub Remove(ByVal key As String)
            Dim toRemove As Decimal = Me.innerDictionary(key)
            Me.innerDictionary.Remove(key)
            Me.m_total -= toRemove
        End Sub
        
        Public Sub Update(ByVal key As Integer, ByVal newValue As Decimal)
            Dim oldValue As Decimal = Me.innerDictionary(key)
            Me.innerDictionary(key) = newValue
            Me.m_total -= oldValue
            Me.m_total += newValue
        End Sub
        
        Other methods..

    End Class
于 2009-08-06T15:35:58.173 に答える