2019-06-03

关于Ceph的CRUSH算法

考虑crush map的bucket选择.

对于一个obejct o,choose的时候需要经过一个hash过程得到一个pseudo random的id x.
也就是对于任何一种bucket 类型,有
x = bucket_hash(o).

假定bucket的长度为n,x的放置位置为y的话.
bucket specific的choose policy为p.
有y=p(x)

对于uniform bucket来说,这个p为
y=p_uniform(x) = x % n

除了uniform的bucket之外,其他bucket type都是有weight权重的.
同样,记录位置w_i为位置i的用权重值,且有0<=i1的prod替换的sum.

也就是,这里的straw其实是一个权重放大倍数比list高得多的一个进阶版本.
而且跟list不同的是,它的argmax没有< w_j的条件限定.
因此它的寻址复杂度是O(n)的.
而list因为条件限定的情况,实际平均表现最差也只是跟straw一致.

在考虑现在ceph的默认policy,steaw2.
这里对hash做个额外的限定,0
这个主要是ceph的实现里有个lookup table的优化.
给定0
所以实际上starw2的hash是一个到[0,1]区间的一个mapping.
对应的log(k)取值区间大概是[-11,0].

所以这里简单重定义-11<=hash(x)<=0

y=p_straw2(x) = argmax(\{j; hash(x)/w_j \})

这里可以看出,只要bucket item的权重不变.节点增减的变化只由argmax的骨架部分决定.
这个跟list也有点类似,但是它是位置无关的.
不像list有对于左右有相对确定的基础cost估计.

但也因为位置无关,所以它存在一些极端情况.

对于节点减少的话,则所有argmax为这个节点的数据都会受影响.
这个是符合intuition的.

对于增加节点,argmax的max value可能都指向变动的那个节点.
这样的极端情况就是造成整体数据的挪动.

但是由于w_j的存在,这个也是可干涉/存在可控性的.

而对于节点数量不变,只是权重改变的情况.
跟节点增加类似.

所以,straw2的基本上算是忠实指向w_j的.

然后考虑hash.

由于hash会对x走一个log的重新分布.
所以实际上考虑的数据分布性质的话,只需要考虑log分布之后的就行了.

也就是在大概[-11,0]的区间内的数据分布问题.

把w_j作为一个tuning parameter/placement parameter考虑的话.
那么理论上,针对现有的实际数据分布是可以做到人工精确干涉的.

而且这么考虑的话,其实starw2的计算也并不需要搞那么复杂.

因为归根到底还是通过w_j来控制的.

2019-05-14

变化

美股作出反映大概是在谈判破裂确定之时.

那么假设大家都是rational的话,一个方向的确定就说明了一个不确定性或者至少两个可能存在的消失.

这两个可能无非是继续谈和谈不了.

既定的谈不了的结果是slump的话,那么另外一个方向自然就是非slump的.
也就是继续negotiate的话,经济趋势是有可能持平或者向上的.

那么加征关税为什么会意味着中短期美国经济会有不愉快呢.
或者说为什么这次会这么敏感.

毕竟这并不是第一次.
以及如果确实因为关税原因存在不确定性的话,市场总会在前期会有分歧波动的.

考虑下现在headline的关注点和方向.

一个是影响面的数据/描述.
这不是新的内容.

一个是关于选举方向的.
这个可能就是impact factor.

作为选举双方来说,差异性的存在所引导的方向多少是需要有不同的.
大多数时候来说都是简单独立的.

那么理论上就应该是贸易和反贸易的立场.

因为立场的不同导致的对贸易情况的走向具有的不确定性可能才是市场犹豫的点.

而现在选择了方向的话,那也就是说这个分歧点/不确定性可能不存在的.

那么这样的话,就貌似合理解释了.

对于川普来说,提高关税和拒绝应该都是在合理的结果预测行为序列中的.
但是可能意外的市场和对手的行动选择可能算是意料外的.

所以市场的不良反应对于一直以来树立的政策倾向来说,造成的选举影响可能一下子就负面了.

某种程度上来说,这是一个trap.

而对手选择贸易冲突这个路线展开的一个期望就是同样的对中国贸易的依赖减少趋势.

