65

パフォーマンス (速度) が重要な金融アプリケーションを C# で作成しています。これは金融アプリであるため、Decimal データ型を集中的に使用する必要があります。

プロファイラーの助けを借りて、可能な限りコードを最適化しました。Decimal を使用する前は、すべてが Double データ型で行われており、速度は数倍高速でした。ただし、Double はバイナリの性質のためオプションではなく、複数の操作の過程で多くの精度エラーが発生します。

.NET のネイティブの Decimal データ型よりもパフォーマンスを向上させることができる、C# とやり取りできる 10 進ライブラリはありますか?

すでに得た回答に基づいて、十分に明確ではないことに気付いたので、ここにいくつかの追加の詳細を示します。

  • アプリは可能な限り高速である必要があります (つまり、Decimal の代わりに Double を使用したときと同じくらい高速になるのは夢のようです)。演算はハードウェア ベースであるため、Double は Decimal よりも約 15 倍高速でした。
  • ハードウェアはすでに一流で (私は Dual Xenon Quad-Core で実行しています)、アプリケーションはスレッドを使用するため、マシンの CPU 使用率は常に 100% です。さらに、アプリは 64 ビット モードで実行されているため、32 ビットよりもパフォーマンスが大幅に向上しています。
  • 私は正気のポイントを超えて最適化しました (1 か月半以上の最適化。信じられないかもしれませんが、最初に参照として使用したのと同じ計算を行うのにかかった時間の約 1/5000 かかります)。この最適化には、文字列処理、I/O、データベース アクセスとインデックス、メモリ、ループ、いくつかの処理方法の変更、さらにはあらゆる場所での "if" に対する "switch" の使用など、すべてが含まれていました。プロファイラーは、残りのパフォーマンスの原因が Decimal データ型演算子にあることを明確に示しています。かなりの時間を追加しているものは他にありません。
  • ここで私を信じてください。アプリケーションを最適化するために、C#.NET の領域でできる限りのことを行ってきました。現在のパフォーマンスには本当に驚かされます。Decimal のパフォーマンスを Double に近いものに改善するための良いアイデアを探しています。私はそれがただの夢であることを知っていますが、ただ確認したかったので、可能な限りすべてを考えました. :)

ありがとう!

4

10 に答える 10

44

long データ型を使用できます。確かに、そこに分数を保存することはできませんが、ポンドではなくペニーを保存するようにアプリをコーディングすれば問題ありません。long データ型の精度は 100% であり、膨大な数 (64 ビットの long 型を使用) を扱う場合を除き、問題ありません。

ペニーの格納を強制できない場合は、整数をクラスにラップして使用します。

于 2008-12-14T20:04:21.113 に答える
25

高速である必要があるとのことですが、具体的な速度要件はありますか? そうでない場合は、正気のポイントを超えて最適化することもできます:)

私の隣に座っている友人が提案したように、代わりにハードウェアをアップグレードできますか? コードを書き直すよりもコストがかからない可能性があります。

最も明白なオプションは、10 進数の代わりに整数を使用することです。ここで、1 つの「単位」は「1000 分の 1 セント」のようなものです (または、お望みのとおりです)。それが実現可能かどうかは、最初に小数値に対して実行している操作によって異なります。これを扱うときは非常に注意する必要があります- 間違いを犯しやすいです (少なくともあなたが私のような人なら)。

プロファイラーは、個別に最適化できるアプリケーション内の特定のホットスポットを示しましたか? たとえば、コードの 1 つの小さな領域で多くの計算を行う必要がある場合は、10 進数を整数形式に変換し、計算を行ってから元に戻すことができます。これにより、コードの大部分でAPIを 10 進数に保つことができ、保守が容易になる可能性があります。ただし、顕著なホットスポットがない場合、それは実現できない可能性があります。

プロファイリングと速度が明確な要件であることを伝えるための+1、ところで:)

于 2008-12-14T19:55:15.690 に答える
8

問題は基本的に double/float がハードウェアでサポートされているのに対し、Decimal などはサポートされていないことです。つまり、速度 + 限られた精度と、より高い精度 + パフォーマンスの低下のどちらかを選択する必要があります。

于 2008-12-14T19:16:36.110 に答える
3

SSE2命令が.NET10進値で簡単に機能するとは思いません。.NET10進データ型は128ビット10進浮動小数点型ですhttp://en.wikipedia.org/wiki/Decimal128_floating-point_format、SSE2命令は128ビット整数型で動作します。

