我在从 hashmap 读取数据时遇到了可伸缩性问题。我的机器有 32 个核心,每个核心有 2 个超线程(总共 64 个 CPU)和 64 GB RAM。 当从 HashMap 读取数据并进行算术计算时,我发现从 16 个线程开始性能下降,但在仅进行算术运算时它按预期缩放。
测试结果如下:
从HashMap中读取并进行算术运算:
线程数 |所用时间(秒)=> 1 | 85, 2 | 93, 4 | 124、 8 | 147、 16 |第644章
仅执行算术运算:
线程数 |所用时间(秒)=> 1 | 25, 2 | 32, 4 | 35, 8 | 41, 16 | 65, 32 | 108、 40 | 112、 64 | 117、 100 | 158
同时添加代码块以供引用:
import java.util.*;
import java.util.concurrent.*;
import java.lang.*;
public class StringCallable2
{
// private static final long size = 500000L;
private static final long size = 1000000L;
// private final static HashMap <Long,Long>map = new HashMap<Long, Long>();
// private static long[] array = new long[(int) size];
public static class StringGenCallable implements Callable
{
int count;
public StringGenCallable(int count)
{
this.count = count;
}
public Long call()
{
//Random rand = new Random();
// System.out.println("Thread " + count + " started test");
long sum = 20;
// do a CPU intensive arithmetic operation; no Input Output
// operations, object creations or floating point arithmetic
for (long i = 0; i < size; i++)
{
//int numNoRange = rand.nextInt((int)(size-1));
//long numNoRange = i;
// Long long1 = map.get((long)i);
//Long long1 = array[(int)i];
sum = i + 19 * sum;
}
// System.out.println("Finished " + count);
return sum;
}
}
public static void main(String args[])
{
try
{
System.out.println("Starting");
// for (long i = 0; i < size; i++)
// {
//array[(int)i] = System.currentTimeMillis();
// map.put(i, System.currentTimeMillis());
// }
int sizt = Integer.valueOf(args[0]);
long curtime = System.currentTimeMillis();
ExecutorService pool = Executors.newFixedThreadPool(sizt);
Set<Future<Integer>> set = new HashSet<Future<Integer>>();
for (int i = 0; i < sizt; i++)
{
Callable<Integer> callable = new StringGenCallable(i);
Future<Integer> future = pool.submit(callable);
set.add(future);
}
long sum = 0;
for (Future<Integer> future : set)
{
future.get();
}
System.out.println("Number of threads : "+sizt);
long finsihtime = System.currentTimeMillis();
System.out.println("Total Time Taken : " + (finsihtime - curtime)+" ms");
pool.shutdown();
// System.exit(sum);
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
catch (Error e) {
// TODO: handle exception
e.printStackTrace();
}
catch (Throwable e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
最佳答案
对于具有这种多处理级别的应用程序,您应该使用 ConcurrentHashMap .我会重新设计以纳入该更改,然后重新审视性能。
我还会仔细考虑您可以有效使用多少个线程。人们很容易将“添加更多线程”视为性能 Elixir ,但事实并非如此。您可以通过限制线程数并将当前共享的数据结构变为 ThreadLocal 来获得更多改进。 ,以减少数据共享以及由此产生的争用和上下文切换。
在这个例子中,即使假设你拥有这个进程的整个盒子,拥有超过 64 个线程也会使进程运行越来越慢,因为工作项完全受 CPU 限制。
在现实世界的应用程序中,工作单元可能比您在此处拥有的要复杂得多或运行时间更长。小心不要从对你的硬件来说是一个相当微不足道的每线程工作单元的东西中得出太多结论。关键是,相对于更复杂的工作负载,这里的线程管理开销相对于执行的工作被放大了。在更复杂的工作负载中,HashMap
中查找的可见效果可能会消失,性能看起来更符合您的预期。
关于java - 多线程多核系统中 HashMap 的可扩展性问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8050289/