考虑目前的攻击点来说,主要就是制造业和农业的回归或者说复兴.

所以至少在这一点上,方向应该是不会有太大差异了的.

国际贸易基于的是广义的成本考虑,不管是狭义的生产制造成本,还是从长期的供应链流程来说.
结果导向的都是最终成本的减少/利润率的提升.

因为是整个产业链条的优化选择,那么对应的自然而然的是paradigm shift.

单纯的贸易成本对冲应该是不能够覆盖整个贸易冲突面的.
所以,产业流向和分布的重新组织再结构几乎是必然的.

而考虑到体量的问题,这个基本上也是全球性范围的一次调整.

考虑到目前的技术普及水平和人工成本,美国要短时间内补全和对冲完影响的话,也不是很容易的事情.
但是考虑的机械化程度和相对宽松有利的民间私营经济环境,通过技术手段来补足应该是可以预见的.

所以某种程度上来说,可能只是个长痛不如短痛的被动革新过程.

把同样的时间和尺度范围放到大洋彼岸来看的话.
可能结果就没那么乐观了.

诚然目前依然是一个制造中枢.
但是从中国制造的蓝图本身看的话,倡导向高端制造业转型imply的就是目前主要还是中低端制造业为主.
而这个的另外一个消息面是向其他新兴发展中国家转移.

这个产业转移可能不是一个短期内能完成的事,尤其考虑到新兴国家本身产业和政治生态的一些问题.
但也不是说不可能.
而且也并不一定还需要,正如技术发展的革新一样.

第二个是国内本身的可能略显利己和投机的社会氛围.
像目前的一些热点方向本身可能并不是太有利于社会的发展以及更重要的,有相对可靠合理的长期盈利模式.

虽然陆续有相关的动作和政策去做一些规范和限制.

但对应的,也是社会经济文化更方面的更趋管制和指导方向明确的现状.

这也就意味着实际上的是一个国家计划指导的全面的发展策略.
imply的可能就是有更多的方向性死亡的出现.

另外一点就是在总体budget有限的情况下,要做到社会效用最大化的话.
可能扶植和倾向的方式方法也会更不太利于规模小的公司和产业个人.

毕竟在确保资源和资质的尽可能充分兑现的前提下,门槛相对可能会比较高.
从执行的角度和执行层次的深度以及基层终端执行素质来说,提高门槛可能是成本最低最容易实现的一个方式.

像如果重点是第一二产业的话,那么作为文娱服务主要的第三产业可能就没那么大的权重了.

但至少这样才有所谓的共克时艰.

不然失去的可能就不单单是十年了.



2019-04-14

谈谈软件工程

考虑人的工作产出为x,时间为t,对应需要支付的成本为s.
那么一个profit=x*t-s

如果令s的支出跟t简单相关则有
profit=(x-c)*t.

简单地,所谓的剩余价值约束工作时长.

这里有两个隐含假设.
一个是x是恒定的,也就是单位时间贡献不浮动.
而是s跟t线性相关.

也就是这里收益和成本都是简单线性描述.

考虑x为一个带一定衰减的函数.
比如X(t) = W*(1-e^-t).
一个bounded by W且随时间增长效率递减的函数.

那么profit=\int_{0}^{t} W*(1-e^{-t})dt - c*t
->
profit=t+e^-t+R-c*t
->
profit=e^(-t) + (c+1)*t + R

如果以天为周期小时为单位计量的话.
大致回报也是线性的.

一般地来说,在给定的时间范围内,只要X的derivative为正的话.
也即是时间计量的margin为正.
那么incentive就自然会是增加时长.

那么如何构造在一定时长后margin为负的情况呢?

最简单的负值构造自然是线性的.
对应的就是抛物线.
即X(t)=a*t^2+b
->
profit=a*t^2+b - s*t
->
profit=W(c-t)^2 + d

在这种情况下才可能存在给定某个工作时长为最优解形式.
如果是存在最大值的话,那么应该有W>0

也就是这个X应该是一个会在某个时间点之后incur performance degrade的曲线.
这不算是一个不现实的约束.

其他的形式呢?

