是否有任何"阈值"证明多线程计算?

Ole*_*eev 7 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,多线程版本的工作速度比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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我希望这不是因为我搞砸了我正在做多线程的方式,而是这个特定的计算/算法不适合并行计算.如果我通过用以下方法替换方法来更改计算以使其更加处理器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;
    }
}
Run Code Online (Sandbox Code Playgroud)

多线程版本的启动速度比4核机器上的单线程版本快两倍.

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

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

Mik*_*ray 2

我认为还有另一个你没有考虑到的因素。当工作单元彼此不依赖时,并行化效果最佳。当后来的计算结果依赖于早期的计算结果时,并行运行计算并不是最优的。从“我需要第一个值来计算第二个值”的意义上来说,依赖性可能很强。在这种情况下,任务是完全串行的,如果不等待早期的计算,就无法计算后面的值。在“如果我有第一个值,我可以更快地计算第二个值”的意义上,也可能存在较弱的依赖性。在这种情况下,并行化的代价是某些工作可能会重复。

这个问题适合在没有多线程的情况下进行优化,因为如果您已经掌握了先前的结果,则可以更快地计算后面的一些值。举个例子j == 4。一旦完成内部循环,就会产生i == 2,但您刚刚计算了两次迭代前的结果j == 2,如果您保存了 的值,len则可以将其计算为 len(4) = 1 + len(2)。

使用数组来存储之前计算的值len,并在方法中稍微调整一下next,您可以以 50 倍以上的速度完成任务。