Ho il seguente codice che esegue uno spostamento circolare dei bit della matrice:Perché l'inversione del ciclo lo rende più lento?
private static void method1(byte[] bytes) {
byte previousByte = bytes[0];
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((bytes[bytes.length - 1] & 0xff) << 7));
for (int i = 1; i < bytes.length; i++) {
byte tmp = bytes[i];
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((previousByte & 0xff) << 7));
previousByte = tmp;
}
}
Poi ho pensato è più facile e più leggibile per andare all'indietro come questo:
private static void method2(byte[] bytes) {
byte lastByte = bytes[bytes.length-1];
for (int i = bytes.length-1; i > 0; i--) {
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((bytes[i-1] & 0xff) << 7));
}
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((lastByte & 0xff) << 7));
}
Ma notato che il secondo (metodo 2) è più lento del primo (metodo 1)! Ho notato la differenza perché sto chiamando il metodo migliaia di volte. Così ho fatto un test per assicurarsi ed ecco il risultato medio da 20 test di chiamare ogni metodo 3000 volte (e il numero di byte è di 1 milione):
method1 average : 4s 572ms
method2 average : 5s 630ms
Quindi la mia domanda è: perché è il primo uno più veloce del secondo?
Ecco il codice di prova per assicurarsi che non sto facendo qualcosa di sbagliato con il mio test:
import java.math.BigInteger;
public class BitShiftTests {
public static void main(String[] args) {
int numOfTests = 20;
int numberOfShifts = 3000;
byte[] numbers = new byte[1000000];
for (int i = 0; i < numbers.length; i++) {
numbers[i] = (byte) (i % 255);
}
System.out.println("Testing method1...");
BigInteger method1Sum = new BigInteger("00000000", 2);
for (int i = 1; i <= numOfTests; i++) {
long total = 0L;
for (int j = 0; j < numberOfShifts; j++) {
long startTime = System.nanoTime();
method1(numbers);
long endTime = System.nanoTime();
total = total + (endTime - startTime);
}
method1Sum = method1Sum.add(new BigInteger(Long.toString(total), 10));
System.out.println(String.format("%-2d: %s", i, getTime(total)));
}
System.out.println("Testing method2...");
BigInteger method2Sum = new BigInteger("00000000", 2);
for (int i = 1; i <= numOfTests; i++) {
long total = 0L;
for (int j = 0; j < numberOfShifts; j++) {
long startTime = System.nanoTime();
method2(numbers);
long endTime = System.nanoTime();
total = total + (endTime - startTime);
}
method2Sum = method2Sum.add(new BigInteger(Long.toString(total), 10));
System.out.println(String.format("%-2d: %s", i, getTime(total)));
}
System.out.println("method1 average : " + getTime(method1Sum.longValue()/numOfTests));
System.out.println("method2 average : " + getTime(method2Sum.longValue()/numOfTests));
}
private static void method1(byte[] bytes) {
byte previousByte = bytes[0];
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((bytes[bytes.length - 1] & 0xff) << 7));
for (int i = 1; i < bytes.length; i++) {
byte tmp = bytes[i];
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((previousByte & 0xff) << 7));
previousByte = tmp;
}
}
private static void method2(byte[] bytes) {
byte lastByte = bytes[bytes.length-1];
for (int i = bytes.length-1; i > 0; i--) {
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((bytes[i-1] & 0xff) << 7));
}
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((lastByte & 0xff) << 7));
}
private static String getTime(long nanoSecs) {
int minutes = (int) (nanoSecs/60000000000.0);
int seconds = (int) (nanoSecs/1000000000.0) - (minutes * 60);
int millisecs = (int) (((nanoSecs/1000000000.0) - (seconds + minutes * 60)) * 1000);
int nanosecs = (int) nanoSecs - (millisecs * 1000000000);
if (minutes == 0 && seconds == 0 && millisecs == 0) {
return nanosecs + "ns";
}
if (minutes == 0 && seconds == 0) {
return millisecs + "ms";
}
if (minutes == 0 && millisecs == 0) {
return seconds + "s";
}
if (seconds == 0 && millisecs == 0) {
return minutes + "min";
}
if (minutes == 0) {
return seconds + "s " + millisecs + "ms";
}
if (seconds == 0) {
return minutes + "min " + millisecs + "ms";
}
if (millisecs == 0) {
return minutes + "min " + seconds + "s";
}
return minutes + "min " + seconds + "s " + millisecs + "ms";
}
}
Aggiornamento:
Sembra che il motivo è che sto accedendo a 2 indici differenti in ogni ciclo nel secondo metodo, mentre stavo accedendo solo a 1 indice nel primo metodo. Quindi non ha nulla a che fare con l'inversione del ciclo.
Grazie a @ rm5248 e @ Ben, sceglierei entrambe le risposte se potessi, ma ho scelto quello precedente.
@Cory Sto accedendo a bytes.length un minor numero di volte nel secondo metodo, ed è ancora più lento del primo metodo. – Motasim
Oh, oops. Ho letto che il secondo era più veloce ...ora sono confuso. –
Forse potresti pubblicare il tuo codice di prova? Supponiamo che i tuoi test siano puliti, ma non possiamo esserne certi. Inoltre, 3000 volte non sono così tanti. Che cosa succede se hai eseguito 1.000 test per chiamarlo 100.000 volte? Riduci il numero di byte con cui stai eseguendo il test se è troppo lungo. –