2

DBで生成されたシーケンス番号(Y)に基づいて、長さがX桁の数(Xはランタイムプロパティファイルから読み取られます)を作成するために、適切に最適化されたコードを作成しようとしています。これは、フォルダーで使用されます。 -ファイルを保存するときの名前。

私はこれまでに3つのアイデアを思いつきましたが、そのうちの最も速いものが最後のアイデアですが、これについて人々からのアドバイスをいただければ幸いです...

1)初期容量XでStringBuilderをインスタンス化します。Yを追加します。長さがX未満の場合、位置ゼロにゼロを挿入します。

2)初期容量XでStringBuilderをインスタンス化します。長さがX未満の場合は、ゼロを追加します。StringBuilder値に基づいてDecimalFormatを作成し、必要に応じて数値をフォーマットします。

3)Math.pow(10、X)の新しいintを作成し、Yを追加します。新しい番号にString.valueOf()を使用してから、substring(1)を使用します。

2つ目は、明らかに外側ループと内側ループのセクションに分割できます。

だから、何かヒントはありますか?10,000回の反復のforループを使用すると、最初の2つから同様のタイミングが得られ、3番目の方法は約10倍高速です。これは正しいようですか?

以下の完全なテストメソッドコード...

    // Setup test variables
    int numDigits = 9;
    int testNumber = 724;
    int numIterations = 10000;
    String folderHolder = null;
    DecimalFormat outputFormat = new DecimalFormat( "#,##0" );

    // StringBuilder test
    long before = System.nanoTime();
    for ( int i = 0; i < numIterations; i++ )
    {
        StringBuilder sb = new StringBuilder( numDigits );
        sb.append( testNumber );
        while ( sb.length() < numDigits )
        {
            sb.insert( 0, 0 );
        }

        folderHolder = sb.toString();
    }
    long after = System.nanoTime();
    System.out.println( "01: " + outputFormat.format( after - before ) + " nanoseconds" );
    System.out.println( "Sanity check: Folder = \"" + folderHolder + "\"" );

    // DecimalFormat test
    before = System.nanoTime();
    StringBuilder sb = new StringBuilder( numDigits );
    while ( sb.length() < numDigits )
    {
        sb.append( 0 );
    }
    DecimalFormat formatter = new DecimalFormat( sb.toString() );
    for ( int i = 0; i < numIterations; i++ )
    {
        folderHolder = formatter.format( testNumber );
    }
    after = System.nanoTime();
    System.out.println( "02: " + outputFormat.format( after - before ) + " nanoseconds" );
    System.out.println( "Sanity check: Folder = \"" + folderHolder + "\"" );

    // Substring test
    before = System.nanoTime();
    int baseNum = (int)Math.pow( 10, numDigits );
    for ( int i = 0; i < numIterations; i++ )
    {
        int newNum = baseNum + testNumber;
        folderHolder = String.valueOf( newNum ).substring( 1 );
    }
    after = System.nanoTime();
    System.out.println( "03: " + outputFormat.format( after - before ) + " nanoseconds" );
    System.out.println( "Sanity check: Folder = \"" + folderHolder + "\"" );
4

5 に答える 5

8

マイクロベンチマークに基づく最適化をやめ、コード的にエレガントに見えるものを選びます。String.format("%0"+numDigits+"d", testNumber)

于 2010-06-07T13:11:04.620 に答える
2

String.format( "%0 [length] d"、i)を使用します

8の長さの場合は

String out = String.format("%08d", i);

速度は遅くなりますが、より複雑なコードの入力とデバッグに費やされる時間は、実行中にこれまでに使用された合計余分な時間を超える可能性があります。

実際、これについて話し合うためにすでに費やしたすべての工数を合計すると、実行時間の節約を大幅に上回っている可能性があります。

于 2010-06-07T13:11:00.830 に答える
1