考虑相对一般的形态.
profit=return(t) - cost(t)
或者
profit=\int return(t) - \int cost(t)
对应的return和cost曲线的对t的integral差异.
即投入和成本曲线的差额.

对于单个个体来说,如果需要maximize profit/on return的话.
相对stable的IC曲线应该是会让问题相对简单的.

这就需要每个人的投入产出以及对应的成本是相对可计量/具有统计意义的.

大多数情况下个人的cost是相对可预测/固定的.
所以这个求解的关键在于个人的time investment return的模式问题.

只要产出模型是有正margin的,那么单纯延长时间是没问题的.

比较麻烦的是那些曲线比较复杂,或者根本不连续甚至在一定周期尺度下具有随即性质的.
这种可能是根本没有明确的解析解的.

从群体尺度来说的话.

在数量到达一定程度的时候,个体的模式浮动性特征是比较容易被表征出来的.
也就是说,到达一定规模之后,理论上来说是可以规约到某一种特定的产出模式上作为当个个体求解的.

但是也并不是就一定存在解.

因为形式上来说,解的存在性和形态取决于产出模式.

那么软件工程是或者说应该是什么样的产出模式呢?

可能软件工程这个提法本身就是比较微妙的.

记得上学的时候就有各种CMMI/敏捷/测试/设计模式等方面的各种宏观架构.
各种方法论解构下来不过就是拆解.

背后的逻辑不过就是把不可控控制在一个认为可控/bounded的范围内.

它可能笑起来像金融的risk control/estimate.
但是至少金融有一套稍微能跑起来自洽解释的计量和数值系统.

而软件工程理论是不具有这些特征的.
最后无论以什么方式拆解出来的方案/步骤/方案,最后指向的是一个单一的计量标准.
也就是时间.

是的,金融方面也不过最后会量化为交易价值.

但两者的不同在于,时间的定量本身是未定义.
或者说没有标准的.

就像敏捷提出来优与瀑布或者更传统的软件开发模式的时候.
一个论点就是拆解的时间单元比对方更小,从而更flexible,更能应对各种可能发生的情况.

至少在这个陈述里面,它没有回避说软件开发的难点在于 各种可能发生的情况 这种风险.
但是逻辑上却微妙地归结到更小而短的周期能够让这种风险的发生几率更低.

那么如果这个观点如果是对的话,imply的就是软件开发的风险在与周期长.

这样就实际上陷入了一种递归指责.
或者说是一个逻辑陷阱.

在没有办法定量指出风险的具体含义的时候,提出了解决风险的方式.

所以,能实施成功的话,也只是因为刚好恰当地风险主要因素确实是时间.

那么,软件工程的风险到底是指什么呢?

可能在于软件工程这个提法本身.

coding本身有所谓的工程化的么?

或者说工程化本身的含义是什么.

一般的工程化可能跟接近于科学的定义.
即是一种能够量化衡量的可重复实验/过程.

coding本身是不太具有量化标准的.
就像文章或者绘画.

即使以统计的角度来说,量化能覆盖的也不过是风格上的问题.

但是完全没有能够bound的东西也不尽然.
至少API/protocol算是一种.

但是这是在integrate阶段的.
这个时候才会有简单的satisfied or not.

所以如果软件工程如果说存在的话.
也应该指示的是在delivering service过程中的这些隼合情况.

实际上,这些理论上是包含在automation里的.
包括compile/build/test/deploy基本都是不需要也不应该由人去干涉的.
而是根据各个节点的specifications来judge的.

再回头看那些工具和方法论.

那么当所谓risk出现的时候,大致就是各个pipeline stage的合规情况出现问题.
这时候这些理论是怎么解决这些问题的呢?

并没有.

或者说也有.

因为在这些理论框架下需要满足的constrain是时间.
如果把它作为所有risk的一个proxy来看待的话.
由于这些工具所处理和解决的都是在满足time bounded的前提.
于是在proxy回实际的处理方案的时候,关注点也通常在如何回避对时间的default.

于是实际执行上,如果build或者test或者deploy或者任意阶段出现问题.
那么可能的做法就是直接抛弃约定,做default处理.

所以这里的核心矛盾就在于.
或者说一个简单的模型描述就是.

