Pregunta

Estoy tratando de crear un bit de código bien optimizado para crear un número de X dígitos de longitud (donde X se lee desde un archivo de propiedades de tiempo de ejecución), según un número de secuencia generado por DB (Y), que luego se usa una carpeta -name al guardar un archivo.

He encontrado tres ideas hasta ahora, la más rápida es la última, pero agradecería cualquier consejo que la gente pueda tener sobre esto ...

1) Instanciar un StringBuilder con la capacidad inicial X. Agregue Y. Mientras que la longitud <x, inserte un cero en POS cero.

2) Instanciar un StringBuilder con la capacidad inicial X. Mientras que la longitud <x, agregue un cero. Cree un DecimalFormat basado en el valor de StringBuilder y luego formatee el número cuando sea necesario.

3) Cree un nuevo INT de Math.Pow (10, X) y agregue Y. Use String.ValueOf () en el nuevo número y luego subcadra (1).

El segundo obviamente se puede dividir en secciones de bucle y bucle externo.

Entonces, ¿algún consejo? Usando un circuito de 10,000 iteraciones, obtengo tiempos similares de los dos primeros, y el tercer método es aproximadamente diez veces más rápido. ¿Parece esto correcto?

Código de método de prueba completo a continuación ...

    // 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 + "\"" );
¿Fue útil?

Solución

Dejaría de hacer optimizaciones basadas en micro-benchmarks y optaría por algo que se vea elegante. String.format("%0"+numDigits+"d", testNumber)

Otros consejos

Use String.Format ("%0 [Longitud] D", i)

Para la longitud de 8 sería

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

Es más lento, pero el tiempo dedicado a escribir y depurar el código más complejo probablemente superará el tiempo extra total jamás utilizado durante la ejecución.

De hecho, si suma todas las horas hombre que ya gastadas discuten esto, lo más probable es que exceda los ahorros de tiempo de ejecución por un factor grande.

Insertar caracteres de relleno uno por uno es obviamente lento. Si el rendimiento es realmente una gran preocupación, podría usar constantes de cadena predefinidas de Lengts 1..n-1 en su lugar (donde norte es la mayor longitud esperada), almacenada en una lista de matrices en los índices correspondientes.

Si norte es muy grande, al menos aún podrías insertar en trozos más grandes en lugar de caracteres individuales.

Pero en general, como otros señalaron también, la optimización solo es factible si ha perfilado su aplicación en circunstancias reales y ha encontrado qué código específico de código es el cuello de botella. Luego puede concentrarse en eso (y, por supuesto, perfil de nuevo para verificar que sus cambios realmente mejoren el rendimiento).

Aquí hay una solución que es básicamente lo mismo que su StringBuilder con dos optimizaciones:

  1. Se escribe directamente en una matriz que omite la sobrecarga de StringBuilder
  2. Realiza las operaciones en reversa en lugar de insertar (0), que requerirá una arraycopy cada vez

También hace los supuestos de que NumDigits será> = a los caracteres reales requeridos, pero manejará correctamente los números negativos:

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

Este método superó bien sus muestras en mi máquina para negativos y fue comparable para positivos:

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"

Editar: Noté que sus pruebas resolvieron algunos de los objetos dentro del bucle de iteración, que no había hecho en la mía (como no recalcular basenum en la versión de subcadena). Cuando alteré las pruebas para que fueran consistentes (sin resultar ningún objeto / cálculo, mi versión funcionó mejor que la suya:

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"

Por supuesto, como otros han mencionado, el becarro de micro es increíblemente difícil / "fudgy" con toda la optimización realizada por la VM y la incapacidad de controlarlos.

Esto probablemente relacionado Link analiza muchas de las formas de hacerlo. Recomendaría la opción Apache, StringUtils, puede o no ser la más rápida, pero generalmente es una de las más fáciles de entender, y ha tenido el) & ##@ golpeado, por lo que probablemente no se romperá En algún caso de borde imprevisto. ;)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top