我正在尝试从单个进程(P1)获取输出,并使用其他进程(P2和P3)对其执行并行任务。到目前为止这么简单。
为此,我将P2和P3连接到P1的单个输出端口。在我看来,这应该意味着P1通过其输出端口同时并发地同时从P2和P3中拾取数据包。
我发现P2和P3不是并行启动的,而是其中一个进程会等到对方完成处理(或者至少对我来说似乎这样)。
例如,这是一个简单的图表,它应该采用JSON输入,然后同时获取时间戳并解析JSON。解析JSON后会采用另一个时间戳,这用作计算JSON解析时间的基本方法。
请注意从ajax/Get
输出端口开始的连接顺序(最后添加了时间戳连接)。
在这种情况下,时间戳的差异大约是5ms,这大致与JSON解析在非NoFlo环境中所花费的时间一致(由于某种原因,它实际上在NoFlo中稍长一些)。
现在使用相同的图表,但这次来自ajax/Get
输出端口的连接顺序已更改(最后添加了解析连接):
这次时间戳之间的差异大约是40-50ms,这显然是一个巨大的差异,远远大于NoFlo之外的解析。
如果有人能够对以下内容有所了解,我真的很感激:
ajax/Get
的2个连接被触发并且并行运行(即,它们不会相互等待)? 如果有帮助,here's a JSON export of the graph from FlowHub。
我还使用CLI整理了一个简单的图表,并设法更好地了解图表的流程,并且可能会对可能导致此问题的原因有所了解:
# This executes in the correct order, though likely by
# coincidence and not due to true parallelisation.
#
# Time1 is run and outputted before Time2.
#
Read(filesystem/ReadFile) OUT -> IN Time1(objects/GetCurrentTimestamp)
Read OUT -> IN Parse(strings/ParseJson)
# This executes the entire Parse path before going back to grab
# and output Time1.
#
# Time1 is run and outputted *after* Time2
# Read doesn't send a disconnect message to Parse until *after*
# Time 1 is outputted.
#
# Read doesn't send a disconnect message to Time1 until *after*
# the Parse path has finished disconnecting.
#
# Read(filesystem/ReadFile) OUT -> IN Parse(strings/ParseJson)
# Read OUT -> IN Time1(objects/GetCurrentTimestamp)
Time1 OUT -> IN Display1(core/Output)
Parse OUT -> IN Time2(objects/GetCurrentTimestamp)
Time2 OUT -> IN Display2(core/Output)
'sample.geojson' -> IN Read
在Read
到Time1
之前定义的Read
到Parse
连接的情况下运行时,网络是有序的,但我注意到Read
等到其他一切都完成后才开始断开连接(是吗?):
DATA -> ENCODING Read() CONN
DATA -> ENCODING Read() DATA
DATA -> ENCODING Read() DISC
DATA -> IN Read() CONN
DATA -> IN Read() DATA
DATA -> IN Read() DISC
Read() OUT -> IN Time1() CONN
Read() OUT -> IN Time1() < sample.geojson
Read() OUT -> IN Parse() CONN
Read() OUT -> IN Parse() < sample.geojson
Parse() OUT -> IN Time2() CONN
Parse() OUT -> IN Time2() < sample.geojson
Read() OUT -> IN Time1() DATA
Time1() OUT -> IN Display1() CONN
Time1() OUT -> IN Display1() DATA
1422549101639
Read() OUT -> IN Parse() DATA
Parse() OUT -> IN Time2() DATA
Time2() OUT -> IN Display2() CONN
Time2() OUT -> IN Display2() DATA
1422549101647
Read() OUT -> IN Time1() > sample.geojson
Read() OUT -> IN Parse() > sample.geojson
Parse() OUT -> IN Time2() > sample.geojson
Read() OUT -> IN Time1() DISC
Time1() OUT -> IN Display1() DISC
Read() OUT -> IN Parse() DISC
Parse() OUT -> IN Time2() DISC
Time2() OUT -> IN Display2() DISC
如果我切换订单以便首先定义Read
到Parse
连接,那么一切都会出错,Time1
甚至不会从Read
发送数据包直到整个Parse
路径已完成(因此Time1
实际上是 Time2
之后的:
DATA -> ENCODING Read() CONN
DATA -> ENCODING Read() DATA
DATA -> ENCODING Read() DISC
DATA -> IN Read() CONN
DATA -> IN Read() DATA
DATA -> IN Read() DISC
Read() OUT -> IN Parse() CONN
Read() OUT -> IN Parse() < sample.geojson
Parse() OUT -> IN Time2() CONN
Parse() OUT -> IN Time2() < sample.geojson
Read() OUT -> IN Time1() CONN
Read() OUT -> IN Time1() < sample.geojson
Read() OUT -> IN Parse() DATA
Parse() OUT -> IN Time2() DATA
Time2() OUT -> IN Display2() CONN
Time2() OUT -> IN Display2() DATA
1422549406952
Read() OUT -> IN Time1() DATA
Time1() OUT -> IN Display1() CONN
Time1() OUT -> IN Display1() DATA
1422549406954
Read() OUT -> IN Parse() > sample.geojson
Parse() OUT -> IN Time2() > sample.geojson
Read() OUT -> IN Time1() > sample.geojson
Read() OUT -> IN Parse() DISC
Parse() OUT -> IN Time2() DISC
Time2() OUT -> IN Display2() DISC
Read() OUT -> IN Time1() DISC
Time1() OUT -> IN Display1() DISC
如果这是正确的行为,那么如何并行运行2个分支而不阻止另一个?
我已经尝试过使每个组件都异步,我已经尝试了这两个组件并使用了WirePattern,我尝试创建多个输出端口并立即通过所有端口发送数据。没有快乐 - 它总是归结为第一条边连接的顺序。我正在把头发拉出来,因为它完全阻止了我对ViziCities使用NoFlo :(
答案 0 :(得分:0)
由于JavaScript引擎的单线程特性,NoFlo无法并行执行多项操作。 I / O调用在他们自己的线程中运行,但是他们的回调总是将我们返回到NoFlo运行的主线程。
在NoFlo中,只要我们处理同步组件(就像示例图中的ajax/Get
之外的其他内容一样),我们就会执行深度优先以确保快速吞吐量。
这意味着从ajax/Get
的第一个出站连接的子流首先运行到完成,然后是第二个。
你想要在这里发生的是广度优先执行而不是深度优先。已经有一些关于通过边缘元数据启用它的讨论,但在此之前,一种方法是在感兴趣的连接之间添加core/RepeatAsync
个节点。
从长远来看,另一个有趣的方法是通过RemoteSubgraph和Web Workers在自己的线程中运行部分流程。从理论上讲,我们甚至可以在自己的线程中运行每个进程,完全兼容传统的FBP。但这会带来巨大的启动成本。
答案 1 :(得分:0)
我不会考虑浏览器示例,它确实是深度优先的,因为浏览器端JavaScript的工作方式正如bergie所解释的那样。
CLI示例更有趣,因为noflo-nodejs广泛使用EventEmitters。它仍然不是真正的并行,但它更加并行。
我们在此处看到的是以下方面的副作用:
data
事件而不是disconnect
触发的。他们不等待disconnect
处理data
并发送结果。在汇总中,它解释了为什么第一个分支在第二个分支之前执行,以及为什么在已经处理完所有数据之后断开连接。
这可能会给你一个纯粹同步的印象,但尽管上面列出的事实,系统仍然是并发的。如果Read
以合适的速度发送多个数据包,您会看到分支1和分支2的事件混合在一起。
更新
这是将同步组件转换为异步的常用技巧:
setTimeout(function() {
doTheSyncJob(); // actual code here
callback();
}, 0);