block/nonblock,sync/nonasync,event-driven/data-driven

整理下关于block/nonblock以及sync/async,还有data depedency,还有event-driving programming 的关系和在实践中的一些体会。

all kinds of IO types

关于block/non-block以及sync/async的概念,以下几个文章都讲的非常清楚明白了

https://www.ibm.com/developerworks/cn/linux/l-async/

https://baiweiblog.wordpress.com/tag/non-blocking/

https://developpaper.com/analysis-of-io-model-blocking-non-blocking-io-multiplexing-signal-driven-asynchronous-io-synchronized-io/

https://www.artima.com/articles/io_design_patterns.html

https://www.cnblogs.com/whyandinside/archive/2012/03/04/2379234.html

具体细节就不再重复一遍了,总之最重要的两个点就是,使用block与non-block时候的场景是,controller是否可以马上返回给caller,这和数据的具体传输过程是没有关系的。

对于sync和async迷惑的时候先想下multiprocess的barier的设计,这个barrier就是为了让多个process/thread在某个时刻达到syncronize。如果这个两个thread/process没有bariier的控制,就是执行顺序是完全无关的,就可以是async的关系。

使用sync和async这个是说,数据传输的thread和调用者(caller)的thread之间的关系。但有的文章也说IO multiplexing属于blocking sync类型的IO,因为参考时序图,实际上数据传输的过程中,caller 也是 block的,所以至少应该是block sync 类型的。

然后就是要具体说说AIO(asyncrounous IO)与event driven programming的关系。主要是参考这个。event-programming和async system是强调事情的两个方面。async system通常是由event-driven或者event-programming的手段来实现,但是event-programming还可以实现syncronous的system,怎么处理event或者对于event做出相应,不一定是通过async的方式。

首先说”async is always achieved by event-driven”,这个比较容易理解,when sth happens,send message or event to caller。当caller收到这个event或者是message,可以启动一个新的thread去执行任务,这个时候新启动的thread就和原本的main execution stream是async的关系。具体对应到不同的系统,新启动的execution stream可以是一个program或者是program里的thread。

然后讨论下为何反着来不行,就是event-programming为何不一定是async的。根据stack over flow 上相关的回答来看。还是说的co-routine的场景。目前自己了解的有两个,一个是golang 的go-routine,还有一个是Argoone Mochi framework中的argobot。原理上很类似,都是实现一个thread层面的scheduler,然后用更细粒度的co-routine来执行任务。这里就说的是,一旦event到来,启动新的routine,这个routine可能会抢占本来的thread,导致之前运行的function block。所以从计算资源的角度上看,新启动的routine和原先的routine在同一个thread上,在一个时间片段内只有一个在运行而另一个被block,因此不能完全称为asyncronous system。

data driven vs event driven

在说下event-driven与data-driven的问题,本质上来说,都是使用的类似的思想。就是logic flow或者程序的执行流程并不是fixed的,并不是固定不变。event-driven强调的是event,比如GUI程序的一个鼠标点击,或者是USB的一个plugin plugout,或者是device上的一些错误等等。而data-driven强调的是data,比如不同的data放到同样的代码中会有不同的logic execution flow,执行出来的结果也不相同。但总的来说这个概念有点被滥用的意味,大部分程序追求的都是灵活性,就是能用比较少的代码适用于尽可能多的数据,这大概就是data-driven所追求的各种重构以及精简code方式。总而言之这里的比较对象都是sequential programming,核心的问题是程序的execution flow 到底是由什么决定或者驱动的(code中固定好的执行逻辑,or event,or data的内容)

还有一个相关的概念叫做table-driven,简单说就是把重要的状态存储到一个table中而不是在logic flow中一一查询,有一些空间换时间的意味。可以说event-driven和data-driven都可以通过table-driven的方式来实现,因为不同event代表什么含义以及不同data的状态代表什么含义,这些都可以通过table driven的方式来展现。

IO between client and server

从Linux IO的角度出发是 block/non-block 以及 sync/async的划分,从不同的program的角度来看,比如不同program之间的RPC调用的角度来看,比如client-server network communication通信的角度上,就是request-response 以及 pub-sub 的方式。pub sub的主要优势在于通过引入的broker把client 和 server 之间直连的方式进行了decouple,比如参考这个,从而减少了client和server之间的traffic。特别是在有大量的client-server彼此之间进行通信的时候。注意这里的使用场景,是大量的client和server之间的通信,如果量少的话,引入的broker就造成了额外的overhead。