于 2010-06-24T03:14:41.083 に答える
2

古い質問ですが、それでも非常に有効です。

Long を使用するという考えを裏付けるいくつかの数値を次に示します。

100'000'000 回の追加にかかった時間

Long     231 mS
Double   286 mS
Decimal 2010 mS

簡単に言えば、10 進数は、Long や Double よりも 10 倍遅くなります。

コード:

Sub Main()
    Const TESTS = 100000000
    Dim sw As Stopwatch

    Dim l As Long = 0
    Dim a As Long = 123456
    sw = Stopwatch.StartNew()
    For x As Integer = 1 To TESTS
        l += a
    Next
    Console.WriteLine(String.Format("Long    {0} mS", sw.ElapsedMilliseconds))

    Dim d As Double = 0
    Dim b As Double = 123456
    sw = Stopwatch.StartNew()
    For x As Integer = 1 To TESTS
        d += b
    Next
    Console.WriteLine(String.Format("Double  {0} mS", sw.ElapsedMilliseconds))

    Dim m As Decimal = 0
    Dim c As Decimal = 123456
    sw = Stopwatch.StartNew()
    For x As Integer = 1 To TESTS
        m += c
    Next
    Console.WriteLine(String.Format("Decimal {0} mS", sw.ElapsedMilliseconds))

    Console.WriteLine("Press a key")
    Console.ReadKey()
End Sub
于 2013-05-16T10:34:06.223 に答える
1

MMX / SSE / SSE2はどうですか?

私はそれが役立つと思います...だから...10進数は128ビットデータ型であり、SSE2も128ビットです...そしてそれは1CPUティックで10進数を追加、サブ、div、マルチすることができます...

VC ++を使用してSSE2用のDLLを記述し、そのDLLをアプリケーションで使用できます。

例//あなたはこのようなことをすることができます

VC ++

#include <emmintrin.h>
#include <tmmintrin.h>

extern "C" DllExport __int32* sse2_add(__int32* arr1, __int32* arr2);

extern "C" DllExport __int32* sse2_add(__int32* arr1, __int32* arr2)
{
    __m128i mi1 = _mm_setr_epi32(arr1[0], arr1[1], arr1[2], arr1[3]);
    __m128i mi2 = _mm_setr_epi32(arr2[0], arr2[1], arr2[2], arr2[3]);

    __m128i mi3 = _mm_add_epi32(mi1, mi2);
    __int32 rarr[4] = { mi3.m128i_i32[0], mi3.m128i_i32[1], mi3.m128i_i32[2], mi3.m128i_i32[3] };
    return rarr;
}

C#

[DllImport("sse2.dll")]
private unsafe static extern int[] sse2_add(int[] arr1, int[] arr2);

public unsafe static decimal addDec(decimal d1, decimal d2)
{
    int[] arr1 = decimal.GetBits(d1);
    int[] arr2 = decimal.GetBits(d2);

    int[] resultArr = sse2_add(arr1, arr2);

    return new decimal(resultArr);
}
于 2009-08-03T09:28:07.100 に答える
1

スタックオーバーフローを開始したばかりなので、まだコメントしたり反対票を投じることはできません。alexsmart に関する私のコメント (2008 年 12 月 23 日 12:31 に投稿) は、Round(n/precision, precision) という式 (n は int で、precisions は long) は、彼の考えを実行しないということです。

1) n/precision は整数除算を返します。つまり、既に丸められますが、小数を使用することはできません。丸めの動作も Math.Round(...) とは異なります。

2) コード " return Math.Round(n/precision, precision).ToString() " は、Math.Round(double, int) と Math.Round(decimal, int) の間のあいまいさのためにコンパイルされません。10 進数にキャストする必要があるため (金融アプリであるため、2 倍ではありません)、最初から 10 進数を使用することもできます。

3 )精度が 4 である n/精度は、小数点以下 4 桁に切り捨てられず、4 で除算されます。おそらくあなたが望んでいたのは、1235000 を取得することです (末尾の 567 から 4 桁の精度に丸められます)。Math.Round では、固定精度ではなく、固定小数点に丸めることができることに注意してください。

更新: コメントを追加できるようになりましたが、これをコメント エリアに入れるには十分なスペースがありません。

于 2008-12-29T22:07:50.463 に答える