给定一定的time resources.
以及一组random generated的consumer.
如何用limited的resource去satisfy的问题.

而这个是无所谓解的.

除非是能够bound consumer.
要么有一定的确定可预测的范围,要么具有某种可描述的模式.

所以,归根到底还是know your issue.









2019-04-06

由铁路售票想起

候车的时候想了个问题.

其实列车售票是一个segment的allocation问题.

给定一条线路{s_0,s_1,...,s_n}.
一个从A->B的请求实际上是某个{[s_i,s_j]; 0<=i 且每一个s_i有对应的一个quota,q_i表示可allocate的数量.

那么一个naive的approach就是做一个atomic的能rollback的transaction,做一个decrease.
这样的话,在q_i <<< concurrent_request,远小于并发请求数的时候.
在给定collision分布的情况下,某些q_i就不停地<0导致transaction rollback,从而造成一个failed allocation.

考虑一个稍微简单的定义.
给定一个sequence或者whatever,{s_i;s_i=0 or s_i=1}
也就是这个离散连续区间的值为0或者1.
或者说,这条线路各站余票只有0或者1两种情况.

对于一个allocation request,a_i_j,则要求的是.
sum_i_j s_i = j-i+1,即使i,j区间均为1.

而一组concurrent request则可以认为给定一组a,求一个satisfied的allocation subset.

这个有点类似有冲突的广告bid.
或者所谓的背包算法还是什么?

考虑optimized的目标是让是个subset尽可能地大.
或者说有尽可能多的request被满足.

那么这个最优解怎么求呢.

从最大或者最小的span开始allocate是不行的.
因为很容易构造一个反例,使人如果这个最优解存在,这种策略是不work的.

考虑对non-overlay的span一次allocation.
这是一个not necessary optimal的solution.
因为没有conflict.

考虑这个allocation set中的某个请求r,存在另一个range conflict的请求r_c.

当r_c的span是r的一个子集的时候,用r_c替换r是可以improve的.
因为此时r_c release原来r range的某些slot.
这使得这些slot有可能被更多的请求allocate到,从而增大了allocation set.

如果反过来的r_c是r的一个super set的话,那么同理,是不能improve的.

考虑只是partial overlay的情况.

令L_r为r的长度,L_r_c为r_c的长度.

那么在把r剔除之后的空出来的长度L至少会是L_r + L_r_c - 1.
不然的话,r和r_c能够同时fill整个range,不会有overlay.

于是对于r的improve就递归为这个L区间的optimal solution了.

则当最终converge的时候,也就是对于当前solution的所有r,都找不到任何improve的时候.

这个是最优解么?
还是说只是一个local minimal?

应该说只是一个local minimal.
因为本质上这是个step one的back search.
存在且容易构造如果回退两步能fill 3个request的情况.

而回退两步能产生更优解的原因应该是在于有了更多的continual的space.

所以可能first fill的时候,应该以保持尽可地避免fragment?

于是理想的做法应该是优先给予线路两侧的短途?







2019-03-16

关于锁的一点想法

考虑namenode或者某种rpc service队列.

假设有k个处理线程,每个请求处理时间是r的话.
则有t时间内的throughput T=k*r.

如果每个请求会收到一个global/share lock或者之类的互斥影响的话.
那么单个请求的开销r应该增加一个extra cost c.
即T=k*(r+c).

对于锁竞争之类的,cost c通常不会是一个常数.
考虑存在一个概率p能描述这个dynamic的话.
即c=p(c)

则每个请求的开销为r+p(c).
考虑有n个请求的话,那么在并行k的情况下.
每个线程处理m_i个请求,则该线程处理时长为\sum_{m_i} r+p(c).
->
cost of thread m_i = r*m_i + \sum p(c).

其中有n=\sum_k m_i即所有线程的处理请求总数加和为n.

当each m_i sufficient large的情况下,
cost of thread m_i ~ r*m_i + m_i*mean(p(c)).
即竞争带来的dynamic cost趋向于它的期望.

考虑线程处理是work stealing的策略的话.
那么并行带来的处理时长应该是每条线程差不多.

因为考虑只有两条线程的情况.
其中a跑了一个task,那么另外一条b要么跑comparable的task,要么lighter,或者heavier.
如果是lither的话,那么b将先完成,并抢先执行下一个.
这时等价于将a的当前task的cost减去b的task的开销,重置回假设初始.
而如若是heavier的话,交换a b即可.

对于k>=2的情况可以将划分为logical两条线程divide and conquer.

于是在这种情况下每条线程都是equality comparable cost的.
而任意一条在m_i sufficient large的情况下有
cost=r*m_i + m_i*mean(p(c))
->
per request cost=r+mean(p(c))

在p(c) dominate r的情况下.
比如在lock contention的情况下,process cost << wait lock. 实际请求开销小于锁竞争的情况下. 那么throuthput ~ mean(p(c))的. 那么考虑p的实际分布可能的形式. 以读写锁为例. 请求require read lock的概率为p,write lock的概率为1-p. 有k个线程竞争. 则,request read lock的情况. 当,all read lock的概率为p^k 因为shareable,所以cost=(p^k)*0 = 0 at least one write lock的概率为1-p^k. 如果获得锁,则有cost=0. 未获得锁的话,存在另外一个描述lcurrl,lock cost under read request lose. 即cost=(1-p^k) * (lcurrl) 请求为write lock的情况下,且获得的情况不论概率,其cost为零. 获取失败的情况下,存在yet another描述,lcuwrl,lock cost under write request lose. 那么mean cost=(p) * ((1-p^k) * (lcurrl)) + (1-p)*lcuwrl. ->lcurrl*p - lcurrl*p^(k+1) + lcuwrl - p*lcuwrl
->(lcurrl-lcuwrl)*p + lcuwrl - lcurrl*p^(k+1)

由于lcurrl和lcuwrl都是在给定p下的某个分布的mean值.
所以对于给定的p,有
(lcurrl-lcuwrl)*p + lcuwrl - lcurrl*p^(k+1)
->
mean cost = a*p^(k+1) + b
其中,a>0,1>=p>=0,k>0,b>0

当p->0的时候,或者k越来越大的话,mean cost -> b -> lcuwrl.
即lock cost under write request lose.
也即写锁竞争失败之后的expected cost.

如果锁是fair的.
那么设计上来说,每个线程得到的几率都是对等的.
于是随着k的增加,lcuwrl也会相应增加.
因为稀释.

而如果是non-fair的,则依赖于具体实现了.

2019-03-12

快消社会

今天看到条消息,是F5收购Nginx.
这好像已经不知道是第几个知名重要的开源软件最终被收编的故事了.

也不能说被收编之后nginx会怎么样.
只是总有种一个时代过去了的感觉.

想到月底Google+也要关闭了.
可能有点什么能说的.

哀叹互联网从开放进取到现在的逐渐封闭,似乎也是个没什么太有意义的话题.
不过难免的总会有一点凄凉感.

互联网能走到今天,大概也是因为这种开放的唾手可得感.
开始的信息都是只有你想不想获取,大抵是没有获取不到的.

至少相对于今天的各种paywall和不可索引/内网黑洞来说,是几乎没有什么门槛的.

而现在,不论是国内外,都趋向于各种封闭不可或者难以延伸的圈子.
或者说用户很难自我发现的一个状况.

前段时间,reddit上有个culture war subreddit关闭的事情.

大概背景就是,这个subreddit如其名,可能是有各种比较激进或者非主流甚至有些反社会的内容.
里面会有一些和平非和平理性非理性的讨论.
维护者称之为national conversation.
大概可以称之为一种比较真正意义上的公开性的讨论.

一方面是因为troll,一个社区庞大之后的无可避免的被无法融合的外来文化冲突.
另一方面是作者自身的一些反思.

也就是moderate的一切正确性/正义性.

用一种可能比较容易理解的话来说就是审查/审核的边界线.

社区维护者或者更泛化地来说,每个人都有自己各自的立场和观点.
那么在判断一个东西是否普适或者符合某种社会道德规范的标准也就实质上的因人而异.

所以什么内容是对什么内容是错,这个本身就不太可能有一个完美的解释.

在这种情况下的审核或者说过滤就天生地容易引发内容之外的矛盾冲突.

有时候就是所谓的网络暴力.

这就是说,在开放性的同时,本身对于一部分人来说,这就是错的.

或者说,开放性的最后演化方向可能几乎必然是走向封闭.
或者说失败的.
因为开放并不代表着能包容全部,以及被任意包容.

Google+当初是个很有意思的几乎是社会实验性质的设计.
它的input/output信息流都是用户可以控制的.

有点类似今天的朋友圈,你可以选择只看哪些人的信息流,也可以控制你的信息流能被哪些人看见.
最关键的是,这两点都是高度自由可选的.

但是这种技术或者说产品设计上的优越性并没有让Google+成为一个成功的产品.
甚至于某种程度上因此失败的原因.

Google最初被人诟病的理由之一跟buzz类似,是过于结合Gmail/Google产品,使人没有一种社交安全感.

这是一个技术和人性的冲突性伪命题.
因为本质上来说,用户对信息流的流向是完全可控的.

但是依然令人不安的原因根本上来说并不在于能不能控制信息流向.
而在于这些信息被人看到后其他人的反应符不符合自身的预期.

也就是说,这种不安感实际上来自于那种对不预期结果的不可撤回干涉性.

这就像有些聊天工具的撤回功能一样.
本质上来说,它并会改变别人已经看到接收的事实.
但是,因为可撤销性给予了一种可挽回可弥补不预期结果的一种可能性.

尽管很多时候是一种假性安慰.

因此对于大多数人来说,这并不会让人觉得Google+的这种设计有安全感.
也自然不太可能有大而普遍的UGC产生.

这是关于outbound的一方面.

另一方面是所谓的鬼城问题.

实际上,Google+可能有些不算小的活跃圈子.
知道的可能就是一些ACG内容的.

里面会有一些很紧密的闭合关系.
而且由于Google+本身的circle的设计,可能会有二层或者三层的private circle存在.

类比的话就类似微信或者其他IM的群组功能.
在基于组员clsoed对外不可见的情况.

这在某种程度上印证了开放社会是不太可能长久存在的.

因为这些private circle的存在或者说建立的理由大致有几个.
一个是回避杜绝某些话题.
一个是基于封闭关系链的安全感.
一个是某种程度的优越性集体意识.

这些是一种有意识的分裂且不太可逆的过程.
加上echo chamber的作用,除了本身内部存在矛盾分裂的时候,是很少会出现在开放空间的.

所以,像Google+这种社会实验表明,大体上来说,开放空间本身是不太可能存在的.
因为其反人性的一些问题.

而像culture war的问题则说明,即便有幸地在某个阶段能沉淀出一个比较合适的社区共识.
但是维持这种共识的不变性和健康性也是一个未知数.

这点也是很多BBS走向衰落的一个因素.

对于那些相对长青的社区来说,无一例外地算是有很好的某种程度的文化传承.
一个例子就是S1.

一个可能不太好但却从另一个层面说明问题的可能就是知乎.
尤以谢邀之类梗为重.

它反映的对外的stereotype,某种程度上是来源于社区理受相当部分用户欢迎和关注的内容.
也即是一种用户文化的传承或者说侵略对抗的结果.

一个社区的存在,在这个层面上来说,是依赖于主要用户的一种文化气质的.

也即是说,一个社会的氛围形式是如何,相当程度上是受之中的大多数人所左右形成的.

那么,回到互联网的封闭倾向问题上来说.
可能也是基于同样的理由.

RSS类开放社会的消亡,开源软件的商业化服务化,software infrastructure的便利化黑盒化.
可能都算是一种自然选择.

毕竟,可能快消社会?

2019-01-13

毕竟人类

考虑一种调用风格.

Promise.costly(()->receive())
.transform((packet)-> mmap.transferFrom(packet))
.sidekick(()->logging.info("saved"))
.catching((exception)->logging.error("save fail",exception))
.addListener((channel)->channel.close())

这里大致是对CompletableFutre和guava对ListeningFuture的一些包装.
稍微链式了一下,以及改动了点语义.

大致涵盖的几个data flow是.
一个正常情况下的receive packet -> save -> close.
一个sidekick是在正常receive之后的并行log部分.
一个是receive exception情况下的catching部分.
以及不管是正常还是异常情况下都并行触发的addListener部分.

最终返回的reference的value是跟transform返回的签名一致.
也就是除了transform,其他都是旁路性质.

这点跟completable是不太一样的.
虽然底层实际用的就是completable future.

这么做的原因主要是不想总是try catch.
或者像go一样检查返回值.

而且为了简单,lambda的声明都是带throw的对应的functional的版本.

所以实现上是把最初的exception一路propagate过来的.
这点跟completable/listening都算一致的.

问题主要是这个compute graph的trigger或者说traversal方式.
或者说transform这些callback的调用应该是同步还是异步的问题.

因为底层实际上就是Comp了table future.
所以实际上就是thenApply之类的调用的.

如果同步的话,实现上是会当场back trace的.

也就是当一个这个graph当中的一个stage完结之后会尝试看依赖项目是否也是可以执行.

这里的一个好处就是execution path可能尽可能地总体时间短.
因为基本上算是instant的.

这样的话,几率上来说对cache可能也会有点好处.

但这样的话为什么不直接写成正常的平坦的workflow呢.
而且可以没有那些基本的代价开销.

一个理由可能就是整体流程比较长.
或者存在一些不太确定的blcoking的部分.
所以通过这种人工的方式拆分出time slice.

某种程度上来说跟go的goroutine/chan/syscall调用时候隐切换是类似思路.
通过一种人为的软调度去提高throughput.

但是同时带来的一个问题就是可能latency相对会高一些.
因为是每个execution context的slice都会被切分并在不同的实际被queue或者stack.

所以,即使是说用async的方式处理stage.
但同样地还有是用first in first out还是last in first out的形式.

像Stream API的parallel使用的common pool就是forkjoin pool的last in first out.

这个在某种程度上来说是兼顾througput和latency的一个选择.
毕竟stack的距离可能没有queue的距离来得远,每个切换的数据访问的差异可能相对没那么大.

至少意图上来说,是有一定的cache friendly的.

但是这里同样会有个问题.

比如
LongStream.range(0,regions).paralle()
.mapToObject((region)->file.map(region))
.flatmap((region)->region.pages())
.map((page)->crc.update(page))
.collect()
这类flatmap的stream generation.

可能隐式地来说,会有一个形式上的synchronize point.
因为pipeline的某个节点是generator,而generate的动作可能又是有一定开销的.
比如这里的mmap,基本上手受限于磁盘的.

这样的话forkjoin thread的local queue/stack就应该都是这些相对比较耗时的task.

也就是说,对于其他task来说,除了enqueue/dequeue和queue work load的影响之外.
还收到来自未来的运行时的不预期的一些开销因素.

所以相对来说,LIFO的不确定性比较高.

另外一点就是对于原生Future这类从设计一开始就没有考虑callback的wrap方式.

guava的actor暴露的是一个ListenInPool的选择.
也就是扔到一个你提供的线程池里blocking get,然后再回调.

在实现的时候开始也采用了类似的做法.
类似Promise.costly就是在一个cache thread pool.
而对应的Promise.light才是在一个forkjoin pool里.

一个不太好的情况就是类似之前go的syscall造成的大量thread的问题.

把future listen在一个cache thread pool的问题就是可能某个瞬间产生大量future的话,会意外地尝试非常多的thread.

当然,一个思路就是限制thread数量.
但是引入cache pool的原因就是想避免一些意外bug或者intended的不会返回的future耗尽thread的问题.

折中的办法是先把future queue起来.
然后类似早期go的net poller,定期的poll遍历touch一下.

代价就是有额外的这个poll的delay.

另外一个模仿点就是timeout.
基本上就是select timeout channel的future形式.
scheudle一个delay的future cancle.

可能本质上来说,就是在各种模仿.

毕竟人类.

牛来餐馆

看了欢迎来龙餐馆. 总的来说,作为一个院线电影,成品多少是有点不太匹配的. 看起来更多像是一部网络大电影. 尤其涉及到一些大场面特效的时候,可以看得出服化道的降本增效. 剧本层面有一些比较有意思或者说闪光点. 就是试图用一个餐馆或者厨师来串起一个比较宏大的叙事题材. 这个可以说是...