需要帮助并行穿越D中的dag

时间:2013-10-22 21:18:34

标签: parallel-processing d graph-traversal

编辑:好的,这是一个更简单的例子来说明我的问题。为什么只有第一个任务进入队列?

import std.stdio;
import std.parallelism;

void simpleWorker(uint depth, uint maxDepth, TaskPool pool){
    writeln("Depth is: ",depth);
    if (++depth < maxDepth){
        pool.put( task!simpleWorker(depth,maxDepth,pool));
    }
}

void main(){
    auto pool = new TaskPool();
    pool.put(task!simpleWorker(0,5,pool));
    pool.finish(true);
    writeln("Done");
}

原件:

我需要遍历这个DAG。当我访问节点时,我将其清理干净。我不能清理一个节点,直到它的所有父母都干净。

我正在尝试的方法是让工作线程的当前节点检查其所有子节点以查看可以处理哪些子节点。任何可以处理的内容都会添加到TaskPool中。

我的问题是我无法弄清楚如何将新任务添加到TaskPool并让它们得到处理。这只是清理DAG中的第一个节点,然后退出,将其他所有内容都弄脏了。

void cleanNode(Node node, TaskPool pool){
    node.doProcess();
    foreach (client; node.clients){
        if (client.canProcess()){
            pool.put(task!cleanNode(client, pool));
        }
    }
}

void main(){
    auto dag = mkTestDag(5);
    auto pool = new TaskPool();

    pool.put( task!cleanNode(dag[0], pool));
    pool.finish(true); 

    writeln("\n\nOutput:");
    foreach (d;dag){
        writeln(d);
        writeln(d.dirty ? "dirty" : "clean","\n");
    }
}

完整代码在此处:http://pastebin.com/LLfMyKVp

3 个答案:

答案 0 :(得分:1)

这是因为在simpleWorker中放入了一个错误。

此版本显示错误:

import std.stdio;
import std.parallelism;

void simpleWorker(uint depth, uint maxDepth, TaskPool pool){
    writeln("Depth is: ",depth);
    if (++depth < maxDepth){
        try {
            pool.put( task!simpleWorker(depth,maxDepth,pool));
        } catch (Error e) {
            writeln("Fail: ", e.msg);
        }
    }
}

void main(){
    auto pool = new TaskPool();
    pool.put(task!simpleWorker(0,5,pool));
    pool.finish(true);
    writeln("Done");
}

输出:

Depth is: 0
Fail: Cannot submit a new task to a pool after calling finish() or stop().
Done

希望其他人可以解释使用TaskPool的正确方法。

修改

通过告诉任务如下运行来实现它:

import std.stdio;
import std.parallelism;

void simpleWorker(uint depth, uint maxDepth, TaskPool pool){
    writeln("Depth is: ",depth);
    if (++depth < maxDepth){
        try 
        {
            auto subWorker = task!simpleWorker(depth,maxDepth, pool);
            pool.put(subWorker);
            subWorker.yieldForce();
        } catch (Error t) {
            writeln("Fail: (",  typeof(t).stringof, ") ", t.msg);
        }
    }
}

void main(){
    auto pool = new TaskPool();

    auto worker = task!simpleWorker(0,5, pool);
    pool.put(worker);
    worker.yieldForce();

    pool.finish(true);
    writeln("Done");
}

输出:

Depth is: 0
Depth is: 1
Depth is: 2
Depth is: 3
Depth is: 4
Done

答案 1 :(得分:0)

您有这样的行为,因为除了根节点之外,所有节点canProcess()都返回false。由于cleanNodeSimple()调用canProcess()来检查是否将新任务放入池中,所以我从不这样做... 检查此(修改的)程序的输出:http://dpaste.dzfl.pl/ea0c393a

编辑: 此外,经过一些分析后,循环也只执行一次,因此没有任何内容添加到任务池中。 :)

代码在DPaste上,请查看。以下是可能有助于您解决问题的最有趣的部分:

// Dejan: will be called only once!
// Because canProcess() returns false for all children.
void cleanNodeSimple(Node node, TaskPool pool){
  node.doProcess();
  writeln("cleanNodeSimple() called for node `", node, "`"); 
  foreach (cli; node.clients){
    writeln("I am ", cli, " and I am executed only once, because I am only RootNode's client.");
    if (cli.canProcess()) {
      writeln("I will be executed only once because because ", cli, " has ", cli.clients.length, " clients.");
      writeln("And its master #1 is ", cli.clients[0].masters[0].dirty ? "dirty" : "clean");
      pool.put( task!cleanNodeSimple(cli, pool) );
    }
  }
}

答案 2 :(得分:0)

以并行方式迭代,您可以使用parallel

foreach (client; pool.parallel(node.clients,1)){
    if (client.canProcess()){
        cleanNode(client, pool);//<- EDIT: no need to make a task here
    }
}

这将阻塞,直到所有客户端都被迭代(让当前线程也做一些工作)

开始你需要force main()中的第一个任务,而不是在游泳池上调用完成