多线程会随着内核的增加而变慢
Multithreading slows down with more cores
我正在尝试使用多线程实现 Eratosthenes 素数筛法。
线程由 treadpool
处理
ThreadPoolExecutor executor =
new ThreadPoolExecutor(
6, // cores
10, // threads
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(n)
);
它自己的筛子是这样实现的
for (int i=2; i<Math.sqrt(n); i++ ){
if (list[i].get()) { // boolean list initialized earlier
executor.execute(new Calc(i,n));
}
}
使用 运行 方法看起来像这样
public void run(){
for (int j=start*2;j<n;j=j+start){
list[j].set(false);
}
}
现在,我不明白的是,为什么当 corePoolSize
较低(其他参数保持不变)时,程序 运行 速度更快。 (2 核 2.5 毫秒;7 - 3.3 等)。
我想我可能犯了一些菜鸟错误,但我不明白为什么会这样?
编辑:
完整 class 看起来像这样(必须为集群使用单个 class,因此一切都在 Main:
int start;
int n;
Main(int start,int n){
this.start=start;
this.n=n;
}
public void run(){
for (int j=start*2;j<n;j=j+start){
list[j].set(true);
}
}
private static list myBoolean[];
public static void main(String[] args) {
int n = 20000;
list= new AtomicBoolean[n+1];
for (int i=2; i<n; i++ ){
list[i]=new AtomicBoolean(false);
}
ThreadPoolExecutor executor =
new ThreadPoolExecutor(
7,
10,
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(n));
long startedTime = System.nanoTime();
for (int i=2; i<Math.sqrt(n); i++ ){
if (list[i]){
executor.execute(new Main(i,n));
}
}
executor.shutdown();
try {
executor.awaitTermination(60, TimeUnit.MINUTES);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Work time "+(float)(System.nanoTime()-startedTime)/1000000+" s.");
}
}
这在很大程度上取决于你有多少核。如果你有 2 个内核并且你想在 7 个线程上执行,只会因为线程上下文切换而使事情变得更慢。这就是阿姆达尔定律。
还值得一提的是,执行器构造函数的前 2 个参数是 corePoolSize 和 maximumPoolSize java doc corePoolSize
表示要保留在池中的线程数,即使它们处于空闲状态 maximumPoolSize
是池中允许的最大线程数
无论如何,主要原因是您唯一要测量的是执行程序关闭所需的时间。您使用 false 初始化数组:list[i]=new AtomicBoolean(false);
那么你为线程池创建任务的唯一情况是当你找到一个真正的元素if (list[i]){
。因为没有真正的元素,所以这永远不会发生,所以没有任务 scheduled/executed。
我正在尝试使用多线程实现 Eratosthenes 素数筛法。 线程由 treadpool
处理ThreadPoolExecutor executor =
new ThreadPoolExecutor(
6, // cores
10, // threads
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(n)
);
它自己的筛子是这样实现的
for (int i=2; i<Math.sqrt(n); i++ ){
if (list[i].get()) { // boolean list initialized earlier
executor.execute(new Calc(i,n));
}
}
使用 运行 方法看起来像这样
public void run(){
for (int j=start*2;j<n;j=j+start){
list[j].set(false);
}
}
现在,我不明白的是,为什么当 corePoolSize
较低(其他参数保持不变)时,程序 运行 速度更快。 (2 核 2.5 毫秒;7 - 3.3 等)。
我想我可能犯了一些菜鸟错误,但我不明白为什么会这样?
编辑: 完整 class 看起来像这样(必须为集群使用单个 class,因此一切都在 Main:
int start;
int n;
Main(int start,int n){
this.start=start;
this.n=n;
}
public void run(){
for (int j=start*2;j<n;j=j+start){
list[j].set(true);
}
}
private static list myBoolean[];
public static void main(String[] args) {
int n = 20000;
list= new AtomicBoolean[n+1];
for (int i=2; i<n; i++ ){
list[i]=new AtomicBoolean(false);
}
ThreadPoolExecutor executor =
new ThreadPoolExecutor(
7,
10,
1000,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(n));
long startedTime = System.nanoTime();
for (int i=2; i<Math.sqrt(n); i++ ){
if (list[i]){
executor.execute(new Main(i,n));
}
}
executor.shutdown();
try {
executor.awaitTermination(60, TimeUnit.MINUTES);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Work time "+(float)(System.nanoTime()-startedTime)/1000000+" s.");
}
}
这在很大程度上取决于你有多少核。如果你有 2 个内核并且你想在 7 个线程上执行,只会因为线程上下文切换而使事情变得更慢。这就是阿姆达尔定律。
还值得一提的是,执行器构造函数的前 2 个参数是 corePoolSize 和 maximumPoolSize java doc corePoolSize
表示要保留在池中的线程数,即使它们处于空闲状态 maximumPoolSize
是池中允许的最大线程数
无论如何,主要原因是您唯一要测量的是执行程序关闭所需的时间。您使用 false 初始化数组:list[i]=new AtomicBoolean(false);
那么你为线程池创建任务的唯一情况是当你找到一个真正的元素if (list[i]){
。因为没有真正的元素,所以这永远不会发生,所以没有任务 scheduled/executed。