分配工作的高效算法?

时间:2010-03-27 21:27:37

标签: java distributed mapreduce concurrent-programming

解释起来有点复杂,但我们走了。基本上,问题是“如何以有效的方式将问题分解为子问题”。这里的“高效”意味着,破碎的子问题尽可能大。基本上,如果我不必分解问题,那将是理想的。但是,因为一个工人只能处理问题的特定块,我确实需要分手。但我想找到一种尽可能粗略的方法。

这是一些伪代码..

我们遇到这样的问题(对不起,这是用Java。如果你不明白,我很乐意解释)。

class Problem {
    final Set<Integer> allSectionIds = { 1,2,4,6,7,8,10 };
    final Data data = //Some data
}

一个子问题是:

class SubProblem {
    final Set<Integer> targetedSectionIds;
    final Data data;

    SubProblem(Set<Integer> targetedSectionsIds, Data data){
        this.targetedSectionIds = targetedSectionIds;
        this.data = data;
    }
}

那么工作会是这样的。

class Work implements Runnable {
    final Set<Section> subSections;
    final Data data;
    final Result result;

    Work(Set<Section> subSections, Data data) {
        this.sections = SubSections;
        this.data = data;
    }

    @Override
    public void run(){
        for(Section section : subSections){
            result.addUp(compute(data, section));
        }
    }
}

现在我们有'Worker'的实例,它们有自己的状态sections I have

class Worker implements ExecutorService {
    final Map<Integer,Section> sectionsIHave;
    {
        sectionsIHave = {1:section1, 5:section5, 8:section8 };
    }

    final ExecutorService executor = //some executor.

    @Override
    public void execute(SubProblem problem){
        Set<Section> sectionsNeeded = fetchSections(problem.targetedSectionIds);
        super.execute(new Work(sectionsNeeded, problem.data);
    }

}

表示不快。

所以,我们有很多ProblemWorkers不断要求更多SubProblems。我的任务是将Problems分解为SubProblem并将其分发给他们。然而,困难在于,我必须稍后收集SubProblems的所有结果,并将它们合并(缩减)为整个Result的{​​{1}}。

然而,这是昂贵的,所以我想给工人“尽可能大的块”(尽可能多Problem)。

它不一定是完美的(尽可能在数学上尽可能高效)。我的意思是,我认为不可能有一个完美的解决方案,因为你无法预测每个计算需要多长时间等等。但是有一个很好的启发式解决方案吗?或者也许在我进入设计之前我可以阅读一些资源?

非常感谢任何建议!

编辑: 我们也控制了部分分配,因此控制这是另一种选择。基本上,对此的唯一限制是工人只能拥有固定数量的部分。

1 个答案:

答案 0 :(得分:1)

好的,似乎你有一个网络服务的分片模型,我们做类似的事情,我们使用反向索引“entityId”(sectionId)到将连接到特定网络的“客户端”(工作者)将处理该特定实体的服务。最简单的方法(见下文)是使用id到worker的反向映射。如果内存是约束,则另一种可能性是使用函数(例如sectionId%服务数)。

为了尽可能多地为服务提供服务,有一个简单的批处理算法将批量填充到某个用户指定的最大值。这将允许根据远程服务能够消耗它们的速度大致调整工作块大小。

public class Worker implements Runnable {

    private final Map<Integer, Section> sections;
    private final BlockingQueue<SubProblem> problemQ = new ArrayBlockingQueue<SubProblem>(4096);
    private final int batchSize;

    public Worker(final Map<Integer, Section> sectionsIHave, final int batchSize) {
        this.sections = sectionsIHave;
        this.batchSize = batchSize;
    }

    public Set<Integer> getSectionIds() {
        return sections.keySet();
    }

    public void execute(final SubProblem command) throws InterruptedException {

        if (sections.containsKey(command.getSectionId())) {
            problemQ.put(command);
        } else {
            throw new IllegalArgumentException("Invalid section id for worker: " + command.getSectionId());
        }

    }

    @Override
    public void run() {
        final List<SubProblem> batch = new ArrayList<SubProblem>(batchSize);
        while (!Thread.interrupted()) {
            batch.clear();

            try {
                batch.add(problemQ.take());
                for (int i = 1; i < batchSize; i++) {
                    final SubProblem problem = problemQ.poll();
                    if (problem != null) {
                        batch.add(problem);
                    } else {
                        break;
                    }

                    process(batch);
                }
            } catch (final InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private void process(final List<SubProblem> batch) {
        // Submit to remote process.
    }

    private static Map<Integer, Worker> indexWorkers(final List<Worker> workers) {
        final Map<Integer, Worker> temp = new HashMap<Integer, Worker>();
        for (final Worker worker : workers) {
            for (final Integer sectionId : worker.getSectionIds()) {
                temp.put(sectionId, worker);
            }
        }
        return Collections.unmodifiableMap(temp);
    }

    public static void main(final String[] args) throws InterruptedException {
     // Load workers, where worker is bound to single remote service
        final List<Worker> workers = getWorkers();
        final Map<Integer, Worker> workerReverseIndex = indexWorkers(workers);
        final List<SubProblem> subProblems = getSubProblems();
        for (final SubProblem problem : subProblems) {
            final Worker w = workerReverseIndex.get(problem.getSectionId());
            w.execute(problem);
        }
    }
}