/*
* Copyright 2016 higherfrequencytrading.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.openhft.affinity.impl;
import net.openhft.affinity.CpuLayout;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.*;
import static java.lang.Integer.parseInt;
/**
* @author peter.lawrey
*/
public class VanillaCpuLayout implements CpuLayout {
public static final int MAX_CPUS_SUPPORTED = 64;
@NotNull
private final List<CpuInfo> cpuDetails;
private final int sockets;
private final int coresPerSocket;
private final int threadsPerCore;
VanillaCpuLayout(@NotNull List<CpuInfo> cpuDetails) {
this.cpuDetails = cpuDetails;
SortedSet<Integer> sockets = new TreeSet<Integer>(),
cores = new TreeSet<Integer>(),
threads = new TreeSet<Integer>();
for (CpuInfo cpuDetail : cpuDetails) {
sockets.add(cpuDetail.socketId);
cores.add((cpuDetail.socketId << 16) + cpuDetail.coreId);
threads.add(cpuDetail.threadId);
}
this.sockets = sockets.size();
this.coresPerSocket = cores.size() / sockets.size();
this.threadsPerCore = threads.size();
if (cpuDetails.size() != sockets() * coresPerSocket() * threadsPerCore()) {
StringBuilder error = new StringBuilder();
error.append("cpuDetails.size= ").append(cpuDetails.size())
.append(" != sockets: ").append(sockets())
.append(" * coresPerSocket: ").append(coresPerSocket())
.append(" * threadsPerCore: ").append(threadsPerCore()).append('\n');
for (CpuInfo detail : cpuDetails) {
error.append(detail).append('\n');
}
throw new AssertionError(error);
}
}
@NotNull
public static VanillaCpuLayout fromProperties(String fileName) throws IOException {
return fromProperties(openFile(fileName));
}
@NotNull
public static VanillaCpuLayout fromProperties(InputStream is) throws IOException {
Properties prop = new Properties();
prop.load(is);
return fromProperties(prop);
}
@NotNull
public static VanillaCpuLayout fromProperties(@NotNull Properties prop) {
List<CpuInfo> cpuDetails = new ArrayList<CpuInfo>();
for (int i = 0; i < MAX_CPUS_SUPPORTED; i++) {
String line = prop.getProperty("" + i);
if (line == null) break;
String[] word = line.trim().split(" *, *");
CpuInfo details = new CpuInfo(parseInt(word[0]),
parseInt(word[1]), parseInt(word[2]));
cpuDetails.add(details);
}
return new VanillaCpuLayout(cpuDetails);
}
@NotNull
public static VanillaCpuLayout fromCpuInfo() throws IOException {
return fromCpuInfo("/proc/cpuinfo");
}
@NotNull
public static VanillaCpuLayout fromCpuInfo(String filename) throws IOException {
return fromCpuInfo(openFile(filename));
}
private static InputStream openFile(String filename) throws FileNotFoundException {
try {
return new FileInputStream(filename);
} catch (FileNotFoundException e) {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
if (is == null)
throw e;
return is;
}
}
@NotNull
public static VanillaCpuLayout fromCpuInfo(InputStream is) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line;
List<CpuInfo> cpuDetails = new ArrayList<CpuInfo>();
CpuInfo details = new CpuInfo();
Map<String, Integer> threadCount = new LinkedHashMap<String, Integer>();
while ((line = br.readLine()) != null) {
if (line.trim().isEmpty()) {
String key = details.socketId + "," + details.coreId;
Integer count = threadCount.get(key);
if (count == null)
threadCount.put(key, count = 1);
else
threadCount.put(key, count += 1);
details.threadId = count - 1;
cpuDetails.add(details);
details = new CpuInfo();
details.coreId = cpuDetails.size();
continue;
}
String[] words = line.split("\\s*:\\s*", 2);
if (words[0].equals("physical id"))
details.socketId = parseInt(words[1]);
else if (words[0].equals("core id"))
details.coreId = parseInt(words[1]);
}
return new VanillaCpuLayout(cpuDetails);
}
@Override
public int cpus() {
return cpuDetails.size();
}
public int sockets() {
return sockets;
}
public int coresPerSocket() {
return coresPerSocket;
}
@Override
public int threadsPerCore() {
return threadsPerCore;
}
@Override
public int socketId(int cpuId) {
return cpuDetails.get(cpuId).socketId;
}
@Override
public int coreId(int cpuId) {
return cpuDetails.get(cpuId).coreId;
}
@Override
public int threadId(int cpuId) {
return cpuDetails.get(cpuId).threadId;
}
@NotNull
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0, cpuDetailsSize = cpuDetails.size(); i < cpuDetailsSize; i++) {
CpuInfo cpuDetail = cpuDetails.get(i);
sb.append(i).append(": ").append(cpuDetail).append('\n');
}
return sb.toString();
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VanillaCpuLayout that = (VanillaCpuLayout) o;
if (coresPerSocket != that.coresPerSocket) return false;
if (sockets != that.sockets) return false;
if (threadsPerCore != that.threadsPerCore) return false;
return cpuDetails.equals(that.cpuDetails);
}
@Override
public int hashCode() {
int result = cpuDetails.hashCode();
result = 31 * result + sockets;
result = 31 * result + coresPerSocket;
result = 31 * result + threadsPerCore;
return result;
}
static class CpuInfo {
int socketId, coreId, threadId;
CpuInfo() {
}
CpuInfo(int socketId, int coreId, int threadId) {
this.socketId = socketId;
this.coreId = coreId;
this.threadId = threadId;
}
@NotNull
@Override
public String toString() {
return "CpuInfo{" +
"socketId=" + socketId +
", coreId=" + coreId +
", threadId=" + threadId +
'}';
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CpuInfo cpuInfo = (CpuInfo) o;
if (coreId != cpuInfo.coreId) return false;
if (socketId != cpuInfo.socketId) return false;
return threadId == cpuInfo.threadId;
}
@Override
public int hashCode() {
int result = socketId;
result = 31 * result + coreId;
result = 31 * result + threadId;
return result;
}
}
}