Quindi, in pratica, avevo bisogno di ottimizzare questo pezzo di codice oggi. Si cerca di trovare la sequenza più lunga prodotta da qualche funzione per i primi numeri milioni partenza:Esiste una "soglia" che giustifichi il calcolo multithreading?
public static void main(String[] args) {
int mostLen = 0;
int mostInt = 0;
long currTime = System.currentTimeMillis();
for(int j=2; j<=1000000; j++) {
long i = j;
int len = 0;
while((i=next(i)) != 1) {
len++;
}
if(len > mostLen) {
mostLen = len;
mostInt = j;
}
}
System.out.println(System.currentTimeMillis() - currTime);
System.out.println("Most len is " + mostLen + " for " + mostInt);
}
static long next(long i) {
if(i%2==0) {
return i/2;
} else {
return i*3+1;
}
}
Il mio errore è stato quello di cercare di introdurre il multithreading:
void doSearch() throws ExecutionException, InterruptedException {
final int numProc = Runtime.getRuntime().availableProcessors();
System.out.println("numProc = " + numProc);
ExecutorService executor = Executors.newFixedThreadPool(numProc);
long currTime = System.currentTimeMillis();
List<Future<ValueBean>> list = new ArrayList<Future<ValueBean>>();
for (int j = 2; j <= 1000000; j++) {
MyCallable<ValueBean> worker = new MyCallable<ValueBean>();
worker.setBean(new ValueBean(j, 0));
Future<ValueBean> f = executor.submit(worker);
list.add(f);
}
System.out.println(System.currentTimeMillis() - currTime);
int mostLen = 0;
int mostInt = 0;
for (Future<ValueBean> f : list) {
final int len = f.get().getLen();
if (len > mostLen) {
mostLen = len;
mostInt = f.get().getNum();
}
}
executor.shutdown();
System.out.println(System.currentTimeMillis() - currTime);
System.out.println("Most len is " + mostLen + " for " + mostInt);
}
public class MyCallable<T> implements Callable<ValueBean> {
public ValueBean bean;
public void setBean(ValueBean bean) {
this.bean = bean;
}
public ValueBean call() throws Exception {
long i = bean.getNum();
int len = 0;
while ((i = next(i)) != 1) {
len++;
}
return new ValueBean(bean.getNum(), len);
}
}
public class ValueBean {
int num;
int len;
public ValueBean(int num, int len) {
this.num = num;
this.len = len;
}
public int getNum() {
return num;
}
public int getLen() {
return len;
}
}
long next(long i) {
if (i % 2 == 0) {
return i/2;
} else {
return i * 3 + 1;
}
}
Purtroppo, la versione multithreaded lavorato 5 volte più lento rispetto al single-threaded su 4 processori (core).
poi ho provato un po 'di più l'approccio grezzo:
static int mostLen = 0;
static int mostInt = 0;
synchronized static void updateIfMore(int len, int intgr) {
if (len > mostLen) {
mostLen = len;
mostInt = intgr;
}
}
public static void main(String[] args) throws InterruptedException {
long currTime = System.currentTimeMillis();
final int numProc = Runtime.getRuntime().availableProcessors();
System.out.println("numProc = " + numProc);
ExecutorService executor = Executors.newFixedThreadPool(numProc);
for (int i = 2; i <= 1000000; i++) {
final int j = i;
executor.execute(new Runnable() {
public void run() {
long l = j;
int len = 0;
while ((l = next(l)) != 1) {
len++;
}
updateIfMore(len, j);
}
});
}
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);
System.out.println(System.currentTimeMillis() - currTime);
System.out.println("Most len is " + mostLen + " for " + mostInt);
}
static long next(long i) {
if (i % 2 == 0) {
return i/2;
} else {
return i * 3 + 1;
}
}
e ha funzionato molto più veloce, ma comunque era più lento del metodo singolo thread.
Spero che non sia perché ho sbagliato il modo in cui sto facendo il multithreading, ma piuttosto questo particolare calcolo/algoritmo non è adatto per il calcolo parallelo. Se cambio calcolo per renderlo più intenso processore con il metodo next
sostituendoli con:
long next(long i) {
Random r = new Random();
for(int j=0; j<10; j++) {
r.nextLong();
}
if (i % 2 == 0) {
return i/2;
} else {
return i * 3 + 1;
}
}
entrambe le versioni multithread cominciano ad eseguire più di due volte più veloce rispetto alla versione singlethreaded su una macchina 4 nucleo.
Così chiaramente ci deve essere una certa soglia, che è possibile utilizzare per determinare se vale la pena di introdurre il multithreading e la mia domanda è:
Qual è la regola di base che avrebbe aiutato a decidere se un determinato calcolo è abbastanza intensiva per essere ottimizzato eseguendolo in parallelo (senza spendere sforzi per implementarlo effettivamente?)
Questo è solo tangenzialmente correlato alla domanda, ma l'algoritmo in questione è correlato alla [congettura di Collatz] (http://en.wikipedia.org/wiki/Collatz_conjecture). È più famoso in geekdom grazie a [questo] (http://xkcd.com/710/) e [questo] (http://store.xkcd.com/xkcd/#CollatzConjecture). –
I * altamente * consiglio il libro [Java Concurrency in Practice] (http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) di Brian Goetz. –