パディング文字を1つずつ挿入するのは明らかに遅いです。パフォーマンスが本当に大きな懸念事項である場合は、代わりにlengts 1..n-1の事前定義された文字列定数(nは予想される最大の長さ)を使用して、対応するインデックスのArrayListに格納できます。

nが非常に大きい場合でも、少なくとも1文字ではなく大きなチャンクに挿入できます。

しかし、全体として、他の人も指摘しているように、最適化は、実際の状況でアプリケーションのプロファイルを作成し、どの特定のコードがボトルネックであるかを見つけた場合にのみ実行可能です。次に、それに焦点を当てることができます(もちろん、変更によって実際にパフォーマンスが向上することを確認するために、再度プロファイルを作成します)。

于 2010-06-07T13:06:16.370 に答える
1

これは、2つの最適化を備えたStringBuilderと基本的に同じソリューションです。

  1. StringBuilderオーバーヘッドをバイパスして配列に直接書き込みます
  2. 毎回アレイコピーを必要とするinsert(0)の代わりに、逆の操作を行います

また、numDigitsが実際に必要な文字に対して> =であると想定しますが、負の数を適切に処理します。

before = System.nanoTime();
String arrString=null;
for ( int j = 0; j < numIterations; j++ ){
  char[] arrNum = new char[numDigits];
  int i = numDigits-1;
  boolean neg = testNumber<0;
  for(int tmp = neg?-testNumber:testNumber;tmp>0;tmp/=10){
    arrNum[i--] = (char)((tmp%10)+48);
  }
  while(i>=0){
    arrNum[i--]='0';
  }
  if(neg)arrNum[0]='-';
  arrString = new String(arrNum);
}
after = System.nanoTime();
System.out.println( "04: " + outputFormat.format( after - before ) + " nanoseconds" );
System.out.println( "Sanity check: Folder = \"" + arrString + "\"" );

この方法は、ネガティブの場合は私のマシンのサンプルをはるかに上回り、ポジティブの場合は同等でした。

01: 18,090,933 nanoseconds
Sanity check: Folder = "000000742"
02: 22,659,205 nanoseconds
Sanity check: Folder = "000000742"
03: 2,309,949 nanoseconds
Sanity check: Folder = "000000742"
04: 6,380,892 nanoseconds
Sanity check: Folder = "000000742"

01: 14,933,369 nanoseconds
Sanity check: Folder = "0000-2745"
02: 21,685,158 nanoseconds
Sanity check: Folder = "-000002745"
03: 3,213,270 nanoseconds
Sanity check: Folder = "99997255"
04: 1,255,660 nanoseconds
Sanity check: Folder = "-00002745"

編集:あなたのテストが反復ループ内のいくつかのオブジェクトを再実行したことに気づきました。これは私が行っていなかったものです(サブストリングバージョンでbaseNumを再計算しないなど)。テストを一貫性のあるものに変更したとき(オブジェクト/計算を再実行しないで、私のバージョンはあなたのバージョンよりもパフォーマンスが良かった:

01: 18,377,935 nanoseconds
Sanity check: Folder = "000000742"
02: 69,443,911 nanoseconds
Sanity check: Folder = "000000742"
03: 6,410,263 nanoseconds
Sanity check: Folder = "000000742"
04: 996,622 nanoseconds
Sanity check: Folder = "000000742"

もちろん、他の人が言及しているように、マイクロベンチマークは、VMによって実行されるすべての最適化と、それらを制御できないため、非常に困難で「曖昧」です。

于 2010-06-07T14:09:41.210 に答える
0

このおそらく関連するリンクでは、それを行うための多くの方法について説明しています。ApacheオプションのStringUtilsをお勧めします。これは絶対的に最速である場合とそうでない場合がありますが、通常は最も理解しやすいオプションの1つであり、)&## @が削除されているため、おそらく壊れることはありません。予期しないエッジの場合。;)

于 2010-06-07T14:06:27.420 に答える