具体实现的原理和行为对比与linux 的AIO总有一些相似之处,要提前把action注册进去,提前告诉相对应的service,如果这个事情或者signal出现了,哪些callback function要被执行。只是说AIO对应的是内核编程,pub/sub强调的是通过networking想连的不同组件之间的通信的行为。如果就假设client和server之间是互相传递数据的服务,那Linux IO的那一套似乎可以套用过来。

如果是client发送request说要获取数据,然后等数据ok 并且在网络之间传送完成,这才返回response(注意这是两个过程)那这就相当于blocking syncronize。

如果client发送request仅仅是查询数据是否ok,然后马上返回,这个就是non-blocking 的模式,如果server的数据没准备好,这个时候client可以做其他的事情,过一段时间再send request,如果request返回的结果是ok,说明数据ready了,然后就可以发送pull data 的请求,把数据从server取到client。这个就是non-blocking syncronize的方式。具体在server end, 在支持data communication的方式上又可以分为 shared space 以及 message queue 两种形式,具体可以参考比较著名的 The many faces of publish/subscribe 的论文,这里就不具体讨论了。

如果是使用pub-sub的方式,就是完全的non-blocking 和 event driven programming一起运行的方式了,client(consumer)在启动的时候可以subscribe关心的数据,然后就做别的事情去了,等到数据avaliable,client会收到notification,之后根据这个notification,做对应的事情,比如启动新的thread或者是block原来的thread进行数据的传输等等,可以是sync(block之前的thread)的也可以是async(启动新的thread)的方式。

注意一下pub/sub不一定需要一个单独的broker,实际上这个broker仅仅是逻辑上的,在实现上可以与server或者是data publisher的IO服务结合在一起。除非有着N:1的关系,比如1个broker的server要服务于各种不同在地理位置上彼此不是相互关联的client and server。

在从pub/sub的publisher的内容的角度上说说看,具体说pub/sub可以是event driven的也可以是data driven的,主要区别是publisher pub的是什么类型的信息。如果这个broker仅仅是一个message broker,比如一个string表示一个message,然后出现event的时候就发送一个event给broker,这就是event-driven plus pub sub。比如IOT的场景下,锅里煮的饭熟了,然后饭煮熟这个event会被publish到一个broker,broker就会查看谁subscribe了饭煮熟的信息,然后将这个信息notify给对应的subscriber的手机。当然subscriber的手机上要有一个对应的程序来listening这个notification,具体这个listening的notification是如何获取到信息的,这就是更细节的Linux IO的操作了,就是第一部分讨论的几种类型。比如使用了socket就是IO Multiplexing。

至于基于data driven的pubsub的方式,本质上是event driven的拓展。不同的data进入到broker,首先要确定这样的data拥有哪种pattern,之后根据不同的pattern来进行不同的logic flow。detect data 属于那种pattern的过程之后本质上就回到了event driven,出现哪种pattern可以算是一种event,detecting 哪种pattern的过程就是具体问题具体分析了。

关于pubsub的使用场景,首先要确认下和pubsub实现同样功能的操作是什么, 说白了就是最基本的request-respond。这个文章说的比较好,”Pub-sub can also make sense when it’s difficult to set up a direct connection between a client and a server, or when the network is low-bandwidth, expensive, or unreliable—for example, when monitoring equipment in remote locations. “

因为试想一下,如果在一个系统中networking的资源不是bottleneck,或者说发起一个query是很cheap的事情,那么不段地polling并不会给系统造成负担。反之如果系统的bottleneck在网络上,则pubsub就很有用了。

还有一个因素就是进行query的client的大小和frequency,如果client总是需要去query,而且有特别多的client,这样server的burdern就会变大,server总是在忙于响应query的请求,而且对于client本身来说,不断地进行polling也会耗费额外的资源,这样都会导致其处理自身业务的性能下降,在这种case下也是一个需要使用pubsub的场景。

data depedency

还有一个容易混淆的地方就是:IO的过程是是否为async,与拿到数据后是否是通过async的方式执行task以及处理数据,这是两个事情。

不论是blocking还是non-blocking的方式,在data传输完成之后都可以使用sync或者async的方式处理数据。使用sync的方式就是继续往后运行,使用async的方式就是启动一个新的thread来处理数据。

这里要说明一个问题,对于存在data depedency的task,并且在资源分配之初就确定了所要执行的任务的方式,具体使用何种IO其实并不是一个很关键的问题。就像你去参加考试,主要任务就是回答试卷,如果这个task已经提前确定好了(code已经确认好),在试卷发下来之前你是不可能做别的事情的,这个时候试卷怎么发其实对你来说并不是一个重要的事情,因为在试卷发下来之前,你都会处在block的状态。至于是通过一个listening或者watcher的方式来block还是说通过不断地查看试卷发下来没,对于caller自己来说是没有太大区别的(当然使用listening或者watcher的方式会减少一些traffic 的overhead)。

