【问题标题】:Threading Isues With Fixed Thread Pool and Large Number of Tasks固定线程池和大量任务的线程问题
【发布时间】:2014-10-17 17:45:08
【问题描述】:

我正在使用一个程序从数学中运行 Collat​​z 猜想 (http://en.wikipedia.org/wiki/Collatz_conjecture)。我已经实现了一个运行猜想算法的类(并返回输出)和一个创建固定线程池(我的处理器数量:8)并接受Callables 的类,这是对猜想算法的调用。

我为 1(输入类型必须是正整数)到 400,000 之间的所有数字创建了一个 HashSet<Callable>。这(似乎)永远挂起,但较低的数字效果很好,这很奇怪。更奇怪的是,运行它来处理这些调用似乎比单个线程处理相同数量的信息需要更长的时间;它还会使内存显着膨胀。

例如,在我的计算机上,程序用不到一秒钟的时间来执行算法(仅一次迭代),其中包含 400,000(最终值),并且所有较低的值都需要更少的时间来计算(可能除了素数,这需要更长的时间)我正在运行具有 8GB 内存和 8 个 2.2Ghz 逻辑处理器的 Windows 8.1。

代码:

private static void initThreads() throws InterruptedException {
    //Files.createDirectories(SEQUENCER_FOLDER_PATH);
    //Files.createFile(SEQUENCER_FILE_PATH);
    ExecutorService service = Executors.newFixedThreadPool(8, new ThreadFactory() {
        private BigInteger count = BigInteger.ZERO;

        @Override
        public Thread newThread(Runnable r) {
            count = count.add(BigInteger.ONE);
            return new Thread(r, "Collatz Sequencer Thread: " + count);
        }
    });
    int finalNumber = 400_000;
    final HashSet<Callable<Void>> tasks = new HashSet<>(finalNumber);
    for (long l = 1; l <= finalNumber; l++) {
        final BigInteger number = BigInteger.valueOf(l);
        tasks.add(() -> {
            CollatzSequencer sequencer = new CollatzSequencer(new BigInteger(number.toString()));
            synchronized (dataSet) {
                dataSet.put(number, sequencer.init());
            }
            return null;
        });
    }
    service.invokeAll(tasks);
    Thread dataThread = new Thread(() -> {
        while (true) {
            synchronized (dataSet) {
                if (dataSet.size() == finalNumber) {
                    System.err.println("Values: \n");
                    for (CollatzSequencer.FinalSequencerReport data : dataSet.values()) {
                        System.err.println("Entry: " + data.getInitialValue() + ", " + data.getIterations());
                    }
                    System.exit(0);
                }
            }
        }
    }, "Collatz Conjecture Data Set Thread");
    dataThread.start();
}

Collat​​z 猜想算法:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.collatzsequencer.core;

import java.math.BigInteger;

/**
 * A sequencer used for computing the collatz sequence.
 *
 * @author Sarah Szabo
 * @version 1.0
 */
public class CollatzSequencer {

private final BigInteger initialValue;

public CollatzSequencer(BigInteger currentValue) {
    if (currentValue == null) {
        throw new NullPointerException("Value passed can't be null");
    } else if (currentValue.compareTo(new BigInteger("1")) < 0) {
        throw new NumberFormatException("The value passed to the constructor must be a natural number.");
    }
    this.initialValue = currentValue;
}

public FinalSequencerReport init() {
    return new FinalSequencerReport(performOperation(new SequencerReport(this.initialValue)), this.initialValue);
}

private SequencerReport performOperation(SequencerReport report) {
    if (report.getResult().equals(new BigInteger("1"))) {
        return new SequencerReport(report.getResult(), report.getIterations(), report.getSequence().length() > 1
                ? report.getSequence().substring(0, report.getSequence().length() - 3) : "The sequence starts and ends at 1 <Nothing Done>");
    } else if (report.getResult().mod(new BigInteger("2")).equals(new BigInteger("0"))) {
        BigInteger value = report.getResult().divide(new BigInteger("2"));
        return performOperation(new SequencerReport(value, report.getIterations().add(new BigInteger("1")),
                report.getSequence() + " " + report.getResult() + "/2 -> " + value + " ->"));
    } else {
        BigInteger value = report.getResult().multiply(new BigInteger("3")).add(new BigInteger("1"));
        return performOperation(new SequencerReport(value, report.getIterations()
                .add(new BigInteger("1")), report.getSequence() + report.getResult() + " * 3 + 1 ->" + value + " ->"));
    }
}

public static final class FinalSequencerReport extends SequencerReport {

    private final BigInteger initialValue;
    private final String finalFormattedString;

    public FinalSequencerReport(SequencerReport finalReport, BigInteger initialValue) {
        super(finalReport.getResult(), finalReport.getIterations(), finalReport.getSequence());
        this.initialValue = initialValue;
        this.finalFormattedString = "Initial Value: "
                + getInitialValue() + "\nFinal Value: " + getResult() + "\nIterations:  "
                + getIterations() + "\nAlgebraic Sequence:\n" + getSequence();
    }

    public String getFinalFormattedString() {
        return finalFormattedString;
    }

    public BigInteger getInitialValue() {
        return initialValue;
    }
}

public static class SequencerReport {

    private final BigInteger result, iterations;
    private final String sequence;

    public SequencerReport(BigInteger result) {
        this(result, new BigInteger("0"), "");
    }

    public SequencerReport(BigInteger result, BigInteger iterations, String sequence) {
        this.result = result;
        this.iterations = iterations;
        this.sequence = sequence;
    }

    public BigInteger getResult() {
        return this.result;
    }

    public BigInteger getIterations() {
        return this.iterations;
    }

    public String getSequence() {
        return this.sequence;
    }

  }
}

【问题讨论】:

  • 您在这里需要BigInteger,还是long 就足够了?最大长度为2^63-1,或9.22 x 10^18
  • 我没有看到您的 ExecutorService 或您的任务在任何地方被使用,您是否忘记显示部分代码?

标签: java multithreading performance algorithm math


【解决方案1】:

正如您所说,您的代码有效;问题可能只是性能。我会尝试一些事情:

  1. 使用long 而不是BigIntegerBigInteger 很慢。
  2. 代替mod 2(或% 2),使用 &amp; 1。二进制 AND 将有效地产生相同的结果并且速度更快。
  3. 你做的太多了,String 操纵太多了。覆盖 sequencerReport.toString() 并在打印数据时让它在最后执行所有 toString 调用。
  4. 不要做new ThreadFactory()。使用Guava's ThreadFactoryBuilder
    • 永远不要在代码中调用new Thread(),除非您真的知道自己在做什么,也就是说不要这样做。
  5. dataThread 添加一个等待/通知机制,而不是一个繁忙的循环。工作完成后调用dataSet.notify() 并在dataThread 正文中调用dataSet.wait()

【讨论】:

  • 我会添加另一个性能提示。 dataThread 正在执行一个繁忙的循环,在等待终止条件时会消耗过多的 CPU。等待/通知机制可能会更好。
  • @EyalSchneider 谢谢,错过了
  • 如果您告诉我 Java 编译器不会将 a%2 替换为 a&amp;1,我会非常失望。在你们中的一些人出生之前,GCC 就一直在进行这种优化。
  • @jameslarge 可能,但如果这里确实需要BigInteger,那么肯定有比mod 更好的方法。除非BigInteger 也进行了优化。
  • @jameslarge 它不能,因为它不知道,它不是负面的。它使用一个更复杂的表达式来解释符号,因此最好手动进行这种微不足道的微优化。
猜你喜欢
  • 2021-11-17
  • 2022-01-18
  • 1970-01-01
  • 2015-02-28
  • 1970-01-01
  • 1970-01-01
  • 2010-11-10
  • 2014-08-07
  • 1970-01-01
相关资源
最近更新 更多