解释起来有点复杂,但我们走了。基本上,问题是“如何以有效的方式将问题分解为子问题”。这里的“高效”意味着,破碎的子问题尽可能大。基本上,如果我不必分解问题,那将是理想的。但是,因为一个工人只能处理问题的特定块,我确实需要分手。但我想找到一种尽可能粗略的方法。
这是一些伪代码..
我们遇到这样的问题(对不起,这是用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);
}
}
表示不快。
所以,我们有很多Problem
和Workers
不断要求更多SubProblems
。我的任务是将Problems
分解为SubProblem
并将其分发给他们。然而,困难在于,我必须稍后收集SubProblems的所有结果,并将它们合并(缩减)为整个Result
的{{1}}。
然而,这是昂贵的,所以我想给工人“尽可能大的块”(尽可能多Problem
)。
它不一定是完美的(尽可能在数学上尽可能高效)。我的意思是,我认为不可能有一个完美的解决方案,因为你无法预测每个计算需要多长时间等等。但是有一个很好的启发式解决方案吗?或者也许在我进入设计之前我可以阅读一些资源?
非常感谢任何建议!
编辑: 我们也控制了部分分配,因此控制这是另一种选择。基本上,对此的唯一限制是工人只能拥有固定数量的部分。
答案 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);
}
}
}