对于另外一个场景,就是计算资源不是binded到某个特定的task上而是可以server多个task的情况,这样使用async的IO才能有点作用。比如一个device,在wechat listening 消息的时候,你还可以看vedio。但是对于wechat本身所占用的资源(某个Process)来看,如果没有message,它也没什其他的事情可以做,只能listenning,这是因为它的task和data是存在depedent的关系的。但是从更高的device的视角来看,wechat的task和vedio的task是不存在depedency的关系的,所以对于一个device来说,使用async IO 或者 IO multiplexing是有价值的。vedio在传输数据的时候wechat也可以传输数据,没有data depedency的task也能同时运行。还有就是在HPC上,比如assign了一些资源,启动这个process的时候总是要知道做什么task吧,如果这个task的data没有得到满足,那这个task就是一直waiting的状态了,资源就会闲置着,也没什么其他的用处。

对应到simulation-analytics-vis的应用

通过这么多的分析,大概有这么一个原则,如果一个workflow,data 是确定的,data pattern是确定的,启动什么task去做什么事情是确定的,那大概不需要event programming。哪里有不确定的地方,哪里就有event programming可以发挥作用的地方。具体来看SAV的应用,似乎只有analytics是不确定的,也就是说可能根据不同的data pattern去执行不同的logic flow,而sim是确定的,vis(或者说是output)也是相对比较确定的。

simulation通常去用所有的resource做processing,因此要想实现async的操作,总是需要有额外的task execution的framework来操作。

从获取数据的角度,默认的情况是consumer知道要获取什么样的数据,比如variable_a step 为 1-10, lower bound 与 upper bound 分别为 [0,0,0] 到 [512,512,512]。这个时候client只需要不断地去发query然后poll这个数据,问data staging service这个数据是否存在,返回的是是否存在的信息,这个query的部分可以不不采用distributed的形式,比如仅仅用rank=0的 process 去进行query即可。如果data确实存在,就可以用distributed的形式来进行query,比如启动多个thread/process,每个仅仅query指定的partition。

如果是data driven的形式,这个时候可能说,我只知道care的varaible叫做variable_a但是我不知道具体哪一个step的数据会被产生出来放到staging中,比如可能是step 1 2 3 也可能是step 1 2 8 这个时候处理的方式有两种,一个是使用一个event queue去存和某个variable相关的event(也可以看做是一种metadata),比如step 1 2 3 的数据存进staging service之后,会另外把一个event信息放到指定的queue,然后作为consumer来说,就是send query给这个queue,如果有信息,就从这个queue中取信息出来然后可以开启一个或者多个thread/process去pull对应的data,对应的event会从queue中被pop出去(这里shared space的model并不适用,因为event的信息并不需要被持久化,只需要被consume)。如果没有event,那么queue还是始终保持在empty的状态,client收不到对应的信息,就会一直空pull。

还有一种方式就是利用主动notify的方式去传递event,这个时候需要consumer要有一个listening的操作,通常情况下可能是一个server,相比于之前的把event放在一个固定的queue里,这个操作可能是把event直接发给那个server的address(前提就是需要这个consumer进行一个subscribe的操作)。然后consumer一旦收到这个event就会启动故一个新的handler的操作。这种case如果使用multithread的话可能比较适合于unstructured parallel programming。因为需要一个thread/process/node pool。

client端获取event的操作通常可以是一个叫做watcher的API(就像etcd中的那样),根据以上两种不同的实现方式,可以是不断地pull event queue也可以是启动一个server,然后等待event被发送过来。从这个角度来讲,event可以被视为一种更加一般化的metadata。

regerence

data driven

https://en.wikipedia.org/wiki/Data-driven_programming

event driven

https://en.wikipedia.org/wiki/Event-driven_programming

https://stackoverflow.com/questions/42174856/data-driven-vs-event-driven-model-architecture

https://stackoverflow.com/questions/1065584/what-is-data-driven-programming

event-driven programming的缺点
https://systemsinnovation.io/event-driven-architecture/

nonlinear nature

https://solace.com/blog/downside-event-driven-architecture/

pubs sub register/subscribed event and get the coresponding notification

这一篇说的比较系统 (有一些相关的理论上的比较分析)
Communication and Optimization Aspects of Parallel Programming Models on Hybrid Architectures
https://journals.sagepub.com/doi/pdf/10.1177/1094342003017001005

推荐文章