Así que, básicamente, necesitaba optimizar este código hoy. Se trata de encontrar la secuencia más larga producida por alguna función en los primeros números de millones de partida:¿Hay algún "umbral" que justifique el cálculo multiproceso?
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;
}
}
Mi error fue tratar de introducir multihilo:
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;
}
}
Por desgracia, la versión multiproceso trabajó 5 veces más lento que el single-threaded en 4 procesadores (núcleos).
Luego probé un poco más de enfoque crudo:
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;
}
}
y funcionó mucho más rápido, pero aún así, era más lento que el enfoque de un solo hilo.
Espero que no sea porque haya estropeado la forma en que estoy haciendo multihilo, sino que este cálculo/algoritmo en particular no es una buena opción para el cálculo en paralelo. Si cambio de cálculo para que sea más intensivo del procesador mediante la sustitución método next
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;
}
}
ambas versiones multiproceso empiezan a ejecutar más de dos veces más rápido que la versión singlethreaded en una máquina de 4 núcleos.
Así que está claro que debe haber algún umbral que se puede utilizar para determinar si vale la pena introducir multihilo y mi pregunta es:
¿Cuál es la norma básica que ayudar a decidir si un determinado cálculo es lo suficientemente intensiva para ser optimizado ejecutándolo en paralelo (sin gastar esfuerzo para implementarlo realmente?)
Esto solo está relacionado tangencialmente con la pregunta, pero el algoritmo en cuestión está relacionado con la [conjetura de Collatz] (http://en.wikipedia.org/wiki/Collatz_conjecture). Es más famoso en geekdom gracias a [this] (http://xkcd.com/710/) y [this] (http://store.xkcd.com/xkcd/#CollatzConjecture). –
I * altamente * recomiendo el libro [Concurrencia de Java en la práctica] (http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) por Brian Goetz. –