Domanda

Am tentativo di creare un po 'ben ottimizzato di codice per creare il numero di X-cifre di lunghezza (dove X viene letto da un file di proprietà operazionali), sulla base di un numero di sequenza DB-generato (Y), che viene poi utilizzata una cartella di nome quando si salva un file.

Sono venuto su con tre idee finora, il più veloce dei quali è l'ultimo, ma Apprezzerei tutto le persone possono avere consigli su questo ...

1) Instantiate uno StringBuilder con capacità iniziale X. accodamento Y. Mentre lunghezza

2) Instantiate uno StringBuilder con capacità iniziale X. Mentre lunghezza

3) Creare una nuova int di Math.pow (10, X) e aggiungere Y. Usa String.valueOf () sul nuovo numero e poi sottostringa (1) esso.

Il secondo può ovviamente essere suddiviso in sezioni fuori-ciclo e dentro ad anello.

Quindi, qualche consiglio? Utilizzando un ciclo for di 10.000 iterazioni, sto diventando tempi simili dai primi due, e il terzo metodo è di circa dieci volte più veloce. Vi sembra giusto?

completa il codice di prova-metodo di sotto ...

    // 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 + "\"" );
È stato utile?

Soluzione

Vorrei smettere di fare ottimizzazioni sulla base di micro-benchmark e andare per qualcosa che sembra elegante codewise, come ad esempio String.format("%0"+numDigits+"d", testNumber)

Altri suggerimenti

Usa String.Format ( "% 0 [lunghezza] d", i)

Per lunghezza di 8 sarebbe

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

E 'più lento, ma il tempo trascorso digitazione e il debug del codice più complesso probabilmente supererà il tempo supplementare totale mai utilizzato durante l'esecuzione.

In realtà, se si sommano tutte le ore-uomo già speso discutere di questo, è molto probabile che supera il risparmio di tempo di esecuzione da un grande fattore.

caratteri di riempimento Inserimento uno per uno è ovviamente lento. Se le prestazioni sono davvero un grosso problema, è possibile utilizzare le costanti di stringa predefinito di lengts 1..N-1 invece (dove n è il più grande durata prevista), memorizzato in un ArrayList gli indici corrispondenti.

Se n è molto grande, almeno si potrebbe ancora inserire in grandi pezzi, invece di singoli caratteri.

Ma nel complesso, come altri hanno sottolineato anche l'ottimizzazione è possibile solo se si è profilata l'applicazione in circostanze reali e trovato che parte specifica del codice è il collo di bottiglia. Quindi è possibile concentrarsi su questo (e di profilo naturalmente ancora una volta per verificare che le modifiche apportate in realtà migliorare le prestazioni).

Ecco una soluzione che è fondamentalmente la stessa cosa come StringBuilder con due ottimizzazioni:

  1. Si scrive direttamente in un array bypassando l'overhead StringBuilder
  2. Lo fa le operazioni in senso inverso invece di inserto (0), che requries un arraycopy ogni volta

E 'anche le assunzioni che saranno numDigits> = per i caratteri effettivi richiesti, ma saranno di gestire correttamente i numeri negativi:

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 + "\"" );

Questo metodo ben ha superato i campioni sulla mia macchina per i negativi e era paragonabile per i positivi:

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"

Modifica Ho notato che i test resued alcuni degli oggetti all'interno del ciclo di iterazione, che non avevo fatto in miniera (tale da non ricalcolare BaseNum nella versione stringa). Quando ho alterato il test per essere coerente (non resuing qualsiasi oggetto / calcoli mia versione eseguita migliore della tua:

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"

Naturalmente come altri hanno detto micro benchmarking è incredibilmente difficile / "fudgy" con tutta l'ottimizzazione eseguita dalla VM e l'incapacità di controllarli.

Questo probabilmente correlati discute link molti dei modi per farlo esso. Mi sento di raccomandare l'opzione Apache, StringUtils, può o non può essere l'assoluto più veloce, ma il suo solito uno dei più facili da capire, e ha avuto la) & ## @ pestate fuori di esso, in modo che probabilmente non si rompe in qualche caso limite imprevisto. ;)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top