¿Es posible verificar en Java si la CPU está hiperproceso?
multithreading hyperthreading (7)
Algunas reflexiones más:
- Hyperthreading puede tener más de 2 hilos por código (Sparc puede tener 8)
- El recolector de basura necesita tiempo de CPU para funcionar también.
- Hyperthreading puede ayudar a un GC concurrente, o no; o la JVM puede solicitar ser propietario exclusivo (no de subprocesamiento) del núcleo. Por lo tanto, obstaculizar el GC para obtener mejores resultados durante una prueba podría estar perjudicando a largo plazo.
- Hyperthreading suele ser útil si hay errores de caché, por lo que la CPU no se detiene, sino que se cambia a otra tarea. Por lo tanto, "a hyperthreading o no" dependería tanto de la carga de trabajo como de la velocidad de memoria / tamaño de la memoria caché L1 / L2 de la CPU, etc.
- Los sistemas operativos pueden tener sesgo hacia / en contra de algunos subprocesos y Thread.setPriority puede no ser respetado (en Linux generalmente no es respetado).
- Es posible establecer la afinidad del proceso, deshabilitando algunos núcleos. Por lo tanto, saber que existe un subproceso no tendrá ninguna virtud significativa en tales casos.
Dicho esto: debe tener una configuración para el tamaño de los subprocesos de trabajo y recomendaciones sobre cómo configurar dados los detalles de la arquitectura.
Me gustaría saber el número óptimo de hilos que puedo ejecutar. Normalmente, esto es igual a Runtime.getRuntime().availableProcessors()
.
Sin embargo, el número devuelto es dos veces más alto en una CPU que admite hipervínculos. Ahora, para algunas tareas, el subproceso es bueno, pero para otras no hace nada. En mi caso, sospecho que no hace nada, por lo que deseo saber si tengo que dividir el número devuelto por Runtime.getRuntime().availableProcessors()
en dos.
Para eso tengo que deducir si la CPU es hiperprocesamiento. De ahí mi pregunta: ¿cómo puedo hacerlo en Java?
Gracias.
EDITAR
OK, he evaluado mi código. Aquí está mi entorno:
- Lenovo ThinkPad W510 (es decir, CPU i7 con 4 núcleos e hyperthreading), 16G de RAM
- Windows 7
- 84 archivos CSV comprimidos con cremallera con tamaños comprimidos que van desde 105M a 16M
- Todos los archivos se leen uno por uno en el hilo principal, sin acceso de subprocesos múltiples al HD.
- Cada fila de archivos CSV contiene algunos datos, que se analizan y una prueba rápida sin contexto determina si la fila es relevante.
- Cada fila relevante contiene dos dobles (que representan la longitud y la latitud, para los curiosos), que se convierten en un solo
Long
, que luego se almacena en un conjunto de hash compartido.
Por lo tanto, los subprocesos de trabajo no leen nada de la HD, pero sí se ocupan de descomprimir y analizar los contenidos (utilizando la biblioteca opencsv ).
A continuación se muestra el código, sin los detalles aburridos:
public void work(File dir) throws IOException, InterruptedException {
Set<Long> allCoordinates = Collections.newSetFromMap(new ConcurrentHashMap<Long, Boolean>());
int n = 6;
// NO WAITING QUEUE !
ThreadPoolExecutor exec = new ThreadPoolExecutor(n, n, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
StopWatch sw1 = new StopWatch();
StopWatch sw2 = new StopWatch();
sw1.start();
sw2.start();
sw2.suspend();
for (WorkItem wi : m_workItems) {
for (File file : dir.listFiles(wi.fileNameFilter)) {
MyTask task;
try {
sw2.resume();
// The only reading from the HD occurs here:
task = new MyTask(file, m_coordinateCollector, allCoordinates, wi.headerClass, wi.rowClass);
sw2.suspend();
} catch (IOException exc) {
System.err.println(String.format("Failed to read %s - %s", file.getName(), exc.getMessage()));
continue;
}
boolean retry = true;
while (retry) {
int count = exec.getActiveCount();
try {
// Fails if the maximum of the worker threads was created and all are busy.
// This prevents us from loading all the files in memory and getting the OOM exception.
exec.submit(task);
retry = false;
} catch (RejectedExecutionException exc) {
// Wait for any worker thread to finish
while (exec.getActiveCount() == count) {
Thread.sleep(100);
}
}
}
}
}
exec.shutdown();
exec.awaitTermination(1, TimeUnit.HOURS);
sw1.stop();
sw2.stop();
System.out.println(String.format("Max concurrent threads = %d", n));
System.out.println(String.format("Total file count = %d", m_stats.getFileCount()));
System.out.println(String.format("Total lines = %d", m_stats.getTotalLineCount()));
System.out.println(String.format("Total good lines = %d", m_stats.getGoodLineCount()));
System.out.println(String.format("Total coordinates = %d", allCoordinates.size()));
System.out.println(String.format("Overall elapsed time = %d sec, excluding I/O = %d sec", sw1.getTime() / 1000, (sw1.getTime() - sw2.getTime()) / 1000));
}
public class MyTask<H extends CsvFileHeader, R extends CsvFileRow<H>> implements Runnable {
private final byte[] m_buffer;
private final String m_name;
private final CoordinateCollector m_coordinateCollector;
private final Set<Long> m_allCoordinates;
private final Class<H> m_headerClass;
private final Class<R> m_rowClass;
public MyTask(File file, CoordinateCollector coordinateCollector, Set<Long> allCoordinates,
Class<H> headerClass, Class<R> rowClass) throws IOException {
m_coordinateCollector = coordinateCollector;
m_allCoordinates = allCoordinates;
m_headerClass = headerClass;
m_rowClass = rowClass;
m_name = file.getName();
m_buffer = Files.toByteArray(file);
}
@Override
public void run() {
try {
m_coordinateCollector.collect(m_name, m_buffer, m_allCoordinates, m_headerClass, m_rowClass);
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}
Por favor, encuentre a continuación los resultados (he cambiado ligeramente la salida para omitir las partes que se repiten):
Max concurrent threads = 4
Total file count = 84
Total lines = 56395333
Total good lines = 35119231
Total coordinates = 987045
Overall elapsed time = 274 sec, excluding I/O = 266 sec
Max concurrent threads = 6
Overall elapsed time = 218 sec, excluding I/O = 209 sec
Max concurrent threads = 7
Overall elapsed time = 209 sec, excluding I/O = 199 sec
Max concurrent threads = 8
Overall elapsed time = 201 sec, excluding I/O = 192 sec
Max concurrent threads = 9
Overall elapsed time = 198 sec, excluding I/O = 186 sec
Usted es libre de sacar sus propias conclusiones, pero la mía es que el hyperthreading mejora el rendimiento en mi caso concreto. Además, tener 6 subprocesos de trabajo parece ser la opción correcta para esta tarea y para mi máquina.
Desafortunadamente, esto no es posible desde Java. Si sabe que la aplicación se ejecutará en una variante moderna de Linux, puede leer el archivo / proc / cpuinfo e inferir si HT está habilitado.
La lectura de la salida de este comando hace el truco:
grep -i "physical id" /proc/cpuinfo | sort -u | wc -l
Es posible que no pueda consultar el sistema operativo o el tiempo de ejecución de manera confiable, pero podría ejecutar un punto de referencia rápido.
Aumente progresivamente los hilos de bloqueo de giro, haciendo pruebas para ver si cada hilo nuevo se itera tan bien como el anterior. Una vez que el rendimiento de uno de los subprocesos es inferior a la mitad de cada una de las pruebas anteriores (al menos para Intel, no sé sobre SPARC), sabes que has comenzado a compartir un núcleo con un hipervínculo.
No es una forma confiable de determinar si tiene un subproceso que está activado, un subproceso que está apagado o no hay subprocesos.
En su lugar, un mejor enfoque es realizar una primera calibración la primera vez que se ejecuta (o cada vez) que ejecuta una primera prueba que determina qué enfoque utilizar.
Otro enfoque es utilizar todos los procesadores incluso si el subproceso no ayuda (siempre que no haga que el código sea mucho más lento)
No hay manera de determinar eso desde Java puro (después de todo, un núcleo lógico es un núcleo, si se implementa utilizando HT o no). Tenga en cuenta que las soluciones propuestas hasta el momento pueden resolver sus requisitos (como usted lo solicitó), pero no solo la CPU de Intel ofrece una forma de hipercruzamiento (Sparc me viene a la mente y estoy seguro de que también hay otras).
Tampoco tuvo en cuenta que incluso si determina que el sistema utiliza HT, no podrá controlar una afinidad de hilos con los núcleos de Java. Así que todavía estás a merced del programador de hilos del sistema operativo. Si bien hay escenarios plausibles en los que un menor número de subprocesos podría funcionar mejor (debido a la reducción de la eliminación del caché), no hay manera de determinar de forma estática cuántos subprocesos se deben usar (después de que todas las CPU tienen tamaños de caché muy diferentes (un rango de 256 KB en el extremo inferior). se pueden esperar razonablemente hasta 16MB en servidores hoy en día, y esto está obligado a cambiar con cada nueva generación).
Simplemente conviértalo en un valor configurable, cualquier intento de determinar esto sin saber exactamente el sistema de destino es inútil.
No hay manera de hacerlo. Una cosa que puede hacer es crear un grupo de subprocesos de Runtime.getRuntime().availableProcessors()
subprocesos en su aplicación y usarlos cuando se recibe la solicitud.
De esta manera puede tener 0 - Runtime.getRuntime().availableProcessors()
número de subprocesos.
Para Windows
, si el número de núcleos lógicos es mayor que el número de núcleos, tiene habilitado el hyper-threading
. Lea más sobre esto here .
Puedes usar wmic
para encontrar esta información:
C:/WINDOWS/system32>wmic CPU Get NumberOfCores,NumberOfLogicalProcessors /Format:List
NumberOfCores=4
NumberOfLogicalProcessors=8
Por lo tanto, mi sistema tiene hyper-threading
. La cantidad de procesadores lógicos es el doble de los núcleos.
Pero tal vez ni siquiera necesites saberlo. Runtime.getRuntime().availableProcessors()
ya devuelve la cantidad de procesadores lógicos.
Un ejemplo completo sobre cómo obtener el recuento de núcleos físicos (solo Windows
):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class PhysicalCores
{
public static void main(String[] arguments) throws IOException, InterruptedException
{
int physicalNumberOfCores = getPhysicalNumberOfCores();
System.out.println(physicalNumberOfCores);
}
private static int getPhysicalNumberOfCores() throws IOException, InterruptedException
{
ProcessBuilder processBuilder = new ProcessBuilder("wmic", "CPU", "Get", "NumberOfCores");
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
String processOutput = getProcessOutput(process);
String[] lines = processOutput.split(System.lineSeparator());
return Integer.parseInt(lines[2]);
}
private static String getProcessOutput(Process process) throws IOException, InterruptedException
{
StringBuilder processOutput = new StringBuilder();
try (BufferedReader processOutputReader = new BufferedReader(
new InputStreamReader(process.getInputStream())))
{
String readLine;
while ((readLine = processOutputReader.readLine()) != null)
{
processOutput.append(readLine);
processOutput.append(System.lineSeparator());
}
process.waitFor();
}
return processOutput.toString().trim();
}
}