Refactor the dataflow mode logic#121
Conversation
|
Thanks @ShangkunLi,
Why we need this? It sounds different iterations are simultaneously executing, isn't it a mess? Shouldn't it be iter by iter (though this is not dataflow's convention)? In other words, for dataflow, there shouldn't be "iter" concept, we are iterating
@itemkelvin and I discussed topological ordering, and he did start from independent ops as follows, why it doesn't help? And a general comment:
|
|
After obtaining the independent op, the original dependency graph will only check for register dependencies when the op is first executed to ensure the order of subsequent propagation of the op. After the initial execution, the dependency graph is actually empty.
…------------------ Original ------------------
From: Cheng Tan ***@***.***>
Date: Mon,Aug 25,2025 0:37 PM
To: coredac/dataflow ***@***.***>
Cc: item ***@***.***>, Mention ***@***.***>
Subject: Re: [coredac/dataflow] Refactor the dataflow mode logic (PR #121)
@tancheng commented on this pull request.
In tools/neura-interpreter/neura-interpreter.cpp:
> + std::vector<Operation *> op_seq; + for (auto &block : func.getBody()) { + for (auto &op : block.getOperations()) { op_seq.emplace_back(&op); } } - // Tracks operation dependencies with dependency graph. - DependencyGraph consumer_dependent_on_producer_graph; - consumer_dependent_on_producer_graph.build(op_seq); - std::vector<Operation*> independent_ops = findIndependentInSequence(op_seq); + // Identifies reserve and constant operations. + llvm::DenseSet<Value> reserve_values; + llvm::DenseSet<Value> constant_values; + + for (Operation *op : op_seq) { + if (auto reserve_op = dyn_cast<neura::ReserveOp>(op)) { + reserve_values.insert(reserve_op.getResult()); + } else if (isa<neura::ConstantOp>(op) || isa<neura::GrantOnceOp>(op)) { + constant_values.insert(op->getResult(0)); + } + } + + // Tracks operation dependencies with dependency graph. + DependencyGraph dependency_graph; + dependency_graph.build(op_seq); + // Initializes executable operations (no unsatisfied dependencies). + std::vector<Operation *> executable_ops = + dependency_graph.getExecutableOperations();
I mean what is the difference? Why it didn't work well?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Because in previous implementation, it only respect the topological order in The bug is: when all the consumer of |
Imagine you are executing the DFG in CGRA in a modulo scheduling manner. We need to respect the topological order in a DFG while respecting the recurrence dependence when we want to execute the DFG in the next DFG iteration. The problem of executing based on the For example, for the dataflow ir given in the pr description. The output based on the previous interpreter is In iteration 3, |
It seems that I am using the Anyway, I have switched to |
Can this somehow be automated? |
Can you help me understand why we need "iteration" here? Why can't we just keep a |
We can add some actions to automatically generate formatted code after merging. |
|
Because the op_queue here is implemented not with a queue but a vector. The iteration here does not refer to the iteration in a for loop; instead, it means that the new instructions propagated after the execution of an instruction are stored in a buffer. Only after all ops in the op_queue have been executed will the buffer be used to update the op_queue.
…------------------ Original ------------------
From: Cheng Tan ***@***.***>
Date: Mon,Aug 25,2025 2:22 PM
To: coredac/dataflow ***@***.***>
Cc: item ***@***.***>, Mention ***@***.***>
Subject: Re: [coredac/dataflow] Refactor the dataflow mode logic (PR #121)
tancheng left a comment (coredac/neura#121)
Thanks @ShangkunLi,
DFG resetting/switching enables iteratively executing a DFG for different loop iterations until we get a valid return value
Why we need this? It sounds different iterations are simultaneously executing, isn't it a mess? Shouldn't it be iter by iter (though this is not dataflow's convention)? In other words, for dataflow, there shouldn't be "iter" concept, we are iterating ready_to_execute_ops based on dependency and is_udpated, right?
Imagine you are executing the DFG in CGRA in a modulo scheduling manner. We need to respect the topological order in a DFG while respecting the recurrence dependence when we want to execute the DFG in the next DFG iteration.
The problem of executing based on the is_updated is that we only focus on the data update and ignore the topological dependence in DFG belonging to different DFG iterations.
For example, for the dataflow ir given in the pr description. The output based on the previous interpreter is
[neura-interpreter] Initial pending operation: %0 = "neura.grant_once"() <{constant_value = 0.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %1 = "neura.grant_once"() <{constant_value = 1.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %2 = "neura.grant_once"() <{constant_value = 3.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %3 = "neura.grant_once"() <{constant_value = 5.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %4 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %6 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %8 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %10 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Initial pending operation: %12 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ---------------------------------------- [neura-interpreter] Iteration 1 | pending_operation_queue: 9 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %0 = "neura.grant_once"() <{constant_value = 0.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_once: [neura-interpreter] ├─ Constant value: 0.000000e+00 [neura-interpreter] ├─ First access - granting predicate [neura-interpreter] └─ Result: value = 0.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %13 = "neura.phi"(%12, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %11 = "neura.phi"(%10, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %1 = "neura.grant_once"() <{constant_value = 1.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_once: [neura-interpreter] ├─ Constant value: 1.000000e+00 [neura-interpreter] ├─ First access - granting predicate [neura-interpreter] └─ Result: value = 1.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %5 = "neura.phi"(%4, %1) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %2 = "neura.grant_once"() <{constant_value = 3.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_once: [neura-interpreter] ├─ Constant value: 3.000000e+00 [neura-interpreter] ├─ First access - granting predicate [neura-interpreter] └─ Result: value = 3.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %7 = "neura.phi"(%6, %2) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %3 = "neura.grant_once"() <{constant_value = 5.000000e+00 : f32}> : () -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_once: [neura-interpreter] ├─ Constant value: 5.000000e+00 [neura-interpreter] ├─ First access - granting predicate [neura-interpreter] └─ Result: value = 5.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %9 = "neura.phi"(%8, %3) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %4 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Executing neura.reserve: [neura-interpreter] └─ Created placeholder : %4 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ├─ Initial value : 0.0f [neura-interpreter] ├─ Initial predicate : false [neura-interpreter] └─ Type : !neura.data<f32, i1> [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %6 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Executing neura.reserve: [neura-interpreter] └─ Created placeholder : %6 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ├─ Initial value : 0.0f [neura-interpreter] ├─ Initial predicate : false [neura-interpreter] └─ Type : !neura.data<f32, i1> [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %8 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Executing neura.reserve: [neura-interpreter] └─ Created placeholder : %8 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ├─ Initial value : 0.0f [neura-interpreter] ├─ Initial predicate : false [neura-interpreter] └─ Type : !neura.data<f32, i1> [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %10 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Executing neura.reserve: [neura-interpreter] └─ Created placeholder : %10 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ├─ Initial value : 0.0f [neura-interpreter] ├─ Initial predicate : false [neura-interpreter] └─ Type : !neura.data<f32, i1> [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %12 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] Executing neura.reserve: [neura-interpreter] └─ Created placeholder : %12 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ├─ Initial value : 0.0f [neura-interpreter] ├─ Initial predicate : false [neura-interpreter] └─ Type : !neura.data<f32, i1> [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ---------------------------------------- [neura-interpreter] Iteration 2 | pending_operation_queue: 5 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %13 = "neura.phi"(%12, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [1] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 0.000000e+00,pred = 0 [neura-interpreter] │ └─[1]:value = 0.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 0.000000e+00, pred = 1, is_updated = 1 [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %15 = neura.grant_predicate %13, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %14 = "neura.fcmp"(%13, %9) <{cmpType = "lt"}> : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<i1, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %11 = "neura.phi"(%10, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [1] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 0.000000e+00,pred = 0 [neura-interpreter] │ └─[1]:value = 0.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 0.000000e+00, pred = 1, is_updated = 1 [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %21 = neura.grant_predicate %11, %20 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %16 = neura.grant_predicate %11, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %5 = "neura.phi"(%4, %1) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [1] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 0.000000e+00,pred = 0 [neura-interpreter] │ └─[1]:value = 1.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 1.000000e+00, pred = 1, is_updated = 1 [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %18 = neura.grant_predicate %5, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %7 = "neura.phi"(%6, %2) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [1] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 0.000000e+00,pred = 0 [neura-interpreter] │ └─[1]:value = 3.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 3.000000e+00, pred = 1, is_updated = 1 [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %17 = neura.grant_predicate %7, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %9 = "neura.phi"(%8, %3) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [1] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 0.000000e+00,pred = 0 [neura-interpreter] │ └─[1]:value = 5.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 5.000000e+00, pred = 1, is_updated = 1 [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %19 = neura.grant_predicate %9, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ---------------------------------------- [neura-interpreter] Iteration 3 | pending_operation_queue: 7 [neura-interpreter] Skipping operation (dependencies not satisfied): %15 = neura.grant_predicate %13, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %14 = "neura.fcmp"(%13, %9) <{cmpType = "lt"}> : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<i1, i1> [neura-interpreter] Executing neura.fcmp: [neura-interpreter] ├─ Operands [neura-interpreter] │ ├─ LHS : value = 0.000000e+00, [pred = 1] [neura-interpreter] │ └─ RHS : value = 5.000000e+00, [pred = 1] [neura-interpreter] ├─ Evaluation [neura-interpreter] │ ├─ Comparison type : lt [neura-interpreter] │ └─ Comparison result : true [neura-interpreter] └─ Result : value = 1.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %20 = "neura.not"(%14) : (!neura.data<i1, i1>) -> !neura.data<i1, i1> [neura-interpreter] Added user to next pending_operation_queue: %15 = neura.grant_predicate %13, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Skipping operation (dependencies not satisfied): %21 = neura.grant_predicate %11, %20 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %16 = neura.grant_predicate %11, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 0.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Granted access [neura-interpreter] └─ Result: value = 0.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %22 = "neura.fadd"(%16, %17) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %18 = neura.grant_predicate %5, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 1.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Granted access [neura-interpreter] └─ Result: value = 1.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %18 -> %4 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %23 = "neura.fadd"(%15, %18) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %17 = neura.grant_predicate %7, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 3.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Granted access [neura-interpreter] └─ Result: value = 3.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %17 -> %6 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %19 = neura.grant_predicate %9, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 5.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Granted access [neura-interpreter] └─ Result: value = 5.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %19 -> %8 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] ---------------------------------------- [neura-interpreter] Iteration 4 | pending_operation_queue: 7 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %20 = "neura.not"(%14) : (!neura.data<i1, i1>) -> !neura.data<i1, i1> [neura-interpreter] Executing neura.not: [neura-interpreter] ├─ Operand [neura-interpreter] │ └─ Input : value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Evaluation [neura-interpreter] │ └─ Logical NOT : !1 [neura-interpreter] └─ Result : value = 0.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %21 = neura.grant_predicate %11, %20 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %15 = neura.grant_predicate %13, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 0.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Granted access [neura-interpreter] └─ Result: value = 0.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %22 = "neura.fadd"(%16, %17) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.fadd: [neura-interpreter] ├─ Operands [neura-interpreter] │ ├─ LHS : value = 0.000000e+00 [pred = 1] [neura-interpreter] │ └─ RHS : value = 3.000000e+00 [pred = 1] [neura-interpreter] └─ Result : value = 3.000000e+00 [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %22 -> %10 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %18 -> %4 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %18 = neura.grant_predicate %5, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %4 = neura.reserve : !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 1) [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %18 -> %4 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %5 = "neura.phi"(%4, %1) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %23 = "neura.fadd"(%15, %18) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.fadd: [neura-interpreter] ├─ Operands [neura-interpreter] │ ├─ LHS : value = 0.000000e+00 [pred = 1] [neura-interpreter] │ └─ RHS : value = 1.000000e+00 [pred = 1] [neura-interpreter] └─ Result : value = 1.000000e+00 [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %23 -> %12 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %17 -> %6 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %17 = neura.grant_predicate %7, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %6 = neura.reserve : !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 1) [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %17 -> %6 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %7 = "neura.phi"(%6, %2) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %19 -> %8 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %19 = neura.grant_predicate %9, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1>, value = 5.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %8 = neura.reserve : !neura.data<f32, i1>, value = 5.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 1) [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %19 -> %8 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %9 = "neura.phi"(%8, %3) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ---------------------------------------- [neura-interpreter] Iteration 5 | pending_operation_queue: 9 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %21 = neura.grant_predicate %11, %20 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 0.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 0.000000e+00, [pred = 1] [neura-interpreter] ├─ Denied access (predicate false) [neura-interpreter] └─ Result: value = 0.000000e+00, [pred = 0] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %22 -> %10 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %22 = "neura.fadd"(%16, %17) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %10 = neura.reserve : !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 1) [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %22 -> %10 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %11 = "neura.phi"(%10, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %18 -> %4 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %18 = neura.grant_predicate %5, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %4 = neura.reserve : !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 0) [neura-interpreter] No update for ctrl_mov target: %4 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %5 = "neura.phi"(%4, %1) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [0] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 1.000000e+00,pred = 1 [neura-interpreter] │ └─[1]:value = 1.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 1.000000e+00, pred = 1, is_updated = 0 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %23 -> %12 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %23 = "neura.fadd"(%15, %18) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %12 = neura.reserve : !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 1) [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: neura.ctrl_mov %23 -> %12 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %13 = "neura.phi"(%12, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %17 -> %6 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %17 = neura.grant_predicate %7, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %6 = neura.reserve : !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 0) [neura-interpreter] No update for ctrl_mov target: %6 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %7 = "neura.phi"(%6, %2) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [0] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 3.000000e+00,pred = 1 [neura-interpreter] │ └─[1]:value = 3.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 3.000000e+00, pred = 1, is_updated = 0 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %19 -> %8 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %19 = neura.grant_predicate %9, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1>, value = 5.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %8 = neura.reserve : !neura.data<f32, i1>, value = 5.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 0) [neura-interpreter] No update for ctrl_mov target: %8 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %9 = "neura.phi"(%8, %3) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [0] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 5.000000e+00,pred = 1 [neura-interpreter] │ └─[1]:value = 5.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 5.000000e+00, pred = 1, is_updated = 0 [neura-interpreter] ---------------------------------------- [neura-interpreter] Iteration 6 | pending_operation_queue: 4 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %22 -> %10 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %22 = "neura.fadd"(%16, %17) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %10 = neura.reserve : !neura.data<f32, i1>, value = 3.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 0) [neura-interpreter] No update for ctrl_mov target: %10 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %11 = "neura.phi"(%10, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [0] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 3.000000e+00,pred = 1 [neura-interpreter] │ └─[1]:value = 0.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 3.000000e+00, pred = 1, is_updated = 1 [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %21 = neura.grant_predicate %11, %20 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %16 = neura.grant_predicate %11, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: neura.ctrl_mov %23 -> %12 : !neura.data<f32, i1> !neura.data<f32, i1> [neura-interpreter] Executing neura.ctrl_mov(dataflow): [neura-interpreter] ├─ Source: %23 = "neura.fadd"(%15, %18) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Target: %12 = neura.reserve : !neura.data<f32, i1>, value = 1.000000e+00, [pred = 1] [neura-interpreter] └─ Updated (is_updated = 0) [neura-interpreter] No update for ctrl_mov target: %12 = neura.reserve : !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %13 = "neura.phi"(%12, %0) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] Executing neura.phi(dataflow): [neura-interpreter] ├─ Selected input [0] (latest valid) [neura-interpreter] ├─ Input values (2) [neura-interpreter] │ ├─[0]:value = 1.000000e+00,pred = 1 [neura-interpreter] │ └─[1]:value = 0.000000e+00,pred = 1 [neura-interpreter] └─ Execution succeeded | Result: value = 1.000000e+00, pred = 1, is_updated = 1 [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %15 = neura.grant_predicate %13, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Added user to next pending_operation_queue: %14 = "neura.fcmp"(%13, %9) <{cmpType = "lt"}> : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<i1, i1> [neura-interpreter] ---------------------------------------- [neura-interpreter] Iteration 7 | pending_operation_queue: 4 [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %21 = neura.grant_predicate %11, %20 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 3.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 0.000000e+00, [pred = 1] [neura-interpreter] ├─ Denied access (predicate false) [neura-interpreter] └─ Result: value = 3.000000e+00, [pred = 0] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %16 = neura.grant_predicate %11, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 3.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Granted access [neura-interpreter] └─ Result: value = 3.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %22 = "neura.fadd"(%16, %17) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %15 = neura.grant_predicate %13, %14 : !neura.data<f32, i1>, !neura.data<i1, i1> -> !neura.data<f32, i1> [neura-interpreter] Executing neura.grant_predicate: [neura-interpreter] ├─ Operand [neura-interpreter] │ ├─ Source: value = 1.000000e+00, [pred = 1] [neura-interpreter] │ └─ New predicate: value = 1.000000e+00, [pred = 1] [neura-interpreter] ├─ Granted access [neura-interpreter] └─ Result: value = 1.000000e+00, [pred = 1] [neura-interpreter] Operation updated, propagating to users... [neura-interpreter] Added user to next pending_operation_queue: %23 = "neura.fadd"(%15, %18) : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<f32, i1> [neura-interpreter] ======================================== [neura-interpreter] Executing operation: %14 = "neura.fcmp"(%13, %9) <{cmpType = "lt"}> : (!neura.data<f32, i1>, !neura.data<f32, i1>) -> !neura.data<i1, i1> [neura-interpreter] Executing neura.fcmp: [neura-interpreter] ├─ Operands [neura-interpreter] │ ├─ LHS : value = 1.000000e+00, [pred = 1] [neura-interpreter] │ └─ RHS : value = 5.000000e+00, [pred = 1] [neura-interpreter] ├─ Evaluation [neura-interpreter] │ ├─ Comparison type : lt [neura-interpreter] │ └─ Comparison result : true [neura-interpreter] └─ Result : value = 1.000000e+00, [pred = 1] [neura-interpreter] ----------------------------------------
In iteration 3, %15 is triggered by %13 update but skipped because %14 is not ready. In iteration 7, %15 is triggered by %13 update again but not skipped because we don't mark the %14 belongs to the last DFG iteration as invalid. So %15 is wrongly executed before %14.
Can you help me understand why we need "iteration" here? Why can't we just keep a pending_execute_ops_queue to fire op's execution one by one?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
|
@itemkelvin @ShangkunLi Don't you think my suggestion is cleaner? Or any potential issue we can't maintain a queue w/o a buffer? |
That Github action won't be a part of PR, let's just manually tune it. What command I need to use on my side to cleanup the format? |
|
Actually, the instructions are still executed in the order of a queue. It's just that during the implementation, I wanted to check whether each instruction could propagate, which led to the vector being retained. It's feasible to change it to a queue.
…------------------ Original ------------------
From: Cheng Tan ***@***.***>
Date: Mon,Aug 25,2025 2:32 PM
To: coredac/dataflow ***@***.***>
Cc: item ***@***.***>, Mention ***@***.***>
Subject: Re: [coredac/dataflow] Refactor the dataflow mode logic (PR #121)
tancheng left a comment (coredac/neura#121)
@itemkelvin @ShangkunLi Don't you think my suggestion is cleaner? Or any potential issue we can't maintain a queue w/o a buffer?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Cool, let's do it in the next cleanup PR. |
I think current implementation meets your requirements. I just maintain a
Thanks~! It would be better if we could keep the original implementation. Then we can deprecate this pr, right? @tancheng |
But your dump msg still shows iter 1, iter 2, iter 3. This PR LGTM anyways, let's make cleanup later. |
Current dump msg looks like Here, each iteration means the level of topological order we are in. And DFG crossing means we have executed all the operations in the DFG, and we need to execute the next iteration of DFG. And the ready operations are |
You can install |
Refactor the dataflow mode logic interpreter
Refactor the dataflow mode logic interpreter
Many thanks for the code base built by @itemkelvin~! Fix some bugs in this pr.
In the previous dataflow mode interpreter, it doesn't respect the topological order of operations, triggering operations based on value update. This may result in executing consumers before producers. For example, for the following simple loop. It should output
15.00000.In the previous implementation,
%14will execute after%15and%16because of the update-based operation triggering. And it cannot handle thereturnoperations properly, thus outputting bothand
.
In this pr, we introduce two features: