All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fathzer.games.util.PhysicalCores Maven / Gradle / Ivy

The newest version!
package com.fathzer.games.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Static utility class for finding the number of physical CPU cores.
 * 
It was greatly inspired by veddan's contribution */ public final class PhysicalCores { private static final NumberOfCores CORES = findPhysicalCoreCount(); private PhysicalCores() { super(); // Just to prevent instantiation } /** * Returns the number of "physical" hardware cores available. *

On a machine with hyper threading, this value may be less than the number * reported by {@link Runtime#availableProcessors()}. * If you are running on a virtual machine, the value returned will be the * number of cores assigned to the VM, not the actual number of physical * cores on the machine. * Likewise, if the number of cores available to this process is less than the * installed number, the available number will be returned. *

*

* The method is thread safe. *

* @return number of physical cores * @throws UnavailableException if it could not be determined */ public static int count() { if (CORES.e!=null) { throw CORES.e; } return CORES.count; } /** Exception thrown when core count can't be found. */ public static class UnavailableException extends RuntimeException { private static final long serialVersionUID = 1L; private UnavailableException(String message, Throwable cause) { super(message, cause); } private UnavailableException(String message) { super(message); } } private static class NumberOfCores { private final UnavailableException e; private final int count; private NumberOfCores(int count) { this.e = null; this.count = count; } private NumberOfCores(String message) { this(new UnavailableException(message)); } private NumberOfCores(UnavailableException e) { this.e = e; this.count = -1; } } private static NumberOfCores findPhysicalCoreCount() { String osName; try { osName = System.getProperty("os.name"); } catch (SecurityException e) { return new NumberOfCores(new UnavailableException("Could not read system property 'os.name'",e)); } try { if (osName == null) { throw new UnsupportedOperationException("Failed to read OS name."); } else if (isLinux(osName)) { return readFromProc(); } else if (isWindows(osName)) { return readFromWMIC(); } else if (isMacOsX(osName)) { return readFromSysctlOsX(); } else if (isFreeBsd(osName)) { return readFromSysctlFreeBSD(); } else { return new NumberOfCores(String.format("Unknown OS '%s'. Please report this so a case can be added.", osName)); } } catch (UnavailableException e) { return new NumberOfCores(e); } } @SuppressWarnings("java:S1075") private static NumberOfCores readFromProc() { final String path = "/proc/cpuinfo"; File cpuinfo = new File(path); if (!cpuinfo.exists()) { return new NumberOfCores(String.format("Old Linux without %s. Will not be able to provide core count.", path)); } try (InputStream in = new FileInputStream(cpuinfo)) { String s = readToString(in, StandardCharsets.UTF_8); // Count number of different tuples (physical id, core id) to discard hyper threading and multiple sockets Map> physicalIdToCoreId = new HashMap<>(); int coreIdCount = 0; String[] split = s.split("\n"); String latestPhysicalId = null; for (String row : split) if (row.startsWith("physical id")) { latestPhysicalId = row; if (physicalIdToCoreId.get(row) == null) physicalIdToCoreId.put(latestPhysicalId, new HashSet<>()); } else if (row.startsWith("core id")) // "physical id" row should always come before "core id" row, so that physicalIdToCoreId should // not be null here. physicalIdToCoreId.get(latestPhysicalId).add(row); for (Set coreIds : physicalIdToCoreId.values()) coreIdCount += coreIds.size(); return new NumberOfCores(coreIdCount); } catch (SecurityException | IOException e) { return new NumberOfCores(String.format("Error %s while reading %s", e, path)); } } private static NumberOfCores readFromWMIC() { String wmic = System.getenv("windir")+"\\system32\\wbem\\WMIC"; ProcessBuilder pb = new ProcessBuilder(wmic, "/OUTPUT:STDOUT", "CPU", "Get", "/Format:List"); pb.redirectErrorStream(true); Process wmicProc; try { wmicProc = pb.start(); wmicProc.getOutputStream().close(); } catch (IOException | SecurityException e) { return new NumberOfCores(String.format("Failed to spawn WMIC process. " + "Will not be able to provide physical core count: %s", e)); } waitFor(wmicProc); try (InputStream in = wmicProc.getInputStream()) { String wmicOutput = readToString(in, StandardCharsets.US_ASCII); return parseWmicOutput(wmicOutput); } catch (UnsupportedEncodingException e) { // Java implementations are required to support US-ASCII, so this shouldn't happen throw new UncheckedIOException(e); } catch (SecurityException | IOException e) { return new NumberOfCores(String.format("Error while reading WMIC output file: %s", e)); } } private static NumberOfCores parseWmicOutput(String wmicOutput) { String[] rows = wmicOutput.split("\n"); int coreCount = 0; for (String row : rows) { if (row.startsWith("NumberOfCores")) { String num = row.split("=")[1].trim(); try { coreCount += Integer.parseInt(num); } catch (NumberFormatException e) { return new NumberOfCores(new UnavailableException(String.format("Unexpected output from WMIC: \"{}\". " + "Will not be able to provide physical core count.", wmicOutput), e)); } } } return coreCount > 0 ? new NumberOfCores(coreCount) : new NumberOfCores(String.format("WMIC returned a negative of null number of cores (%d)", coreCount)); } private static NumberOfCores readFromSysctlOsX() { String result = readSysctl("hw.physicalcpu", "-n"); if (result == null) { return new NumberOfCores("sysctl hw.physicalcpu -n returned no data"); } try { return new NumberOfCores(Integer.parseInt(result)); } catch (NumberFormatException e) { return new NumberOfCores(String.format("sysctl returned something that was not a number: '%s'", result)); } } private static NumberOfCores readFromSysctlFreeBSD() { String result = readSysctl("dev.cpu"); if (result == null) { return null; } Set cpuLocations = new HashSet<>(); for (String row : result.split("\n")) { if (row.contains("location")) { cpuLocations.add(row.split("\\\\")[1]); } } return cpuLocations.isEmpty() ? new NumberOfCores("sysctl dev.cpu returns no cores") : new NumberOfCores(cpuLocations.size()); } private static String readSysctl(String variable, String... options) { List command = new ArrayList<>(); command.add("sysctl"); command.addAll(Arrays.asList(options)); command.add(variable); ProcessBuilder pb = new ProcessBuilder(command); pb.redirectErrorStream(true); Process sysctlProc; try { sysctlProc = pb.start(); } catch (IOException | SecurityException e) { throw new UnavailableException("Failed to spawn sysctl process. " + "Will not be able to provide physical core count.", e); } String result; try { result = readToString(sysctlProc.getInputStream(), StandardCharsets.UTF_8).trim(); } catch (UnsupportedEncodingException e) { // Java implementations are required to support UTF-8, so this can't happen throw new UncheckedIOException(e); } catch (IOException e) { throw new UnavailableException("Error while reading from sysctl process", e); } int exitStatus = waitFor(sysctlProc); if (exitStatus != 0) { throw new UnavailableException(String.format("Could not read sysctl variable %s. Exit status was %s", variable, exitStatus)); } return result; } private static boolean isLinux(String osName) { return osName.startsWith("Linux") || osName.startsWith("LINUX"); } private static boolean isWindows(String osName) { return osName.startsWith("Windows"); } private static boolean isMacOsX(String osName) { return osName.startsWith("Mac OS X"); } private static boolean isFreeBsd(String osName) { return osName.startsWith("FreeBSD"); } private static String readToString(InputStream in, Charset charset) throws IOException { try (InputStreamReader reader = new InputStreamReader(in , charset)) { StringWriter sw = new StringWriter(); char[] buf = new char[10000]; while (reader.read(buf) != -1) { sw.write(buf); } return sw.toString(); } } private static int waitFor(Process proc) { try { return proc.waitFor(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new UnavailableException("Interrupted while waiting for process", e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy