Est-il possible de vérifier en Java si le CPU est hyper threading?
j'aimerais connaître le nombre optimal de threads que je peux lancer. Normalement, cela équivaut à Runtime.getRuntime().availableProcessors()
.
cependant, le nombre retourné est deux fois plus élevé sur un processeur supportant un filetage hyper. Maintenant, pour certaines tâches hyper threading est bon, mais pour d'autres, il ne fait rien. Dans mon cas, je soupçonne, il ne fait rien et je souhaite donc savoir si je dois diviser le nombre retourné par Runtime.getRuntime().availableProcessors()
en deux.
pour cela je dois déduire si le CPU est hyper filetage. Conséquent ma question - comment puis-je le faire en Java?
Merci.
EDIT
OK, j'ai comparé mon code. Voici mon environnement:
- Lenovo ThinkPad W510 (i.e. i7 CPU with 4 core and hyperthreading), 16G de RAM
- Windows 7
- 84 fichiers CSV zippés avec des tailles de zippés allant de 105M à 16M
- tous les fichiers sont lus un par un dans le thread principal - pas d'accès multithreading HD.
- chaque ligne de fichier CSV contient des données, qui sont analysées et un test rapide sans contexte détermine si la ligne est pertinente.
- chaque ligne pertinente contient deux doubles (représentant la longitude et la latitude, pour les curieux), qui sont forcés en un seul
Long
, qui est ensuite stocké dans un hachage partagée ensemble.
ainsi les fils d'ouvrier ne lisent rien de la HD, mais ils s'occupent avec le dézipping et l'analyse du contenu (à l'aide de la opencsv bibliothèque).
ci-dessous se trouve le code, avec les détails ennuyeux:
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.
}
}
}
Veuillez trouver ci-dessous les résultats (j'ai légèrement changé la sortie d'omettre de le répéter):
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
vous êtes libre de tirer vos propres conclusions, mais la mienne est que l'hyperthreading améliore la performance dans mon cas concret. En outre, avoir 6 fils ouvriers semble être le bon choix pour cette tâche et ma machine.
7 réponses
Windows
, si le nombre de noyaux logiques est plus élevé que le nombre de cœurs, vous avez hyper-threading
activé. Lire plus sur le sujet ici.
Vous pouvez utiliser wmic
pour trouver cette information:
C:\WINDOWS\system32>wmic CPU Get NumberOfCores,NumberOfLogicalProcessors /Format:List
NumberOfCores=4
NumberOfLogicalProcessors=8
par conséquent, mon système a hyper-threading
. La quantité de processeurs logiques est le double des noyaux.
Mais vous pouvez même pas besoin de savoir. Runtime.getRuntime().availableProcessors()
déjà renvoie le nombre de processeurs logiques.
complet exemple pour obtenir le nombre de noyaux physiques (Windows
uniquement):
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();
}
}
malheureusement, ce n'est pas possible depuis java. Si vous savez que l'application fonctionnera sur une variante linux moderne, vous pouvez lire le fichier /proc/cpuinfo et en déduire si HT est activé.
Lecture de la sortie de cette commande fait le truc:
grep -i "physical id" /proc/cpuinfo | sort -u | wc -l
ce n'est pas un moyen fiable pour déterminer si vous avez un filetage hyper qui est en marche, filetage hyper qui est éteint ou pas filetage hyper.
au lieu de cela une meilleure approche est de faire un premier calibrage la première fois que vous exécutez (ou chaque fois) qui exécute un premier test qui détermine quelle approche à utiliser.
une autre approche consiste à utiliser tous les processeurs même si le threading hyper n'aide pas (à condition que cela ne rende pas le code beaucoup plus lent)
Quelques réflexions:
- l'Hyperthreading peut avoir plus de 2 threads par code (Sparc peut avoir 8)
- le ramasseur d'ordures a besoin de temps CPU pour travailler aussi.
- Hyperthreading peut aider un GC concurrent ou ne peut pas; ou la JVM peut demander à être propriétaire exclusif (et non hyperthreading) du noyau. Donc entraver le GC pour obtenir vos meilleurs résultats pendant un test pourrait être douloureux à long terme.
- L'Hyperthreading est habituellement utile s'il y a cache-manques, de sorte que le CPU n'est pas bloqué mais commuté à une autre tâche. Par conséquent, "to hyperthreading or not" dépendrait à la fois de la charge de travail et de la taille du cache CPU L1/L2/vitesse de mémoire, etc.
- OS peut avoir un biais vers / contre certains threads et threads.setPriority peut ne pas être honoré (sur Linux, il n'est généralement pas honoré).
- il est possible de définir l'affinité du processus, en rejetant certains noyaux. Donc, savoir qu'il y a hyperthreading ne sera d'aucune vertu significative dans une telle cas.
ceci étant dit: vous devriez avoir un paramètre pour la taille des threads de worker et une recommandation sur la façon de configurer étant donné les spécificités de l'architecture.
il n'y a aucun moyen de déterminer que de Java pur (après tout un noyau logique est un noyau, si il est implémenté en utilisant HT ou non). Prenez garde que les solutions proposées jusqu'à présent puissent répondre à vos besoins (comme vous l'avez demandé), mais pas seulement L'offre D'Intel CPU d'une forme d'hyperthreading (Sparc vient à l'esprit et je suis sûr qu'il y en a d'autres aussi).
Vous aussi de ne pas prendre en compte que même si vous déterminez que le système utilise HT, vous ne pourrez pas contrôler un threads affinité avec les noyaux de Java. Vous êtes donc toujours à la merci du Planificateur de thread de L'OS. Bien qu'il y ait des scénarios plausibles où moins de threads pourraient donner de meilleurs résultats (à cause de la réduction du trash de cache), il n'y a aucun moyen de déterminer statiquement combien de threads devraient être utilisés (après que tous les CPU aient très différentes tailles de cache (une gamme allant de 256KB sur le bas de gamme à >16MB dans les serveurs peuvent être raisonnablement attendus de nos jours. Et cela va changer avec de nouveaux chaque génération.)
il suffit D'en faire un réglage configurable, toute tentative de le déterminer sans connaître exactement le système cible est futile.
il n'y a aucun moyen de faire cela, une chose que vous pouvez faire est de créer un pool de Runtime.getRuntime().availableProcessors()
Threads dans votre application et de l'utilisation que lors de la demande.
de Cette façon, vous pouvez avoir 0 - Runtime.getRuntime().availableProcessors()
nombre de threads.
il se peut que vous ne puissiez pas interroger le système D'exploitation ou exécuter Runtime de manière fiable, mais vous pouvez exécuter un benchmark rapide.
augmenter progressivement les threads de spin-lock, tester pour voir si chaque nouveau thread itère aussi bien que le précédent. Une fois que la performance d'un des threads est inférieure à la moitié de chacun des tests précédents (au moins pour intel, Je ne sais pas pour SPARC), vous savez que vous avez commencé à partager un noyau avec un hyperthread.