java - 是否有任何 "threshold"证明多线程计算是合理的?

标签 java multithreading

所以基本上我今天需要优化这段代码。它试图找到某个函数为前百万个起始数字生成的最长序列:

public static void main(String[] args) {
    int mostLen = 0;
    int mostInt = 0;
    long currTime = System.currentTimeMillis();
    for(int j=2; j<=1000000; j++) {
        long i = j;
        int len = 0;
        while((i=next(i)) != 1) {
            len++;
        }
        if(len > mostLen) {
            mostLen = len;
            mostInt = j;
        }
    }
    System.out.println(System.currentTimeMillis() - currTime);
    System.out.println("Most len is " + mostLen + " for " + mostInt);
}


static long next(long i) {
    if(i%2==0) {
        return i/2;
    } else {
        return i*3+1;
    }
}

我的错误是试图引入多线程:

void doSearch() throws ExecutionException, InterruptedException {
    final int numProc = Runtime.getRuntime().availableProcessors();
    System.out.println("numProc = " + numProc);
    ExecutorService executor = Executors.newFixedThreadPool(numProc);
    long currTime = System.currentTimeMillis();
    List<Future<ValueBean>> list = new ArrayList<Future<ValueBean>>();
    for (int j = 2; j <= 1000000; j++) {
        MyCallable<ValueBean> worker = new MyCallable<ValueBean>();
        worker.setBean(new ValueBean(j, 0));
        Future<ValueBean> f = executor.submit(worker);
        list.add(f);
    }
    System.out.println(System.currentTimeMillis() - currTime);

    int mostLen = 0;
    int mostInt = 0;
    for (Future<ValueBean> f : list) {
        final int len = f.get().getLen();
        if (len > mostLen) {
            mostLen = len;
            mostInt = f.get().getNum();
        }
    }
    executor.shutdown();
    System.out.println(System.currentTimeMillis() - currTime);
    System.out.println("Most len is " + mostLen + " for " + mostInt);
}

public class MyCallable<T> implements Callable<ValueBean> {
    public ValueBean bean;

    public void setBean(ValueBean bean) {
        this.bean = bean;
    }

    public ValueBean call() throws Exception {
        long i = bean.getNum();
        int len = 0;
        while ((i = next(i)) != 1) {
            len++;
        }
        return new ValueBean(bean.getNum(), len);
    }
}

public class ValueBean {
    int num;
    int len;

    public ValueBean(int num, int len) {
        this.num = num;
        this.len = len;
    }

    public int getNum() {
        return num;
    }

    public int getLen() {
        return len;
    }
}

long next(long i) {
    if (i % 2 == 0) {
        return i / 2;
    } else {
        return i * 3 + 1;
    }
}

不幸的是,在 4 个处理器(内核)上,多线程版本的运行速度比单线程版本慢 5 倍。

然后我尝试了一些更粗略的方法:

static int mostLen = 0;
static int mostInt = 0;

synchronized static void updateIfMore(int len, int intgr) {
    if (len > mostLen) {
        mostLen = len;
        mostInt = intgr;
    }
}

public static void main(String[] args) throws InterruptedException {
    long currTime = System.currentTimeMillis();
    final int numProc = Runtime.getRuntime().availableProcessors();
    System.out.println("numProc = " + numProc);
    ExecutorService executor = Executors.newFixedThreadPool(numProc);
    for (int i = 2; i <= 1000000; i++) {
        final int j = i;
        executor.execute(new Runnable() {
            public void run() {
                long l = j;
                int len = 0;
                while ((l = next(l)) != 1) {
                    len++;
                }
                updateIfMore(len, j);
            }
        });
    }
    executor.shutdown();
    executor.awaitTermination(30, TimeUnit.SECONDS);
    System.out.println(System.currentTimeMillis() - currTime);
    System.out.println("Most len is " + mostLen + " for " + mostInt);
}


static long next(long i) {
    if (i % 2 == 0) {
        return i / 2;
    } else {
        return i * 3 + 1;
    }
}

它工作得更快,但仍然比单线程方法慢。

我希望这不是因为我搞砸了我执行多线程的方式,而是这个特定的计算/算法不适合并行计算。如果我通过将方法 next 替换为以下内容来更改计算以使其更加处理器密集:

long next(long i) {
    Random r = new Random();
    for(int j=0; j<10; j++) {
        r.nextLong();
    }
    if (i % 2 == 0) {
        return i / 2;
    } else {
        return i * 3 + 1;
    }
}

在 4 核机器上,两个多线程版本的执行速度都是单线程版本的两倍多。

很明显,必须有一些阈值可以用来确定是否值得引入多线程,我的问题是:

什么基本规则可以帮助确定给定的计算是否足够密集以通过并行运行来优化它(无需花费精力实际实现它?)

最佳答案

有效实现多线程的关键是确保成本不会太高。没有固定的规则,因为它们在很大程度上取决于您的硬件。

启动和停止线程的成本很高。当然,您已经使用了可大大降低这些成本的执行器服务,因为它使用一堆工作线程来执行您的 Runnable。然而,每个 Runnable 仍然有一些开销。减少 runnable 的数量并增加每个 runnable 的工作量将提高性能,但您仍然希望 executor 服务有足够的 runnable 以有效地将它们分配到工作线程。

您已选择为每个起始值创建一个可运行对象,因此您最终创建了 1000000 个可运行对象。如果让每个 Runnable 执行一批 1000 个起始值,您可能会得到更好的结果。这意味着您只需要 1000 个可运行实体,大大减少了开销。

关于java - 是否有任何 "threshold"证明多线程计算是合理的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10696079/

相关文章:

python-3.x - 我应该如何设置 spaCy 服务器来处理多个并发请求(非阻塞)?

java - JVM可以在单线程程序上调用中断吗?

c++ - 在 C++ 中使用并行化的预期加速是多少(不是 OpenMp,而是 <thread>)

.net - .net thread.sleep不准确

c# - 在工作线程上创建的WinForms窗口未收到所有预期的消息

java - 如何隐藏/删除 SWT 表中的列

java - 如何按照我想要的方式准确定位按钮?

Java 通用可序列化、可迭代堆栈

java - Apache solr 配置与 tomcat 6.0

java 无法保留一个实体,可能是因为 boolean 字段