我有可能是一个基本问题。当我创建1亿个Hashtables时,如果我在一个核心上执行它,我的机器上大约需要6秒钟(运行时间=每个核心6秒)。如果我在12个内核上执行多线程(我的机器有6个内核允许超线程),则需要大约10秒钟(运行时间=每个内核112秒)。
这是我使用的代码:
主要
public class Tests
{
public static void main(String args[])
{
double start = System.currentTimeMillis();
int nThreads = 12;
double[] runTime = new double[nThreads];
TestsThread[] threads = new TestsThread[nThreads];
int totalJob = 100000000;
int jobsize = totalJob/nThreads;
for(int i = 0; i < threads.length; i++)
{
threads[i] = new TestsThread(jobsize,runTime, i);
threads[i].start();
}
waitThreads(threads);
for(int i = 0; i < runTime.length; i++)
{
System.out.println("Runtime thread:" + i + " = " + (runTime[i]/1000000) + "ms");
}
double end = System.currentTimeMillis();
System.out.println("Total runtime = " + (end-start) + " ms");
}
private static void waitThreads(TestsThread[] threads)
{
for(int i = 0; i < threads.length; i++)
{
while(threads[i].finished == false)//keep waiting untill the thread is done
{
//System.out.println("waiting on thread:" + i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
发
import java.util.HashMap;
import java.util.Map;
public class TestsThread extends Thread
{
int jobSize = 0;
double[] runTime;
boolean finished;
int threadNumber;
TestsThread(int job, double[] runTime, int threadNumber)
{
this.finished = false;
this.jobSize = job;
this.runTime = runTime;
this.threadNumber = threadNumber;
}
public void run()
{
double start = System.nanoTime();
for(int l = 0; l < jobSize ; l++)
{
double[] test = new double[65];
}
double end = System.nanoTime();
double difference = end-start;
runTime[threadNumber] += difference;
this.finished = true;
}
}
我不明白为什么在多个线程中同时创建对象需要花费更长的时间,然后在1个线程中连续执行。如果我删除我创建Hashtable的行,这个问题就会消失。如果有人能帮助我,我将非常感激。
答案 0 :(得分:1)
更新:此问题已关联bug report,并已通过Java 1.7u40
修复。而Java 1.8
从来都不是问题,因为Java 8有一个完全不同的哈希表算法。
由于您没有使用创建的对象,操作将被优化掉。所以你只是在衡量创建线程的开销。这肯定是你开始的线程越多的开销。
我必须更正有关细节的答案,我还不知道:类Hashtable
和HashMap
有一些特别之处。它们都在构造函数中调用sun.misc.Hashing.randomHashSeed(this)
。换句话说,它们的实例在构造期间逃逸,这对存储器可见性有影响。这意味着它们的构造与ArrayList
不同,不能优化,并且多线程构造因该方法内部发生的事情(即同步)而减慢。
如上所述,这对这些类来说很特别,当然还有这个实现(我的设置:1.7.0_13
)。对于普通类,这种代码的构造时间直接为零。
在这里,我添加了更复杂的基准代码。观察DO_HASH_MAP = true
和DO_HASH_MAP = false
之间的区别(当false
创建ArrayList
而不会产生此类特殊行为时)。
import java.util.*;
import java.util.concurrent.*;
public class AllocBench {
static final int NUM_THREADS = 1;
static final int NUM_OBJECTS = 100000000 / NUM_THREADS;
static final boolean DO_HASH_MAP = true;
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService threadPool = Executors.newFixedThreadPool(NUM_THREADS);
Callable<Long> task=new Callable<Long>() {
public Long call() {
return doAllocation(NUM_OBJECTS);
}
};
long startTime=System.nanoTime(), cpuTime=0;
for(Future<Long> f: threadPool.invokeAll(Collections.nCopies(NUM_THREADS, task))) {
cpuTime+=f.get();
}
long time=System.nanoTime()-startTime;
System.out.println("Number of threads: "+NUM_THREADS);
System.out.printf("entire allocation required %.03f s%n", time*1e-9);
System.out.printf("time x numThreads %.03f s%n", time*1e-9*NUM_THREADS);
System.out.printf("real accumulated cpu time %.03f s%n", cpuTime*1e-9);
threadPool.shutdown();
}
static long doAllocation(int numObjects) {
long t0=System.nanoTime();
for(int i=0; i<numObjects; i++)
if(DO_HASH_MAP) new HashMap<Object, Object>(); else new ArrayList<Object>();
return System.nanoTime()-t0;
}
}
答案 1 :(得分:0)
如果你在6个内核上做什么呢?超线程与拥有两倍内核完全相同,因此您可能也想尝试实际内核的数量。
此外,操作系统不一定会将每个线程安排到自己的核心。
答案 2 :(得分:0)
由于你所做的只是测量时间和搅拌内存,你的瓶颈很可能是你的L3缓存或总线到主内存。在这种情况下,协调线程之间的工作可能会产生如此多的开销,而不是更好。
这对于评论来说太长了,但你的内部循环可能只是
double start = System.nanoTime();
for(int l = 0; l < jobSize ; l++){
Map<String,Integer> test = new HashMap<String,Integer>();
}
// runtime is an AtomicLong for thread safety
runtime.addAndGet(System.nanoTime() - start); // time in nano-seconds.
花时间可以创建一个HashMap,因此如果你经常调用计时器,你可能无法测量你的想法。
BTW Hashtable是同步的,您可能会发现使用HashMap更快,并且可能更具可扩展性。