viernes
ago142015
Java 7, divide y vencerás.
El siguiente ejemplo ejecuta el rellenado de un array de bytes de un tamaño importante de dos maneras:
- La primera de ellas, de manera secuencial. Recorre todas las posiciones, y rellena cada una de ellas con un número aleatorio
- En la segunda de ellas, realiza el mismo algoritmo en paralelo. Divide el bloque grande en bloques más pequeños, y cada uno de ellos forma parte de una tarea global (comparten memoria, cada uno de ellos tiene conocimientos de su tarea específica...). Se aprovecha entonces, en el caso del que escribe, los dos procesadores del núcleo de la CPU.
Muy interesante este concepto, a tener en cuenta para tareas grandes susceptibles de ser divididas en tareas más pequeñas.
Me recordó un poco al principio inicial de MapReduce (divide una tarea grande en tareas pequeñas.... y vencerás.
La salida, en el caso del que escribe, fue la siguiente:
Sequential processing time: 1683 ms
Sequential processing time: 1662 ms
Number of processor available: 2
Parallel processing time: 930 ms
Parallel processing time: 851 ms
Number of steals: 598
Saludos cordiales y gracias por su atención, como siempre un gusto
01.
package
com.sprunck.sample;
02.
03.
import
static
java.util.Arrays.asList;
04.
05.
import
java.util.Random;
06.
import
java.util.concurrent.ForkJoinPool;
07.
import
java.util.concurrent.RecursiveAction;
08.
09.
public
class
ForkJoinRandomFillAction {
10.
Random random =
new
Random();
11.
12.
public
void
loadArray(
int
[] array) {
13.
for
(
int
i =
0
; i < array.length; i++) {
14.
array[i] = random.nextInt(
10000
);
// Generates numbers from 0 to
15.
// 10000
16.
}
17.
}
18.
19.
public
static
void
main(String[] args) {
20.
ForkJoinRandomFillAction sort =
new
ForkJoinRandomFillAction();
21.
22.
int
arrayLength = 1_00_00_0000;
23.
int
array[] =
new
int
[arrayLength];
24.
25.
// No. of times sequential & Parallel operation should be performed to
26.
// warm up HotSpot JVM
27.
final
int
iterations =
2
;
28.
29.
for
(
int
i =
0
; i < iterations; i++) {
30.
long
start = System.currentTimeMillis();
31.
sort.loadArray(array);
32.
33.
System.out.println(
"Sequential processing time: "
34.
+ (System.currentTimeMillis() - start) +
" ms"
);
35.
36.
}
37.
38.
System.out.println(
"Number of processor available: "
39.
+ Runtime.getRuntime().availableProcessors());
40.
41.
ForkJoinPool fjpool =
new
ForkJoinPool(
64
);
// Default parallelism level
42.
// =
43.
// Runtime.getRuntime().availableProcessors()
44.
45.
for
(
int
i =
0
; i < iterations; i++) {
46.
// Create a task with the complete array
47.
RecursiveAction task =
new
RandomFillAction(array,
0
, array.length);
48.
long
start = System.currentTimeMillis();
49.
fjpool.invoke(task);
50.
51.
System.out.println(
"Parallel processing time: "
52.
+ (System.currentTimeMillis() - start) +
" ms"
);
53.
}
54.
55.
System.out
56.
.println(
"Number of steals: "
+ fjpool.getStealCount() +
"\n"
);
57.
}
58.
}
59.
60.
class
RandomFillAction
extends
RecursiveAction {
61.
private
static
final
long
serialVersionUID = 1L;
62.
final
int
low;
63.
final
int
high;
64.
private
int
[] array;
65.
final
int
splitSize =
40000
;
// Some threshold size to spit the task
66.
67.
public
RandomFillAction(
int
[] array,
int
low,
int
high) {
68.
this
.low = low;
69.
this
.high = high;
70.
this
.array = array;
71.
}
72.
73.
@Override
74.
protected
void
compute() {
75.
if
(high - low > splitSize) {
76.
// task is huge so divide in half
77.
int
mid = (low + high) /
2
;
78.
invokeAll(asList(
new
RandomFillAction(array, low, mid),
79.
new
RandomFillAction(array, mid, high)));
80.
}
else
{
81.
// Some calculation logic
82.
Random random =
new
Random();
83.
for
(
int
i = low; i < high; i++) {
84.
array[i] = random.nextInt(
10000
);
85.
}
86.
}
87.
}
88.
}
Reader Comments (1)
Muy interesante, la verdad que son capacidades que podemos usar en el día a día de la programación pero las ignoramos.
Estas librerías de java.util.concurrent.ForkJoinPool y RecursiveAction sólo están en Java 7 ¿verdad?
Muy útil!!!