精华内容
下载资源
问答
  • 最新版《浏览器工作原理(how-browsers-work)中英文对照版》,共享给爱前端爱Web的童鞋~后端看看也能涨见识啦!好书值得推荐嘛~
  • leetcode中文版编程面试大学 我最初创建它是为了成为一名软件工程师的学习主题的简短待办事项列表,但它增长到您今天看到的大列表。 完成这个学习计划后,! 你可能不需要像我一样学习。 总之,你需要的都在这里。 ...
  • I take the train to work every day. 我每天搭火車上班。 CC-BY 2.0 (France) Attribution: tatoeba.org #4487322 (CK) & #759563 (Martha) I think I should get right to work. 我認為我該馬上去工作。 CC-BY 2.0...
  • 在线英文翻译中文比较

    千次阅读 2019-01-16 10:32:04
    最简单的方式就是用工具在线翻译一下。为了挑选一个合适的在线翻译,我进行了简单的比较。   英文原文,是一段技术文档: Objects are structures allocated on the heap. Special rules apply to the use of ...

    在看英文资料时,对理解比较模糊的句子,希望看看别人是怎么理解的。最简单的方式就是用工具在线翻译一下。为了挑选一个合适的在线翻译,我进行了简单的比较。

     

    英文原文,是一段技术文档:

    Objects are structures allocated on the heap. Special rules apply to the use of objects to ensure they are properly garbage-collected.

    Objects are never allocated statically or on the stack; they must be accessed through special macros and functions only.

    (Type objects are exceptions to the first rule; the standard types are represented by statically initialized type objects,

    although work on type/class unification for Python 2.2 made it possible to have heap-allocated type objects too).

     

    几款免费在线翻译结果如下:

    注:前面是个人主观评分,后面是产品名和网址。黄色字表示翻译出彩的(翻译出彩的),红色划线字表示翻译严重错误的(翻译有语病的)。

     

    --- 88分 google

    对象是在堆上分配的结构。 特殊规则适用于使用对象确保它们被正确地垃圾收集。

    永远不会静态地或在堆栈上分配对象; 他们一定是仅通过特殊宏和函数访问。

    (类型对象是第一条规则的例外情况; 标准类型由表示静态初始化的类型对象,虽然在类型/类统一上工作对于Python 2.2,也可以使用堆分配的类型对象。

     

    --- 88分 bing https://cn.bing.com/translator/

    对象是在堆上分配的结构。 特殊规则适用于使用对象确保它们被正确地垃圾收集。

    对象从不静态或在堆栈上分配;他们必须仅通过特殊的宏和函数访问。

    (类型对象是第一条规则的例外情况;标准类型由静态初始化的类型对象, 虽然在类型/类统一上工作对于 Python 2.2, 也可以拥有堆分配的类型对象。

     

    --- 90分 sogou fanyi.sogou.com

    对象是在堆上分配的结构。特殊规则适用于使用对象确保它们被正确地垃圾收集。

    对象从不静态分配或在堆栈上分配;他们一定是仅通过特殊宏和函数访问。

    (类型对象是第一条规则的例外情况;标准类型由表示静态初始化的类型对象,尽管工作在类型/类统一上对于Python 2.2,也可以有堆分配的类型对象)

     

    --- 80分 baidu

    对象是堆上分配的结构。特殊规则适用于对象的使用确保它们被正确地垃圾收集。

    对象永远不会静态分配或堆栈;它们必须是仅通过特殊宏和函数访问。

    (类型对象是第一个规则的例外;标准类型由静态初始化类型对象,虽然在类型/类统一方面工作对于Python 2.2,也可以使用堆分配的类型对象。

     

    --- 70分 youdao http://fanyi.youdao.com/

    对象是在堆上分配的结构。特别规则适用于使用对象以确保正确地收集垃圾

    对象从不静态或堆栈上分配;他们必须仅通过特殊宏和函数访问。

    (类型对象第一项规则的例外情况;标准类型表示为静态初始化类型对象不过要处理类型/类的统一对于Python 2.2,也可以拥有堆分配的类型对象)

     

    结论:

    对这个测试例句,sogou翻译是最好的,基本没有错误,标点符号也处理的很好,而且还语句比较通顺。

    其次是google和bing,翻译不错,标点符号处理有欠缺(最后一句的右括号缺失);

    baidu 和 youdao 有语句明显有语病,容易导致误解,还需要继续改进。

    有比较专业的测评,也是sogou在线翻译评分最高,看来sogou的确有实力。

    展开全文
  • tomcat how work 中文版本

    2010-01-09 21:39:24
    tomcat how work 中文版本 是国内一些开源社区进行翻译的,该书主要是讲解tomcat怎么运行的一些核心资料。
  • BBR论文中文翻译

    千次阅读 多人点赞 2019-03-31 10:34:42
    BBR 论文中文翻译(原文:BBR: Congestion-Based Congestion Control) 译者:林佳烁 邮件:15622383059@163.com Github仓库:https://github.com/yue2388253/BBR-Translation Measureing bottleneck bandwidth and ...

    BBR 论文中文翻译(原文:BBR: Congestion-Based Congestion Control)

    译者:林佳烁 邮件:15622383059@163.com Github仓库:https://github.com/yue2388253/BBR-Translation

    Measureing bottleneck bandwidth and round-trip propagation time

    因为各种原因,今天的互联网并不能像我们所期望的那样很好地传输数据。世界上大部分蜂窝网络用户都会经历几秒乃至几分钟的时延;在公共局域如机场和会议厅等,WIFI质量经常是非常差的。物理和气候学者想要与全球范围内的合作者传输千兆字节级别的数据,但可能会发现他们精心准备的Gbps级别的底层网络在洲际传输中只能达到Mbps级别。

    这些问题之所以会发生,是因为在80年代设计TCP拥塞控制的时候,TCP将丢包作为“拥塞”的信号。在那个年代,这个假设确实是正确的,但这是因为受限于技术原因。而当网卡的速率从Mbps级别进化为Gbps级别,内存从KB级别进化到GB级别之后,丢包与拥塞的关系变得不那么紧密了。

    今天的这些TCP拥塞控制算法,包括至今为止最好的算法CUBIC,都是基于丢包的。他们是造成这些问题的主要元凶。当瓶颈链路的缓存较大时,这些基于丢包的拥塞控制算法流填满了缓存,造成了bufferbloat。当瓶颈链路的缓存较小时,这些算法会又将丢包作为发生拥塞的信号,从而降低速率导致了较低的吞吐量。为了解决这些问题,必须提出一种不是基于丢包的拥塞控制算法,这需要设计者对网络拥塞是如何并且在哪里产生的有非常深刻的理解。

    Congestion and Bottlenecks

    在一个TCP连接中,每个传输方向都存在一个最慢的链路,或者说瓶颈链路(bottleneck)。

    Bottleneck很重要!这是因为:

    1. 它决定了该连接的最大传输速率(举个例子:如果高速公路上的某一段路发生了车祸,将会导致该道上的车速降低至那一段路的车速)。

    2. 它是造成队列的元凶!因为只有一个链路的离开速率大于它的到达速率,队列才会缩短。对于一个尽力传输最大速率的连接来说,因为其他的链路速率都比bottleneck大,所以最后会造成bottleneck的队列。

    无论一个TCP连接需要穿越多少链路,也无论这些链路的速率各自是多少,从TCP的角度来说,一个及其复杂的路径的行为终究跟一条与它拥有相同RTT和bottleneck速率的简单链路一样。这两个参数,RTprop(Round-trip propagation time)和BtlBw(bottleneck bandwidth),决定了传输的性能。(打个比方,如果将一条网络路径类比为一个管道,RTprop就是管道的长度,BtlBw就是管道中最短的半径。)

    图1展示了RTT和发送速率与发送数据大小的关系。图中的蓝线受到了RTprop的约束,绿线受到BtlBw约束,红线受到瓶颈链路的缓存约束。注意阴影区域的部分是不可达的,因为这会至少违反一项约束。这三种约束形成了3不同的区域,分别为app-limited,bandwidth-limited,buffer-limited。在三种区域,流的行为是完全不同的。

    图1

    图1 发送速率和RTT vs 在外数据

    当没有足够的数据来填满管道时,RTprop决定了流的行为;当有足够的数据填满时,那就变成了BtlBw来决定。这两条约束交汇在点inflight=BtlBw*RTprop,也就是管道的BDP(带宽与时延的乘积)。当管道被填满时,那些超过的部分(inflight-BDP)就会在瓶颈链路中制造了一个队列,从而导致了RTT的增大,如图1所示。当数据继续增加直到填满了缓存时,多余的报文就会被丢弃了。拥塞就是发生在BDP点的右边,而拥塞控制算法就是来控制流的平均工作点离BDP点有多远。

    基于丢包的拥塞控制算法工作在bandwidth-limited区域的右边界区域,尽管这种算法可以达到最大的传输速率,但是它是以高延迟和高丢包率作为代价的。在存储介质较为的时候,缓存大小只比BDP大一点,此时这种算法的时延并不会很高。然而,当存储介质变得便宜之后,交换机的缓存大小已经是ISP链路BDP的很多很多倍了,这导致了bufferbloat,从而导致了RTT从毫秒级升到了秒级。

    工作在bandwidth-limited区域的左边界比工作在有边界好。在1979年Leonard Kleinrock就展示了,无论是对流自身而言,或是对整个网络来说,工作在左边界是最优点,这个点在实现最大传输速率的同时,保持了低时延和低丢包。不幸的是,当时Jeffrey M.Jaffe也同时证明了不可能存在一个分布式算法可以收敛到这个边界点。这使得学术研究不再尝试去设计可以工作在该边界点的分布式算法。

    我们谷歌工作组每天花了大量的时间来查看从世界各地发来的TCP报文头部,从而对流的行为机制有了很深的了解。我们通常首先计算出流最重要的两个参数,RTprop和BtlBw,这两个参数都可以从trace中推断出来。这表明Jaffe的结论可能不再适用。他当时做出这个结论是因为测量具有模糊性(例如,RTT的增加有可能是因为流的路径改变了,也有可能是瓶颈链路的带宽减少了,也有可能是因为别的流竞争而导致队列等等)。尽管不可能在单次测量中得到非常可靠的值,但是一个持续时间较长的连接会告诉你很多信息,从中或许可以设法来估计得到一个可靠的值。

    使用最近在控制领域中提出的鲁棒伺服环来对这些观测结果进行处理,可以设计出一种分布式拥塞控制协议。该协议可以针对真实的拥塞进行反应,而不是基于丢包或者短暂的队列时延的,它可以大概率收敛到Kleinrock的最优边界点。因此这推动了我们近三年的研究——如何基于这两个观测参数bottleneck带宽及RTT(这就是BBR缩写的来源,bottleneck bandwidth and round-trip propagation time)来设计拥塞控制算法。

    Characterizing the bottleneck

    当一个连接满足以下两个条件时,它可以在达到最高的吞吐量的同时保持最低时延:

    1. 速率平衡:瓶颈带宽的数据到达速率与BtlBw相等;

    2. 填满管道:所有的在外数据(inflight data)与BDP(带宽与时延的乘积)相等

    其中,第一个约束保证了瓶颈带宽可以得到100%利用。而第二个约束保证了流有足够的数据来填满瓶颈链路而且同时不会溢出(排队)。第一个条件本身并无法保证路径中不存在队列,它只能保证流的速率不发生改变(例如,考虑一个连接在一开始就发送了10个报文到一个BDP只有5个网络中,并且接下来一直保持瓶颈速率发送。这样子会导致在一开始就填满了管道,并且制造了5个报文的队列,并且这个队列永远不会被消灭)。相似地,full pipe条件也不能保证链路中没有队列。比如,一个TCP连接可以以burst方式发送BDP数量的报文,若该连接每次发二分之一BDP,并且发两次,此时full pipe条件得到满足了,然而网络中的平均队列长度是BDP/4。为了最小化网络中的队列长度,唯一的方式是同时满足以上两个条件。

    然而,BtlBw和RTprop可能是动态变化的,所以我们需要实时地对它们进行估计。目前TCP为了检测丢包,必须实时地跟踪RTT的大小。在任意的时间t,
    R T T t = R T p r o p t + η t RTT_t = RT_{prop_t} + \eta_t RTTt=RTpropt+ηt

    其中,最后一项表示“噪声”。造成噪声的因素主要有:链路队列,接收方的时延ACK配置,ACK聚合等因素等待。RTprop是路径的物理特性,并且只有路径变化才会改变。由于一般来说路径变化的时间尺度远远大于RTprop,所以RTprop可以由以下公式进行估计:

    R T p r o p ^ = R T p r o p + m i n ( η t ) = m i n ( R T T t )   ∀ t ∈ [ T − W R , T ] \widehat {RT_{prop}} = RT_{prop} + min(\eta_t) = min(RTT_t) \space \forall t \in [T -W_R, T] RTprop =RTprop+min(ηt)=min(RTTt) t[TWR,T]

    即,在一个时间窗口中对RTT取最小值。一般将该窗口大小设置为几十秒至几分钟。

    然而,bottleneck bandwidth的估计不像RTT那样方便,没有一种TCP spec要求实现算法来跟踪估计bottleneck带宽,但是,我们可以通过跟踪发送速率来估计bottleneck带宽。当发送方收到一个ACK报文时,它可以计算出该报文的RTT,并且从发出报文到收到ack报文这段时间的data Inflight。这段时间内的平均发送速率就可以以此计算出来:deliveryRate = delta delivered/ delta t。这个计算出的速率必定小于bottleneck速率(因为delta delivered是确定的,但是delta t会较大)。因此,BtwBw可以根据以下公式进行估计。
    B t l B w ^ = m a x ( d e l i v e r y R a t e t )   ∀ t ∈ [ T − W B , T ] \widehat{BtlBw} = max(deliveryRate_t) \space \forall t \in [T-W_B, T] BtlBw =max(deliveryRatet) t[TWB,T]
    其中,时间窗口大小的值一般为6~10个RTT。

    TCP必须记录每个报文的离开时间从而计算RTT。BBR必须额外记录已经发送的数据大小,使得在收到每一个ACK之后,计算RTT及发送速率的值,最后得到RTprop和BtlBw的估计值。

    值得注意的是,这两个值是完全独立的:RTprop可以发生变化然而保持bottleneck不变(比如发生路由变化),或者BtlBw可以变化而路径不变(比如无线链路速率发生变化)。(This independence is why both constraints have to be known to match sending behavior to delivery path.)如图1所示,只有在BDP的左边,才能观测到RTprop,并且只有在BDP的右边才能观测到BtlBw,他们遵循了一个不确定性原则:当其中一个可以被观测的时候,另外一个并不能。直觉地,这是因为为了观测出管道的容量,必须填满管道,而这会创造出一个队列从而无法观测到管道的长度。例如,一个使用request/response协议的应用可能永远不会发送足够的数据来填满管道,并且观测到BtlBw。一个持续几个小时的大文件传输应用可能永远处在bandwidth-limited区域,而仅仅在第一个报文中的RTT采集到RTprop。这种不确定性机制意味着,在信息变得不明确的时候,必须要有机制来从当前的工作状态中学习到这两个值的其中一个,并且进入对应的工作区来重新学习这两个值。

    Matching the packet flow to the delivery path

    BBR核心算法包含两大部分:

    • 当收到一个ACK报文时:

    每一个ACK提供了一个新的RTT和新的发送速率估计,BBR将以此为根据来更新RTprop和BtlBw。

    function onAck(packet)
    	rtt = now - packet.sendtime
    	update_min_filter(RTpropFilter, rtt) 
    	delivered += packet.size 
    	delivered_time = now 
    	deliveryRate = (delivered - packet.delivered) 
    				   /(now - packet.delivered_time)
    	if (deliveryRate > BtlBwFilter.currentMax 
    		|| ! packet.app_limited) 
    		update_max_filter(BtlBwFilter, 
    						  deliveryRate)
    	if (app_limited_until > 0) 
    		app_limited_until - = packet.size
    

    伪代码中的if语句是对上一段所讲的不确定性进行估计的:发送方可能受限于应用,发送数据太少,而并没有将管道填满。当发送方采取的协议是request/response类时,这种现象是非常常见的。当没有数据可以发送时,BBR会将对应的带宽估计标志为是应用限制的(见下文伪代码中的send())。这里的算法是为了决定流应该采集哪些数据,而避免采集那些被应用限制的而不是被网络限制的采集数据。BtlBw是发送速率的上界,所以如果计算出的发送速率大于当前所估计的BtlBw值,那么这表示这个估计值太小了(无论该发送速率是否是应用限制的),我们都应该更新它。否则,那些被应用限制的采集数据将会被丢弃。(如图1所示,在app-limited区域中,deliveryRate低估了BtlBw。这些if判断避免BBR低估了BtlBw而导致发送低速率)。

    • 当发送数据时

    为了使得bottleneck链路的报文到达速率和报文离开速率相等,BBR必须进行packet paced。BBR的发送速率必须与bottleneck的速率相等,这意味着BBR的实现需要pacing的支持——pacing_rate是BBR的一个主要控制参数!第二个参数,cwnd_gain,将inflight控制为比BDP稍大一些,从而处理常见的网络机制和接收方机制(参见后文Dealyed and Stretched ACKs)。TCP发送的时候做的事大概如下列伪代码所示(在Linux中,可以使用FQ/pacing qdisc来发送数据,这可以使得BBR在与几千个低速率的paced流在千兆链路上很好地共存,并且使用FQ这种机制也不会额外造成CPU的负担)。

    function send(packet)
    	bdp = BtlBwFilter.currentMax * RTpropFilter.currentMin
    	if (inflight >= cwnd_gain * bdp) // wait for ack or timeout 
    		return
    	if (now >= nextSendTime) 
    		packet = nextPacketToSend() 
    		if (! packet) 
    			app_limited_until = inflight 
    			return
    		packet.app_limited = (app_limited_until > 0)
    		packet.sendtime = now 
    		packet.delivered = delivered
    		packet.delivered_time = delivered_time 
    		ship(packet) 
    		nextSendTime = now + packet.size / (pacing_gain * BtlBwFilter.currentMax)
    	timerCallbackAt(send, nextSendTime)
    

    Steady-state behavior

    BBR的发送速率和发送数量完全就是一个关于BtlBw和RTprop的函数,所以BBR应该小心地估计这两个值。这创造了一种新型的闭环控制,如图2所示。图2显示了一个10Mbps,40ms的流在700ms中的RTT(蓝线),inflight(绿线)和发送速率(红线)变化过程。注意图中在发送速率上方的灰线是BtlBw最大化滤波器的状态。图中产生的三角形形状是因为BBR的pacing_gain周期性的变化产生的,因为BBR必须以此来探测BtlBw是否提高了。图中展示了该增益值在一个周期不同时间的变化和其影响的数据变化。

    BBR将它的大部分时间的在外发送数据都保持为一个BDP大小,并且发送速率保持在估计得BtlBw值,这将会最小化时延。但是这会把网络中的瓶颈链路移动到BBR发送方本身,所以BBR无法察觉BtlBw是否上升了。所以,BBR周期性的在一个RTprop时间内将pacing_gain设为一个大于1的值,这将会增加发送速率和在外报文。如果BtlBw没有改变,那么这意味着BBR在网络中制造了队列,增大了RTT,而deliveryRate仍然没有改变。(这个队列将会在下个RTprop周期被BBR使用小于1的pacing_gain来消除)。如果BtlBw增大了,那么deliveryRate增大了,并且BBR会立即更新BtlBw的估计值,从而增大了发送速率。通过这种机制,BBR可以以指数速度非常快地收敛到瓶颈链路。如图3显示的,我们在1条10Mbps,40ms的流在20s稳定运行之后将BtlBw提高了1倍(20Mbps),然后在第40s又将BtlBw恢复至20Mbps。

    图2 RTT(蓝线),在外数据(绿线)和发送速率(红线)细节

    (BBR is a simple instance of a Max-plus control system, a new approach to control based on nonstandard algebra.12 This approach allows the adaptation rate [controlled by the max gain] to be independent of the queue growth [controlled by the average gain]. Applied to this problem, it results in a simple, implicit control loop where the adaptation to physical constraint changes is automatically handled by the filters representing those constraints. A conventional control system would require multiple loops connected by a complex state machine to accomplish the same result.)

    Single BBR flow Startup behavior

    现有的TCP实现使用基于事件的算法来对事件如启动、关闭、丢包恢复(loss recovery)进行处理,这需要非常多的代码。BBR使用前文提到的代码(见Matching the Packet Flow to the Delivery Path一章)来应对一切。BBR通过一系列异步“状态”来处理事件,这些“状态”是根据一张包含多个固定增益和退出条件的表来定义的。BBR大多时候都出于ProbeBW阶状态(见Steady-state Behavior一章)。在连接启动的时候,BBR使用了Startup和Drain状态(见图4)。为了处理大小范围超过12个数量级的带宽,Startup对BtlBw进行二乘查找,它使用2/ln2的增益来对发送速率翻倍。这将会在log_2BDP的RTT时间内确定BtlBw的值,但会制造最多2个BDP的队列。一单Startup找到了BtlBw,BBR转变为Drain状态,该状态使用了Startup增益的倒数来消耗队列。消耗完队列之后,BBR进入ProbeBW阶段来维持1个BDP的在外数据。

    在这里插入图片描述

    图3 带宽变化<\center>

    图4展示了1个10Mbps,40ms的BBR流在一开始的1秒内,发送方(绿线)和接收方(蓝线)的过程。红线表示的是同样条件下的CUBIC发送。垂直的灰线表示了BBR状态的转换。下方图片展示了两种连接的RTT在这段时间的变化。注意,只有收到了ACK(蓝线)之后才能确定出RTT,所以在时间上有点偏移。图中标注了BBR何时学习到RTT和如何反应。

    在这里插入图片描述

    图4 10Mbps、40ms链路上BBR流的第一秒

    图4的下图展现了BBR和CUBIC行为的巨大不同。他们的初始行为都是相似的,但是BBR可以完全地消耗掉它启动阶段产生的队列,而CUBIC并不能。CUBIC没有一个路径模型来告诉它应该发送多少报文,所以CUBIC一直在缓慢地增加他的在外报文,直到瓶颈链路缓存填满而丢包、或者接收方的缓存(TCP接受窗口)满了。

    图5展示了在图4中展示的BBR和CUBIC流在开始8秒的行为。CUBIC(红线)填满了缓存之后,周期性地在70%~100%的带宽范围内波动。然而BBR(绿线)在启动过程结束后,就非常稳定地运行,并且不会产生任何队列。

    在这里插入图片描述

    图5 在10Mbps、40ms链路上的BBR流和CUBIC流的前8秒对比

    Multiple BBR flows sharing a bottleneck

    图6展示了在一个100Mbps、10ms的瓶颈链路上共享的多个BBR流是如何收敛到公平值的。他们之所以会减小带宽是因为他们进入了ProbeRTT阶段,使得他们快速地收敛。

    ProbeBW机制(见图2)会使得大流会让渡带宽给小流,最终使得每个流都学习到自己的fair share。尽管后发送者可能会因为别的流暂时地创造了队列,而高估了RTprop,从而导致不公平性,但最终都会在几个ProbeBW周期内收敛到公平值。

    为了得到真实的RTprop值,一个流会使用ProbeRTT状态来将自己的工作点移动到BDP的左边:当好几秒内都没有更新RTprop时,BBR进入ProbeRTT状态,这会使得inflight在至少一个RTT内降低为4个报文,然后BBR再重新进入正常阶段。大流进入ProbeRTT阶段会消耗队列中的许多报文,所以会让其他的流学习到一个更小的RTprop值。这会同时让他们的RTprop估计值过期,并同时一起进入ProbeRTT阶段。这会更快地消耗掉队列中的报文,并使得更多的流学习到一个新的RTprop值并一直循环。这个分布式的合作机制是实现公平性和稳定性的重点!

    在这里插入图片描述

    图6 共享同一个瓶颈链路的5个BBR流的吞吐量

    BBR可以使得多个流共享一个链路而不会造成队列,而基于丢包的拥塞控制算法会造成队列周期性地增长然后再溢出,导致了高时延和丢包。

    Google B4 WAN deployment experience

    谷歌B4网络是一个使用商业交换机的高速广域网。在这个网络中,丢包经常是因为这些只有少量缓存的交换机没办法缓存大量突发性的报文。在2015年谷歌开始将B4的生产流量从CUBIC换成BBR。在这个过程中没有出现任何问题,一切都很顺利。直到2016年,所有B4的TCP流量都使用BBR。图7展示了做这种改变的一个原因:BBR的吞吐量能够稳定地维持在CUBIC的2到25倍。我们本来期望能够提高得更多,但是最终发现75%的BBR连接都被内核的TCP接收缓存限制了,这是因为网络运维组故意将缓存设成比较低的值(8MB)来避免CUBIC发送太多的inflight。手动地在一条从美国到欧洲的路径上的接收缓存调高,会立即将BBR的吞吐量提高至2Gbps,然而CUBIC还是维持在15Mbps(Mathis语言可以达到133倍的提高)。

    在这里插入图片描述

    图7 BBR的吞吐量相对CUBIC的吞吐量提高比例

    图7展示了BBR相比CUBIC的吞吐量提升,子图显示的是吞吐量的CDF(累积分布函数)。图中的数据来源于一个探测服务,该探测服务每分钟都会打开一个BBR连接和一个CUBIC连接到远端数据中心,然后传输8MB数据。这些连接包括了B4的很多路径,如在北美、欧洲、亚洲之间或者在洲内传输路径等等。

    BBR不再使用丢包作为拥塞信号是我们的一大贡献!为了达到最大带宽,现有的基于丢包的拥塞控制算法需要丢包率小于BDP的倒数的平方(例如,在1Gbps/100ms的链路上,丢包率需要小于三千万分之一)。图八比较了BBR和CUBIC在不同丢包率的吞吐量。可以看到,CUBIC很受丢包影响,而BBR的影响并不大。当BBR的丢包率接近ProbeBW的增益时,估计到实际BtlBw的概率急剧下降,这导致了BBR低估了BtlBw。

    图8展示了在一条100Mbps,100ms的链路上,BBR和CUBIC在60秒内的吞吐量与随机丢包率(从0.001%~50%)的关系。在丢包率只有0.1%的时候,CUBIC的吞吐量就已经下降了10倍,并且在丢包率为1%的时候就几乎炸了。而理论上的最大吞吐量是链路速率乘以(1-丢包率)。BBR在丢包率为5%以下时还能基本维持在最大吞吐量附近,在15%丢包率的时候虽然有所下降但还是不错。

    YOUTUBE edge deployment experience

    我们将BBR部署在Google.com和YouTube的视频服务器上。我们随机挑选一部分用户来使用CUBIC或BBR来进行实验。使用BBR算法的playback基本上在所有的YouTube的QoE参数上都有所显著提高,这可能是因为BBR的行为是更加一致的并且是可预测的。由于YouTube已经能很好地自适应将视频传输速率调节到BtlBw之下来避免bufferbloat和重缓存,所以BBR在吞吐量上只比CUBIC提高了一点点。尽管如此,在全世界范围内,BBR将RTT中位数平均降低了53%,而这个数值在发展中国家是80%。图9统计了在一星期中,在五大洲的超过2亿YouTube的playback连接中,BBR的RTT中位数相对于CUBIC的提高幅度。

    在这里插入图片描述

    图8 BBC和CUBIC的吞吐量与丢包率的关系

    在全球范围内的70亿互联网移动终端中,超过一半设备使用8至114kbps的2.G系统。因为基于丢包的拥塞控制算法填满buffer的特点,这些连接已经暴露了太多太多的问题。他们的bottleneck链路一般情况下都是无线终端与基站之间的链路。图10展示了使用BBR和使用CUBIC在基站与用户终端见的延迟的对比。其中的水平线标志了一个很严重的结果:TCP在连接建立阶段没有办法忍受太长的RTT时延,在该阶段,TCP等待SYN的时延是一个与操作系统相关的timeout。当移动设备在接收大量的数据而此时的基站缓存又足够大时,在基站的队列耗尽之前,移动终端将没有办法打开新的连接到互联网!(这是因为移动终端等到花儿都谢了都没有等到SYN ACK)

    在这里插入图片描述

    图9 BBR的RTT中位数相比CUBIC的提高比例

    图10展示了在稳定状态下,在一个具有8个BBR(绿线)或CUBIC流(红线)的128Kbps,40ms的链路上,RTT中位数与链路缓存大小的关系。由图中可以看到,无论链路缓存如何变化,BBR都可以保持队列为空。而由于CUBIC总会把缓存填满,所以CUBIC的曲线随着缓存的增大而线性增长。
    在这里插入图片描述

    图10 稳定状态下RTT中位数随着链路缓存大小的变化

    Mobile cellular adaptive bandwidth

    蜂窝系统对每一个用户使用一个队列来统计报文数量,并以此为根据来评估用户的需求带宽,从而自适应调整每个用户的带宽。早期我们为了不创造太深的队列,调整了BBR,但是这导致了连接速率非常慢。提高ProbeBW的pacing_gain会创造较长的队列,但会减少低速连接的数量。从这里我们学到了,BBR可能对某些网络太过仁慈了!目前我们将BtlBw的增益设置为1.25,经过测试,BBR不会在任何一种网络中输给CUBIC了!

    Delayed and stretched acks

    蜂窝、WiFi以及有限宽带网络经常会将ACK进行延时或者聚合。当BBR将带宽控制为1个BDP时,这种行为可能会导致吞吐量不够高。可以将ProbeBW的cwnd_gain提高为2从而允许BBR持续地以估计的速率平滑地发送数据,即使ACK被延时了1个RTT。这可以很好地避免BBR产生低吞吐量。

    Token-bucket policers

    早期我们将BBR部署在YouTube的时候,我们就发现了全球范围内大多数ISP都会使用令牌桶策略将流量进行限速。

    大多数情况下,在连接的开始阶段,bucket都是满的,所以BBR可以学习到网络的Btlbw,然而一旦bucket空了,所有发送速率大于bucket补充速率(该速率远小于BtlBw)的报文都会被丢弃。最终BBR会学习这个新的发送速率,然而ProbeBW仍然会导致持续丢包。为了避免浪费带宽,为了降低这些丢包带来的时延,我们为BBR添加了一个流量管控检测模块和一个显式的流量管控模块。我们也正在寻找更好的办法来降低这种流量管控带来的影响。

    Competition with loss-based congestion control

    无论竞争流的拥塞控制算法是BBR还是其他基于丢包的算法,BBR都能最终收敛到一个公平值。即使基于丢包的算法会将缓存填满,ProbeBW仍然会鲁棒地将BtlBw估计为公平值,ProbeRTT仍会估计到一个较高的RTProp来保持公平性。然而,有时候,路由器的缓存太大,大小超过了几个BDP,这会使得持续时间较长的基于丢包的竞争流填满了队列,而得到了一个较大的份额。未来的研究方向包括如何降低这种流的影响!

    Conclusion

    重新对拥塞控制进行思考会带来很大的好处。BBR并不是基于事件如丢包、或者发生缓存等,这些事件并没有跟拥塞发生非常紧密的联系。BBR起源于Kleinrock关于拥塞的模型,它工作在那个最优点上。通过异步观测估计时延和带宽,BBR绕过了那个不确定魔咒,即时延和带宽没办法同时被估计。我们使用了控制和估计理论最近几年的发展,设计了一个简单的分布式控制环,它可以近似地工作在最优点,在完全利用网络带宽的同时并不会制造长的队列。目前开源的Linux内核TCP已经集成了谷歌的BBR实现,关于更多的细节,请查看附录。

    我们将BBR部署在谷歌的B4骨干网,它的吞吐量相比CUBIC提高了几个数量级。我们也将BBR部署在谷歌和YouTube的Web服务器上,它减小了五大洲的传输时延,特别是在发展中地区。BBR仅工作在发送端,它无需对协议、接收方、网络作任何修改,这使得它非常容易部署。BBR只关心RTT和数据传输速率,所以它能够被部署在大多数的传输层协议上。

    联系我们:https://googlegroups.com/d/forum/bbr-dev

    Acknowledgments

    The authors are grateful to Len Kleinrock for pointing out the right way to do congestion control. We are indebted to Larry Brakmo for pioneering work on Vegas2 and New Vegas congestion control that presaged many elements of BBR, and for advice and guidance during BBR’s early development. We would also like to thank Eric Dumazet, Nandita Dukkipati, Jana Iyengar, Ian Swett, M. Fitz Nowlan, David Wetherall, Leonidas Kontothanassis, Amin Vahdat, and the Google BwE and YouTube infrastructure teams for their invaluable help and support.

    References

    1. Abrahamsson, M. 2015. TCP ACK suppression. IETF AQM mailing list; https://www.ietf.org/mail-archive/web/aqm/urrent/msg01480.html.

    2. Brakmo, L. S., Peterson, L.L. 1995. TCP Vegas: end-to-end congestion avoidance on a global Internet. IEEE Journal on Selected Areas in Communications 13(8): 1465–1480.

    3. Chakravorty, R., Cartwright, J., Pratt, I. 2002. Practical experience with TCP over GPRS. In IEEE GLOBECOM.

    4. Corbet, J. 2013. TSO sizing and the FQ scheduler. LWN. net; https://lwn.net/Articles/564978/.

    5. Ericsson. 2015 Ericsson Mobility Report (June); https:// www.ericsson.com/res/docs/2015/ericsson-mobility- report-june-2015.pdf.

    6. ESnet. Application tuning to optimize international astronomy workflow from NERSC to LFI-DPC at INAF- OATs; http://fasterdata.es.net/data-transfer-tools/case- studies/nersc-astronomy/.

    7. Flach, T., Papageorge, P., Terzis, A., Pedrosa, L., Cheng, Y., Karim, T., Katz-Bassett, E., Govindan, R. 2016. An Internet-wide analysis of traffic policing. In ACM SIGCOMM: 468–482.

    8. Gail, R., Kleinrock, L. 1981. An invariant property of computer network power. In Conference Record, International Conference on Communications: 63.1.1- 63.1.5.

    9. Gettys, J., Nichols, K. 2011. Bufferbloat: dark buffers in the Internet. acmqueue 9(11); http://queue.acm.org/ detail.cfm?id=2071893.

    10. Ha, S., Rhee, I. 2011. Taming the elephants: new TCP slow start. Computer Networks 55(9): 2092–2110.

    11. Ha, S., Rhee, I., Xu, L. 2008. CUBIC: a new TCP-friendly high-speed TCP variant. ACM SIGOPS Operating Systems Review 42(5): 64–74.

    12. Heidergott, B., Olsder, G. J., Van Der Woude, J. 2014. Max Plus at Work: Modeling and Analysis of Synchronized Systems: a Course on Max-Plus Algebra and its Applications. Princeton University Press.

    13. Jacobson, V. 1988. Congestion avoidance and control. ACM SIGCOMM Computer Communication Review 18(4): 314–329.

    14. Jaffe, J. 1981. Flow control power is nondecentralizable. IEEE Transactions on Communications 29(9): 1301–1306.

    15. Jain, S., Kumar, A., Mandal, S., Ong, J., Poutievski, L., Singh, A., Venkata, S., Wanderer, J., Zhou, J., Zhu, M.,et al. 2013. B4: experience with a globally-deployed software defined WAN. ACM SIGCOMM Computer Communication Review 43(4): 3–14.

    16. Kleinrock, L. 1979. Power and deterministic rules of thumb for probabilistic problems in computer communications. In Conference Record, International Conference on Communications: 43.1.1-43.1.10.

    17. Mathis, M., Semke, J., Mahdavi, J., Ott, T. 1997. The macroscopic behavior of the TCP congestion avoidance algorithm. ACM SIGCOMM Computer Communication Review 27(3): 67–82.

    18. Wikipedia. GPRS core network serving GPRS support node; https://en.wikipedia.org/wiki/GPRS_core_ network#Serving_GPRS_support_node_.28SGSN.29.

    Related Articles

    Appendix - Detailed Description

    A state machine for sequential probing

    pacing_gain用于控制相对于BtlBw的数据发送速率,它是BBR的关键!当pacing_gain大于1时,它增加了在外报文数量,减少了数据到达间隔时间,在图1中,它将工作点右移了。当pacing_gain小于1时,它的效果刚好相反,它将工作点往左移。

    BBR使用pacing_gain来实现一个简单的异步探测状态机,它不停地探测高可用带宽和低时延。(没有必要去探测带宽是否变低了,因为BtlBw滤波器会自动地做这件事:当一个估计值迟迟没有更新时,它就会丢弃掉这个值重新估计带宽了。类似地,RTprop滤波器也会自动地丢掉过时的估计值)。

    如果链路带宽提高了,BBR必须发送得快一些来发现它。相似地,如果实际的往返传播时延变化了,从而改变了BDP,因此BBBR必须将发少一些,使得在外传输报文数量低于BDP从而估计新的RTprop值。因此,为了发现这些动态变化,唯一的办法就是做探测:发快点来检测BtlBw是否提高,发慢点来检测RTprop是否降低。做这些探测的频率、强度、持续时间和结构都取决于已知(在startup和稳定状态)和发送应用的行为(断续的或持续的)。

    Steady-state behavior

    BBR大多数的时间都处于ProbeBW状态,它使用一种名为gain cycling的方法来探测带宽,从而取得较高的吞吐量,较低的队列实验,并且最终收敛到一个公平带宽值。使用这个方法,BBR周期性地调整pacing_gain值。它使用一个有八个阶段的周期,值分别为:5/4,3/4,1,1,1,1,1,1。每个阶段一般持续时间为估计的RTprop值。这种设计使得BBR首先使用一个大于1的pacing_gain值来探测高可用带宽,并在下一阶段使用一个低于1的值来消耗队列,然后接下来都稳定工作在值为1的阶段。因为ProbeBW旨在调整平均发送速率与可用带宽相等来保持对网络的高利用率,并且同时不制造一个长队列,所以该增益值的平均值为1。注意,尽管pacing_gain的值随着时间变化,但是cwnd_gain的值并不变化,它保持为2,这是因为ACK会被时延(详见章节Delayed and Stretched Acks)。

    而且,为了提高公平性,并且在多个BBR流共享同一个瓶颈链路的时候能保持队列较短,BBR随机挑选一个值来作为gain cycling周期的开始值来进入ProbeBW状态(除了3/4)。为什么3/4不能被挑选为初始值呢?因为3/4是为了消耗在它之前的那个5/4产生的队列。当BBR从Drain状态或ProbeRTT状态进入ProbeBW状态时,并不需要消耗队列。使用3/4只会使得在该阶段,链路的利用率为3/4,而不是1。既然3/4没有好处只有坏处,我们干嘛还要使用它呢?

    多个BBR流可以周期地进入ProbeRTT状态从而消耗掉瓶颈链路的队列。当BBR的状态不是ProbeRTT时,只要RTProp估计值一段时间内(超过10秒)没有得到更新(如,得到一个更低的RTT观测值),那么BBR就会进入ProbeRTT状态,并且将cwnd值减小为一个非常小的值(4个包)。在保持这个非常小的cwnd值运行至少200ms和一个往返时间后,BBR离开ProbeRTT状态,进入Startup或者ProbeBW阶段(这取决于它对当前管道是否为满的评估)。

    我们将BBR设计为将绝大多数的时间(约98%)处于ProbeBW阶段,并且其余的时间处于ProbeRTT阶段。ProbeRTT会至少持续200ms来适应不同的RTT,这对整个BBR流的性能惩罚已经足够小了(大约在2%,200ms/10ms)。)对于流量变化或路由变化来说,RTprop滤波器的窗口大小(10s)足够小,它可以快速收敛。另一方面对于那些交互性的应用(如网页浏览、RPC、视频传输等)来说,这个窗口大小是足够大的,它可以允许这些应用在一小段时间内传输很少的数据,而且可以消耗掉这段瓶颈链路的队列。由于RTprop滤波器会时常更新值,所以BBR并不必经常进入ProbeRTT阶段。通过这种方式,典型地,当多个大流持续地在整个RTprop窗口内发送大量报文时,BBR只需要受到2%的惩罚。

    Startup behavior

    当一个BBR流开始启动的时候,它开始它的第一个异步探测过程。网络链路的带宽范围非常大(10^12),从几个位每秒到100kMb每秒。为了在这个这么大的范围内学习到BtlBw,BBR的查找是幂次的。这可以非常快地在确定BtlBw的值(在log_2BDP时间内),但这会在最后一步时制造出2BDP的队列。在BBR的Startup阶段后,Drain阶段会消耗掉这些队列。

    一开始,Startup以幂次方增加发送速率,每次将它乘以2。为了平滑地快速地进行这个探测的过程,在Startup阶段,BBR的pacing_gain和cwnd_gain被设为2/ln2,这个值是可以使得发送速率每次乘2的最小值。一旦管道满了,cmwd_gain会保证队列的长度不超过(cwnd_gain-1)*BDP。

    当BBR处于Startup状态时,若BtlBw估计值在一段时间内不变,那么BBR判断当前管道已满。如果它注意到在几个(3)往返时间内,当它尝试对发送速率进行翻倍并没有引起很大的提升(低于25%),那么它判断它已经到达了BtlBw,所以BBR会离开Startup状态,然后进入Drain阶段。之所以BBR要等待3个往返时间,是因为它要确保BtlBw估计值不发生改变不是因为接收方的接收窗口导致的。BBR等待3个往返时间使得接收方可以调节接收窗口,从而使得BBR发送方可以察觉到BtlBw可以更高:在第一个往返中,接收方窗口自动调节的算法会将接收窗口增大;第二个往返中,发送方将该接收窗口填满;所以,第三个往返中,发送方可以取得更高的发送速率估计值。我们通过YouTube的实验数据来验证了这个阈值(3个往返)。

    BBR的Drain状态旨在快速消耗掉Startup状态产生的队列,它是通过将pacing_gain的大小设为其在Startup状态时的大小的倒数来实现的,这会在一个往返时间内就消耗完队列。当在外数据大小与估计的BDP值相等时,这意味着BBR已经将网络中的队列清空了,并且此时的管道仍然是满的。此时BBR离开Drain状态,进入ProbeBW状态。

    注意,BBR的Startup阶段和CUBIC的慢启动都是以指数探测瓶颈带宽,它们都在每个往返将发送速率翻倍。然而它们是不同的。首先,BBR在发现可用带宽这方面更加鲁棒,因为它并不是根据丢包或者时延的增加(如CUBIC的Hystart)来离开Startup状态的。第二,BBR平滑地加速它的发送速率,而CUBIC在每一次往返都是突发地发送数据,然后在接下来一段时间内就不发了(就算设置了pacing也一样)。图4展示了BBR和CUBIC在每次收到ack的在外报文数量和RTT。

    Reacting to transients

    网络路径和网络流量都可能会发生突然变化。为了平滑地、鲁棒地处理这些变化,并且降低在这些情况下的丢包,BBR使用一些列策略在实现它的核心模型。首先,BBR的在外数据目标是cwnd_gain与BDP的乘积,而BBR的cwnd是小心地增大的,cwnd每次增大的大小都不会超过被确认的数据。其次,当发生重传超时时,这意味着发送端会认为所有的在外报文都被丢弃了,BBR保守地将cwnd减小为1,并且仅发送1个报文(就像CUBIC等那些基于丢包的拥塞控制算法一样)。最后,当发送方发现丢包,但此时仍存在在外数据时,在第一个往返时,BBR会暂时地将发送速率减小为当前的发送速率;在第二个往返以及之后,它会确保发送速率不会超过当前发送速率的两倍。当BBR遇到流量管控或者与其他的流竞争一个缓存只有一个BDP的链路时,这会显著地降低暂时的丢包。

    The authors are members of Google’s make-tcp-fast project, whose goal is to evolve Internet transport via fundamental research and open source software. Project contributions include TFO (TCP Fast Open), TLP (Tail Loss Probe), RACK loss recovery, fq/pacing, and a large fraction of the git commits to the Linux kernel TCP code for the past five years.

    最后,向大牛致敬吧!

    展开全文
  • 电机控制工作台(MotorControl Workbench)中文翻译介绍
  • gpl3.0中文翻译

    千次阅读 2019-04-08 21:15:42
    如果有,请看本文的翻译,这是译者花费30个小时,参考多份译本,自己翻译的。当然译者水平有限,难免有错误、疏漏,望各位读者谅解。本译本的目的只有一个:让你能够流畅的读完这个开源许可证,并且理解原文表达的...

    各位读者在阅读GNU GPL V3.0的时候,是否一头雾水,各种卡壳,被译文弄得不知所云、痛苦不堪。如果有,请看本文的翻译,这是译者花费30个小时,参考多份译本,自己翻译的。当然译者水平有限,难免有错误、疏漏,望各位读者谅解。本译本的目的只有一个:让你能够流畅的读完这个开源许可证,并且理解原文表达的核心意思,如果能够达到此目的,即实现了译者的初衷。

    本译本尽可能尊重英文原版,但为了便于读者理解,译者对部分名词,采用了使用我国著作权法里专有名词的译法(内涵可能存在一定区别),并将原文里部分的长句或者从句的语法结构进行了变更、拆解或者组合,将部分无关紧要的定语进行了删减,将部分不符合中文表达规范的用词进行了类似的替换,以使得读着可以流畅的阅读并掌握原文的主要内容。欢迎对此译本进行免费转载,转载请完整保留译者信息。欢迎各位读者批评指正。

    译者:徐顺,北京市京都(深圳)律师事务所执业律师 电话:15989486116,微信号:xushun19,欢迎交流开源法律问题,欢迎关注微信公众号:ziyouwusuojiban。

    在这里插入图片描述
    This translation of the GPL is informal, and not officially approved by the Free Software Foundation as valid. To be completely sure of what is permitted, refer to the original GPL (in English).
    本 GPL 翻译文本是非正式的,没有被自由软件基金会(FSF)正式批准为有效。若要完全确定何种行为被允许,请参阅原始 GPL(英文)。

    GNU GENERAL PUBLIC LICENSE

    GNU通用公共授权许可证

    Version 3, 29 June 2007

    2007年6月29日第三版

    Copyright © 2007 Free Software Foundation, Inc. http://fsf.org/
    Everyone is permitted to copy and distribute verbatim copies
    of this license document, but changing it is not allowed.

    自由软件基金会公司,2007版权所有。 http://fsf.org/
    每个人都可以原样复制和呈现此授权许可证全文,但不允许修改。

    Preamble
    序言

    The GNU General Public License is a free, copyleft license for
    software and other kinds of works.

    GNU通用公共授权许可证是一种可以免费使用的,针对软件和其他作品的“著佐权”许可证。

    The licenses for most software and other practical works are designed
    to take away your freedom to share and change the works. By contrast,
    the GNU General Public License is intended to guarantee your freedom to
    share and change all versions of a program–to make sure it remains free
    software for all its users. We, the Free Software Foundation, use the
    GNU General Public License for most of our software; it applies also to
    any other work released this way by its authors. You can apply it to
    your programs, too.

    绝大多数的软件和实用作品的授权许可证,都被设计为不允许共享作品、修改作品。相较而言,GNU通用公共授权许可证意在保证您拥有共享和修改一个程序的全部版本的自由,使得软件面向所有用户均保持为自由软件。我们自由软件基金,在我们大多数软件中都使用GNU通用公共授权许可证,许可证同样可以适在希望按照这种方式发布的任何其他作品上。你也可以让你的软件使用本授权。

    When we speak of free software, we are referring to freedom, not
    price. Our General Public Licenses are designed to make sure that you
    have the freedom to distribute copies of free software (and charge for
    them if you wish), that you receive source code or can get it if you
    want it, that you can change the software or use pieces of it in new
    free programs, and that you know you can do these things.

    当我们谈及自由软件软件,我们指的是行动自由,而非价格免费。我们的通用公共授权许可证意在确保你拥有发行自由软件副本的自由(如果你愿意,也可以收费),确保你可以收到源代码,或者在你想要的时候能找到源代码,确保你可以修改相关软件或者将其一部分用于新的自由程序中,同时确保你知道你有权做上述事情。

    To protect your rights, we need to prevent others from denying you
    these rights or asking you to surrender the rights. Therefore, you have
    certain responsibilities if you distribute copies of the software, or if
    you modify it: responsibilities to respect the freedom of others.

    为了保护你的权利,我们需要防止其他人否认你的这些权利,或者要求你放弃这些权利。因此,如果你发行相关软件副本,或者修改副本,你需要肩负起尊重他人自由的责任。

    For example, if you distribute copies of such a program, whether
    gratis or for a fee, you must pass on to the recipients the same
    freedoms that you received. You must make sure that they, too, receive
    or can get the source code. And you must show them these terms so they
    know their rights.

    例如,如果你发行一个自由程序的副本,无论是免费的还是收费的,你必须将你从上游收到的自由,向下游传递下去。你必须确保他们也可以收到或者找到源代码。你必须向他们展示同样的授权条款,让他们知道他们的权利。

    Developers that use the GNU GPL protect your rights with two steps:
    (1) assert copyright on the software, and (2) offer you this License
    giving you legal permission to copy, distribute and/or modify it.

    使用GNU通用公共授权许可证的开发者们,通过以下两个步骤保护你的权利:(1)在软件上声明拥有版权(2)给你提供本授权许可证,给你法律上的复制权、发行权和/或修改权。

    For the developers’ and authors’ protection, the GPL clearly explains
    that there is no warranty for this free software. For both users’ and
    authors’ sake, the GPL requires that modified versions be marked as
    changed, so that their problems will not be attributed erroneously to
    authors of previous versions.

    为了对开发者和作者进行保护,本通用公共授权许可证明确说明如下:自由软件不含任何担保。无论是从使用者,还是从作者的利益角度出发,本通用公共授权许可证要求:如果对软进行了修改,则需要进行标注,以避免修改版软件中的问题会被错误的归咎到先前版本的作者身上。

    Some devices are designed to deny users access to install or run
    modified versions of the software inside them, although the manufacturer
    can do so. This is fundamentally incompatible with the aim of
    protecting users’ freedom to change the software. The systematic
    pattern of such abuse occurs in the area of products for individuals to
    use, which is precisely where it is most unacceptable. Therefore, we
    have designed this version of the GPL to prohibit the practice for those
    products. If such problems arise substantially in other domains, we
    stand ready to extend this provision to those domains in future versions
    of the GPL, as needed to protect the freedom of users.

    有些设备被设计成拒绝用户在其上面安装或者运行修改版的软件,而将此权利保留给制造商(译者注:例如打印机)。这种情况与保护用户拥有自由修改软件的权利出现了根本性冲突。这种系统性大量滥用权利的情况,出现在了个人用户产品领域,这是最让人无法接受的。因此,我们设计了此版本的通用公共授权许可证来阻止这样的做法。如果这些问题在其他领域实质性出现,我们准备将在未来版本的通用公共授权许可证里,将条款延伸至这些相应领域,用来保护用户的自由。

    Finally, every program is threatened constantly by software patents.
    States should not allow patents to restrict development and use of
    software on general-purpose computers, but in those that do, we wish to
    avoid the special danger that patents applied to a free program could
    make it effectively proprietary. To prevent this, the GPL assures that
    patents cannot be used to render the program non-free.

    最后,每个程序都持续的受到软件专利的威胁。政府不应当让专利限制了通用计算机软件的发展和使用。但是,在那些允许专利限制的地方,我们希望可以避免这种因专利导致自由软件实质上被私有化的特殊威胁。为了防止此事,本通用公共授权许可证声明,专利不能够不能够被用来将自有软件程序非自由化。

    The precise terms and conditions for copying, distribution and
    modification follow.

    有关复制权、发行权和修改权的具体条款及适用条件如下

    TERMS AND CONDITIONS

    条款及适用条件

    1. Definitions.

    0.定义

    “This License” refers to version 3 of the GNU General Public License.

    “本授权许可”指GNU通用公共授权许可证第三版。

    “Copyright” also means copyright-like laws that apply to other kinds of
    works, such as semiconductor masks.

    “版权”亦至适用于其他作品种类的,类似版权法的法律,如半导体掩膜领域的版权保护法律。

    “The Program” refers to any copyrightable work licensed under this
    License. Each licensee is addressed as “you”. “Licensees” and
    “recipients” may be individuals or organizations.

    “程序”指由本许可证授权的所有可获得版权的作品。被授权人称为“你”,“被授权人”和“接收人”可以是个人,也可以是组织。

    To “modify” a work means to copy from or adapt all or part of the work
    in a fashion requiring copyright permission, other than the making of an
    exact copy. The resulting work is called a “modified version” of the
    earlier work or a work “based on” the earlier work.

    “修改”一份作品指的是除了制作一份完全相同的副本之外,对作品整体或者一部分进行复制或改变。所产生的作品称为先前作品的“修改版”,或者是“基于”先前作品的作品。

    A “covered work” means either the unmodified Program or a work based
    on the Program.

    “受保护作品”指未经修改的程序作品,或基于该程序作品的衍生作品。

    To “propagate” a work means to do anything with it that, without
    permission, would make you directly or secondarily liable for
    infringement under applicable copyright law, except executing it on a
    computer or modifying a private copy. Propagation includes copying,
    distribution (with or without modification), making available to the
    public, and in some countries other activities as well.

    “传播”作品指除了在计算机上运行或者修改一份自己持有副本情形外,任何可能使你直接或者间接承担版权法侵权责任的行为。传播行为包括复制、分发(无论是否修改),让公众共享,和在某些国家里的其他类似行为。

    To “convey” a work means any kind of propagation that enables other
    parties to make or receive copies. Mere interaction with a user through
    a computer network, with no transfer of a copy, is not conveying.

    “传递”作品指任何能够使其他主体制作或获取副本的所有传播行为。仅仅通过计算机网络与用户交互,而没有传送副本,不属于传递。(译者注:此处属于“传播”但不属于“传递”的行为,指本人或者本组织内的传播,例如多台设备之间的安装或备份)

    An interactive user interface displays “Appropriate Legal Notices”
    to the extent that it includes a convenient and prominently visible
    feature that (1) displays an appropriate copyright notice, and (2)
    tells the user that there is no warranty for the work (except to the
    extent that warranties are provided), that licensees may convey the
    work under this License, and how to view a copy of this License. If
    the interface presents a list of user commands or options, such as a
    menu, a prominent item in the list meets this criterion.

    交互式用户界面中的“相关法律声明”应当以便利且显著的可视化的特征来展示,法律声明包括:(1)一份合适的版权说明,和(2)告诉用户:a.本作品不含担保(除非确实提供了担保), b.本授权许可证持有人有权传递本作品,c.告诉用户如何查看本授权许可证的副本。如果交互式界面中有用户命令和选项列表,比如菜单,在列表中的一个显著栏目来展示法律声明,即符合规范。

    1. Source Code.

    1、源代码

    The “source code” for a work means the preferred form of the work
    for making modifications to it. “Object code” means any non-source
    form of a work.

    作品“源代码”指对作品进行修改时最常用的代码表现形式。“目标代码”指作品的任何非源代码形式的代码。

    A “Standard Interface” means an interface that either is an official
    standard defined by a recognized standards body, or, in the case of
    interfaces specified for a particular programming language, one that
    is widely used among developers working in that language.

    “标准接口”指接口是由公认的标准组织来定义的符合官方标准的接口、或者是某种特定语言定义的众多接口中,由使用这种语言的开发者们广泛使用的接口。

    The “System Libraries” of an executable work include anything, other
    than the work as a whole, that (a) is included in the normal form of
    packaging a Major Component, but which is not part of that Major
    Component, and (b) serves only to enable use of the work with that
    Major Component, or to implement a Standard Interface for which an
    implementation is available to the public in source code form. A
    “Major Component”, in this context, means a major essential component
    (kernel, window system, and so on) of the specific operating system
    (if any) on which the executable work runs, or a compiler used to
    produce the work, or an object code interpreter used to run it.

    可执行作品的“系统库”不是指作品本身,而是指(a)调用程序中的主要组件所使用的全部通常形式,不包括主要组件本身,和(b)用于使作品能和主要组件协同工作,或者用于使得公众在源代码形式上可用的标准接口。此处的“主要组件”指的是运行程序作品的特定操作系统(如有)的核心组件(内核、视窗系统等)、用来运行程序作品,或者生成程序的编译器、或者是运行目标代码的翻译器。

    The “Corresponding Source” for a work in object code form means all
    the source code needed to generate, install, and (for an executable
    work) run the object code and to modify the work, including scripts to
    control those activities. However, it does not include the work’s
    System Libraries, or general-purpose tools or generally available free
    programs which are used unmodified in performing those activities but
    which are not part of the work. For example, Corresponding Source
    includes interface definition files associated with source files for
    the work, and the source code for shared libraries and dynamically
    linked subprograms that the work is specifically designed to require,
    such as by intimate data communication or control flow between those
    subprograms and other parts of the work.

    以目标代码形式存在的作品,其“对应源代码”指所有生成、安装和运行(对可执行程序作品而言)目标代码程序、或修改程序作品所需要的全部原代码,也包括控制这些行为的脚本。然而,前述“对应源代码”并不包含作品的系统库,或者通用工具,或者那些用来实现前述行为的不属于作品一部分的、通常可以免费获得的、未经修改的其他程序。例如,对应源代码包括与作品源文件相关的接口定义文件,共享库的源代码和因程序特别设计需要而定义的动态链接子程序,因为有时,作品的其他部分和子程序需要频繁的数据交互或大量的数据流。

    The Corresponding Source need not include anything that users
    can regenerate automatically from other parts of the Corresponding
    Source.

    对应源代码不包括用户可以用对应源代码自动生成的任何内容。

    The Corresponding Source for a work in source code form is that
    same work.

    本身以源代码形式存在的作品和目标代码作品是同一作品。

    1. Basic Permissions.

    2.基本许可

    All rights granted under this License are granted for the term of
    copyright on the Program, and are irrevocable provided the stated
    conditions are met. This License explicitly affirms your unlimited
    permission to run the unmodified Program. The output from running a
    covered work is covered by this License only if the output, given its
    content, constitutes a covered work. This License acknowledges your
    rights of fair use or other equivalent, as provided by copyright law.

    本授权许可证授予的所有权利,在整个版权期限内均有效,并且在满足既定的要求时不可撤销。本许可证明确的授权你可以无限制的运行程序的原始版本。运行程序作品所得到的结果,只有在该结果的内容能够成为一个新作品的时候,才继续受本授权许可证管理。本授权许可承认你有合理使用(包括法律规定的其他类型的合理使用)的权利。

    You may make, run and propagate covered works that you do not
    convey, without conditions so long as your license otherwise remains
    in force. You may convey covered works to others for the sole purpose
    of having them make modifications exclusively for you, or provide you
    with facilities for running those works, provided that you comply with
    the terms of this License in conveying all material for which you do
    not control copyright. Those thus making or running the covered works
    for you must do so exclusively on your behalf, under your direction
    and control, on terms that prohibit them from making any copies of
    your copyrighted material outside their relationship with you.

    只要你的授权持续有效,你可以无条件地制作、运行和传播(不得传递)作品。在向第三方传递你不具有版权的作品时,必须按照本许可证的要求,只有在第三方单独只为你修改作品、或者提供运行设备的情况下,才能够传递给他。那些为你制作或者运行作品的第三方必须仅限于你的利益,在你的指示和控制下进行,并要禁止他们在超出和你的关系之外制作任何作品的复制件。

    Conveying under any other circumstances is permitted solely under
    the conditions stated below. Sublicensing is not allowed; section 10
    makes it unnecessary.

    其他情况下的传递行为只有在满足以下章节的规定时才可为。转让授权许可证是不允许的,第十节的规定也让这种行为变得没有必要。

    1. Protecting Users’ Legal Rights From Anti-Circumvention Law.

    3、防止反规避立法(译者注:规避行为主要指破解行为,为便于理解,阅读时尝试直接将“规避”一词替换成“破解”)影响用户的合法权利。

    No covered work shall be deemed part of an effective technological
    measure under any applicable law fulfilling obligations under article
    11 of the WIPO copyright treaty adopted on 20 December 1996, or
    similar laws prohibiting or restricting circumvention of such
    measures.

    为履行1996年12月20日世界知识产权组织条约第11章规定的义务,法律规定有禁止、限制规避措施的条款,本授权许可证项下的所有作品都不应当被视为采取了前述法律规定里指称的有效的反规避技术性措施。

    When you convey a covered work, you waive any legal power to forbid
    circumvention of technological measures to the extent such circumvention
    is effected by exercising rights under this License with respect to
    the covered work, and you disclaim any intention to limit operation or
    modification of the work as a means of enforcing, against the work’s
    users, your or third parties’ legal rights to forbid circumvention of
    technological measures.

    当你传递一个(本许可证项下)的受保护作品,即表明你放弃任何禁止技术规避措施法律所赋予的权利,行使本授权许可证授予的权利可以实现规避,同时,你放弃禁止技术规避措施相关法律赋予你的或者第三方的权利,不会针对作品的用户,限制其操作和修改作品。

    1. Conveying Verbatim Copies.

    4、传递原始(未经修改的)副本

    You may convey verbatim copies of the Program’s source code as you
    receive it, in any medium, provided that you conspicuously and
    appropriately publish on each copy an appropriate copyright notice;
    keep intact all notices stating that this License and any
    non-permissive terms added in accord with section 7 apply to the code;
    keep intact all notices of the absence of any warranty; and give all
    recipients a copy of this License along with the Program.

    如果满足:1、你显著地在每份副本里都附上恰当的版权声明;2、声明中,将“本许可证条款及添加的非许可条款(依照第7节)均适用于该代码”的内容保持完整;3、声明中,将“不含有任何担保”的内容保持完整;4、跟程序一起,给每一份接受者提供一份本授权许可证文本,则你可以通过各种媒体将程序未经修改的源代码进行传递。

    You may charge any price or no price for each copy that you convey,
    and you may offer support or warranty protection for a fee.

    你在传递时,可以就每一份副本收取任意费用,也可以免费提供,你也可以提供有偿的技术支持或担保。

    1. Conveying Modified Source Versions.

    5、传递修改后的源代码。

    You may convey a work based on the Program, or the modifications to
    produce it from the Program, in the form of source code under the
    terms of section 4, provided that you also meet all of these conditions:

    如果你同时也能够满足下面的全部条件,你可以传递一个基于原程序的修改程序(以第四节规定的源代码的形式),或者是修改内容自身源代码。

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    a)作品中必须明确声明被你修改过,并给出相关修改的日期。

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7. This requirement modifies the requirement in section 4 to
    “keep intact all notices”.

    b)作品中必须明确声明:作品依据本授权许可证及本授权许可证第7节规定的其他补充条件发行。此处的要求修改了第四节“保持完整全部声明”的要求。

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy. This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged. This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    c)你必须将全部作品作为一个整体,按照本授权许可证,授权给任何得到副本的人。无论作品怎样组织在一起,本授权许可证及按照本授权许可证第7节添加的条款均适用于全部作品。本授权许可证不允许对作品进行其他方式的授权许可,但是如果你个别的收到这样的许可,本授权许可证亦部不会致使其无效。

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

    d)如果作品有用户交互界面,每一类界面中都要显示恰当的法律声明,如果原程序的交互界面也没有显示,则你的作品不需要这么做。

    A compilation of a covered work with other separate and independent
    works, which are not by their nature extensions of the covered work,
    and which are not combined with it such as to form a larger program,
    in or on a volume of a storage or distribution medium, is called an
    “aggregate” if the compilation and its resulting copyright are not
    used to limit the access or legal rights of the compilation’s users
    beyond what the individual works permit. Inclusion of a covered work
    in an aggregate does not cause this License to apply to the other
    parts of the aggregate.

    本授权下的作品和其他与之分离的单独作品组成的一个汇编作品中,后者并非前者的自然延伸,与前者并未混合在一起,也未意图在某种存储或分发媒介上组成一个更大的程序,如果这种汇编作品及其版权限制,不影响单个作品的版权限制,则它被称为“软件集合包”,在“软件集合包”中含有本授权项下的作品, “软件集合包”中的其他软件作品不必适用本许可证。

    1. Conveying Non-Source Forms.

    6、传递非源代码形式的作品

    You may convey a covered work in object code form under the terms
    of sections 4 and 5, provided that you also convey the
    machine-readable Corresponding Source under the terms of this License,
    in one of these ways:

    在你依据本许可证第4节、第5节的规定,以目标代码的形式传递作品时,需要按照如下方式中的一种,传递机器可读的对应源代码。

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    a)以物理方式(包括物理媒介)传递或嵌入目标代码时,同时将对应源代码植入通常用于软件交换的耐久性物理媒介上。

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    b)以物理形式(包括物理发行媒介)传递或嵌入目标代码时,同时提供书面要约。要约有效期至少3年,有效期也应涵盖提供备用配件或者客户支持的周期,要约的内容是使得目标代码持有者(1)获得本许可证下全部作品对应源代码的副本,该副本应该记录在通常用于软件交换的耐久性物理媒介上,获取副本的成本需合理;或者(2)可从从网络服务器免费复制对应的源代码。

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source. This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    c)单独传递一个目标代码副本时,提供一份书面要约副本。此种方式只有在偶然而且非商业的情况下才可以使用,且目标代码副本和要约副本需要同时传递,要约内容也必须满足上述第6节b)款要求。

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge. You need not require recipients to copy the
    Corresponding Source along with the object code. If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source. Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    d)通过在指定位置提供权限的方式传递目标代码(收费或免费),并在相同的位置、以相同的方式提供获得对应源代码的同等权限(不可再次额外收费)。你不必要求接收方在复制目标代码的时候也一并复制源代码。如果是从一个网络服务器上复制目标代码,则你在目标代码旁清楚地标明对应源代码的可获取位置的情况下,对应源代码可以位于另一个不同的服务器上,该服务器应提供同等复制权限(由你或者其他第三方维护)。无论由哪个服务器存储对应源代码,你都有义务在保证,在有这种复制源代码的需求的时候,服务器能用。

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

    e) 用点对点传输方式传递目标代码,你需要告知其他的节点目标代码和对应的源代码的位置,并按照第6节d)款的要求向公众免费提供。

    A separable portion of the object code, whose source code is excluded
    from the Corresponding Source as a System Library, need not be
    included in conveying the object code work.

    目标代码中可分离的部分,其源代码属于系统库,因而从对应源代码中分离出来,在传递中,不需要包含这部分。

    A “User Product” is either (1) a “consumer product”, which means any
    tangible personal property which is normally used for personal, family,
    or household purposes, or (2) anything designed or sold for incorporation
    into a dwelling. In determining whether a product is a consumer product,
    doubtful cases shall be resolved in favor of coverage. For a particular
    product received by a particular user, “normally used” refers to a
    typical or common use of that class of product, regardless of the status
    of the particular user or of the way in which the particular user
    actually uses, or expects or is expected to use, the product. A product
    is a consumer product regardless of whether the product has substantial
    commercial, industrial or non-consumer uses, unless such uses represent
    the only significant mode of use of the product.

    用户产品是指(1)“消费者产品”,即任何通常用于个人、家庭、家居为目的有形个人财产,或者(2)任何以嵌入住宅为目的而设计或者销售的产品。在决定一个产品是否为消费者产品时候,存疑的案例应当考虑扩大范围(尽量包含进来)。对于特定接收人所接受的特定产品而言,“一般用途”是指该类产品常用的或者普遍的用途,而与其特定的使用人自身情况、特定的使用人实际使用情况、或者期望、或者期望的使用方式无关。无论产品是否已实质性地具备了商业化用途、工业化用途或者非消费者用途,只要这类用途没有变成产品的唯一主要应用场景,此产品就应当是消费者产品。

    “Installation Information” for a User Product means any methods,
    procedures, authorization keys, or other information required to install
    and execute modified versions of a covered work in that User Product from
    a modified version of its Corresponding Source. The information must
    suffice to ensure that the continued functioning of the modified object
    code is in no case prevented or interfered with solely because
    modification has been made.

    用户产品的安装信息是指由修改过的源代码生成的,用来安装和执行修改版作品的任何方法、步骤、授权秘钥、或者其他信息。安装信息应当足以保证修改后的目标代码不会仅仅因为存在修改,而使其持续运行能力受到限制或者干扰。

    If you convey an object code work under this section in, or with, or
    specifically for use in, a User Product, and the conveying occurs as
    part of a transaction in which the right of possession and use of the
    User Product is transferred to the recipient in perpetuity or for a
    fixed term (regardless of how the transaction is characterized), the
    Corresponding Source conveyed under this section must be accompanied
    by the Installation Information. But this requirement does not apply
    if neither you nor any third party retains the ability to install
    modified object code on the User Product (for example, the work has
    been installed in ROM).

    如果你按照本节的规定传递一个目标代码作品到用户产品中、或者与用户产品一起、或者为了特别用于用户产品的目的,传递行为成为交易行为的一部分,使得用户产品的所有权和使用权永久性的或者一定期限内转移到接收者名下(无论此项交易的特点如何),按照本节规定,传递对应源代码时应当提供安装信息。但是这种要求,在无论是你还是第三方都不具备在用户产品中安装修改版目标代码的能力时,无需遵守(例如,把作品安装到ROM上)。

    The requirement to provide Installation Information does not include a
    requirement to continue to provide support service, warranty, or updates
    for a work that has been modified or installed by the recipient, or for
    the User Product in which it has been modified or installed. Access to a
    network may be denied when the modification itself materially and
    adversely affects the operation of the network or violates the rules and
    protocols for communication across the network.

    提供安装信息并不要求:针对接收者修改或安装的软件作品,或者针对安装了修改软件产品的硬件产品,提供持续性的服务支持、保证、或者升级。当修改版的软件作品严重危害到网络运行或者违反网络传输协议规则时,可以禁止其联网。

    Corresponding Source conveyed, and Installation Information provided,
    in accord with this section must be in a format that is publicly
    documented (and with an implementation available to the public in
    source code form), and must require no special password or key for
    unpacking, reading or copying.

    根据本节规定,传递的对应源代码、安装信息必须采用公共文档格式(要求在源代码的形式上,实现对对公众可用),不能使用特殊的密码或秘钥来解压缩、阅读和复制。

    1. Additional Terms.

    7.补充条款

    “Additional permissions” are terms that supplement the terms of this
    License by making exceptions from one or more of its conditions.
    Additional permissions that are applicable to the entire Program shall
    be treated as though they were included in this License, to the extent
    that they are valid under applicable law. If additional permissions
    apply only to part of the Program, that part may be used separately
    under those permissions, but the entire Program remains governed by
    this License without regard to the additional permissions.

    补充许可条款是指对本授权许可证里的一项或者多项适用条件规定例外情形,来补充本授权许可证。只要补充许可条款在法律上有效,能够适用于整个程序的补充条款,应当被看做本授权许可证的一部分。如果补充许可条款只适用于程序的一部分,此部分则单独适用补充许可条款,整个程序仍然适用本授权许可证而不用考虑补充许可条款。

    When you convey a copy of a covered work, you may at your option
    remove any additional permissions from that copy, or from any part of
    it. (Additional permissions may be written to require their own
    removal in certain cases when you modify the work.) You may place
    additional permissions on material, added by you to a covered work,
    for which you have or can give appropriate copyright permission.

    当你传递一个作品的副本时,你可以自主选择从副本或者副本里的任何地方移除任何补充许可条款。(某些情况下,当你修改作品的时候,补充许可条款已经被写成要求将自身删除)对于那些你拥有或者可以授予合适版权的作品,你可以在作品的材料中添加补充许可条款。

    Notwithstanding any other provision of this License, for material you
    add to a covered work, you may (if authorized by the copyright holders of
    that material) supplement the terms of this License with terms:

    尽管本授权许可证还有其他条款,然而针对你添加到受作品里面的材料(如果得到了材料版权所有者的授权),你可使用以下类型的补充条款。

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    a)以与本授权许可证第15节、第16节不同的方式,拒绝担保或者缩限责任,或

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    b)要求保留特定的、合理的法律声明,或者要求保留署名权(前述材料里已有的署名、或者作品里法律声明中的署名)。

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    c)禁止对前述材料的来源进行错误陈述,若对相关材料进行修改,要求进行合理标注以示和原始版本存在不同;或

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    d)限制对作者或者权利人的姓名进行公开使用;或

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    e)拒绝授权商号、商标或者服务标识的使用(商标法下)。

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

    f)当他人在传递作品(或者其修改版)时,若对接收者提供合同契约式的责任假设,则此人需要对授权人和作者承担赔偿责任,因为任何合同契约式的责任假设,都将造成这些授权人和作者直接承担责任。

    All other non-permissive additional terms are considered “further
    restrictions” within the meaning of section 10. If the Program as you
    received it, or any part of it, contains a notice stating that it is
    governed by this License along with a term that is a further
    restriction, you may remove that term. If a license document contains
    a further restriction but permits relicensing or conveying under this
    License, you may add to a covered work material governed by the terms
    of that license document, provided that the further restriction does
    not survive such relicensing or conveying.

    所有其他类型的非许可性补充条款,都属于第10节中的“进一步限制”。如果你接收到的程序或者程序的一部分,含有一份由本授权许可证管理的声明,声明中又包含进一步限制的条款,你可以去除该进一步限制的条款。如果一份授权文书中包含进一步限制条款,但是容许根据本授权许可证进行具再授权和传递,你仍然可以选择适用此授权文书,并在受保护作品里面添加其他作品,只要此进一步限制条款针对再授权和传递行为并不生效。

    If you add terms to a covered work in accord with this section, you
    must place, in the relevant source files, a statement of the
    additional terms that apply to those files, or a notice indicating
    where to find the applicable terms.

    如果你根据本节在作品中添加条款,你必须在相关源代码文件内放置相关补充条款声明,或者一份指明那里可以找到这些补充条款的提示。

    Additional terms, permissive or non-permissive, may be stated in the
    form of a separately written license, or stated as exceptions;
    the above requirements apply either way.

    补充条款,许可性的或者非许可性的,可以规定在单独的书面许可证里,也可以以例外陈述的形式存在,两种方式均符合要求。

    1. Termination.

    8.终止

    You may not propagate or modify a covered work except as expressly
    provided under this License. Any attempt otherwise to propagate or
    modify it is void, and will automatically terminate your rights under
    this License (including any patent licenses granted under the third
    paragraph of section 11).

    你不得传播或修改受保护作品,除非获得了本授权许可证的明确授权。所有传播或者修改受保护作品的尝试都是无效的,并会自动终止你根据本授权许可证取得的全部权利(包括根据第11节第三段授予的任何专利许可授权)

    However, if you cease all violation of this License, then your
    license from a particular copyright holder is reinstated (a)
    provisionally, unless and until the copyright holder explicitly and
    finally terminates your license, and (b) permanently, if the copyright
    holder fails to notify you of the violation by some reasonable means
    prior to 60 days after the cessation.

    然而,如果你停止了违反本授权许可证的行为,你从特定版权持有人处获得的授权许可就会得到临时性或永久性的恢复:(a)临时性恢复,除非并直到版权所有人明确并终局性的终止了你的授权(译者注:版权所有人明确并终局性的终止授权之前,你仍可临时性拥有授权),(b)永久性恢复,如果版权所有人没能在你停止违反行为之日后60日内以合理方式将违反情形通知你。

    Moreover, your license from a particular copyright holder is
    reinstated permanently if the copyright holder notifies you of the
    violation by some reasonable means, this is the first time you have
    received notice of violation of this License (for any work) from that
    copyright holder, and you cure the violation prior to 30 days after
    your receipt of the notice.

    然而,如果版权所有人以合理的方式将违反本授权许可证的行为通知你,这是你第一次从版权所有人处接到违反本授权许可证的通知(无论针对哪个作品),如果你在收到通知后30天内修正了违反行为,你从特定版权所有人处获得的授权即得到永久性恢复。

    Termination of your rights under this section does not terminate the
    licenses of parties who have received copies or rights from you under
    this License. If your rights have been terminated and not permanently
    reinstated, you do not qualify to receive new licenses for the same
    material under section 10.

    按照本节的规定,终止你的权利,并不会终止那些从你那里按照本授权许可协议取得副本和授权的其他人的授权许可证也随之终止。如果你的权利被终止了,并且没有得到永久性恢复,你并没有资格依据第10节的规定针对相同的作品获得新的授权许可证。

    1. Acceptance Not Required for Having Copies.

    9.持有副本无需同意(本授权)

    You are not required to accept this License in order to receive or
    run a copy of the Program. Ancillary propagation of a covered work
    occurring solely as a consequence of using peer-to-peer transmission
    to receive a copy likewise does not require acceptance. However,
    nothing other than this License grants you permission to propagate or
    modify any covered work. These actions infringe copyright if you do
    not accept this License. Therefore, by modifying or propagating a
    covered work, you indicate your acceptance of this License to do so.

    你仅仅接收、运行一个程序副本,不需要同意本授权许可证。使用点对点传输技术对原始文件进行的附带性传播并不需要同意本授权许可证。然而,只有本授权许可证准许你传播和修改受保护作品。如果你不同意本授权,前述行为就侵犯了著作权。因此,你修改和传播一个受保护作品,即表明你同意本授权许可证。

    1. Automatic Licensing of Downstream Recipients.

    10.自动对下游接受者授权

    Each time you convey a covered work, the recipient automatically
    receives a license from the original licensors, to run, modify and
    propagate that work, subject to this License. You are not responsible
    for enforcing compliance by third parties with this License.

    你每次一传递一个受保护作品,接收者都自动获得了来自原始许可人的授权许可,并可根据授权许可运行、修改或传播该作品。你对第三方是否遵守本授权许可证不承担责任。

    An “entity transaction” is a transaction transferring control of an
    organization, or substantially all assets of one, or subdividing an
    organization, or merging organizations. If propagation of a covered
    work results from an entity transaction, each party to that
    transaction who receives a copy of the work also receives whatever
    licenses to the work the party’s predecessor in interest had or could
    give under the previous paragraph, plus a right to possession of the
    Corresponding Source of the work from the predecessor in interest, if
    the predecessor has it or can get it with reasonable efforts.

    “实体交易”是指一种转移组织控制权,或者全部财产权利,或者拆分组织,或者合并几个组织。如果传播受作品是由一项实体交易产生的,每一个收到作品副本的交易参与者,将同样接收到来自其前任(已有的或者按照前述段落所述可以授予的)针对作品的全部授权文件,如果其前任可以合法拥有作品的对应源代码,或者通过合理限度的努力可以取得对应源代码,则参与者亦有权获取此对应源代码。

    You may not impose any further restrictions on the exercise of the
    rights granted or affirmed under this License. For example, you may
    not impose a license fee, royalty, or other charge for exercise of
    rights granted under this License, and you may not initiate litigation
    (including a cross-claim or counterclaim in a lawsuit) alleging that
    any patent claim is infringed by making, using, selling, offering for
    sale, or importing the Program or any portion of it.

    你不能对实施本授权许可证授予或者确认的权利,施加以任何进一步的限制。例如,你不能对实施本授权许可证所授予的的权利,强制收取许可证使用费、版权费或者其他费用,并且不能对制作、使用、销售、推销、引进整个程序或者部分程序的行为,以侵犯专利为由,发起诉讼程序(包括交叉诉讼和反诉)

    1. Patents.

    11.专利

    A “contributor” is a copyright holder who authorizes use under this
    License of the Program or a work on which the Program is based. The
    work thus licensed is called the contributor’s “contributor version”.

    “贡献者”指授予作品使用权的人、或者授予衍生作品版权的人。授权作品被称为贡献者的“贡献者版本”。

    A contributor’s “essential patent claims” are all patent claims
    owned or controlled by the contributor, whether already acquired or
    hereafter acquired, that would be infringed by some manner, permitted
    by this License, of making, using, or selling its contributor version,
    but do not include claims that would be infringed only as a
    consequence of further modification of the contributor version. For
    purposes of this definition, “control” includes the right to grant
    patent sublicenses in a manner consistent with the requirements of
    this License.

    贡献者的“必要专利声明”涵盖了贡献者所享有的或者控制的全部专利(无论是已经获得或者即将获得的),本授权许可证允许的部分行为,如制作、使用、销售贡献者版本将对该“必要专利声明”构成侵权,但“对贡献者版本进行修改即构成侵权”不包括在声明内。根据该定义的目的,“控制”一词包括有权进行专利转授权。

    Each contributor grants you a non-exclusive, worldwide, royalty-free
    patent license under the contributor’s essential patent claims, to
    make, use, sell, offer for sale, import and otherwise run, modify and
    propagate the contents of its contributor version.

    每一个贡献都依据此“必要专利声明”,授予你非排他的,世界范围的、无版权费的专利授权许可,你有权制作、使用、销售、推销、引进以及运行、修改和传播贡献者版本。

    In the following three paragraphs, a “patent license” is any express
    agreement or commitment, however denominated, not to enforce a patent
    (such as an express permission to practice a patent or covenant not to
    sue for patent infringement). To “grant” such a patent license to a
    party means to make such an agreement or commitment not to enforce a
    patent against the party.

    在此后的三个段落中,“专利许可证”指任何不实施专利的明确协议或承诺,无论其名字如何(例如,使用专利权的明确许可、放弃就侵犯专利进行诉讼的明确协议)。“授权”专利许可证给一方,即表明达成一项不对专利强制进行要求的协议或承诺。

    If you convey a covered work, knowingly relying on a patent license,
    and the Corresponding Source of the work is not available for anyone
    to copy, free of charge and under the terms of this License, through a
    publicly available network server or other readily accessible means,
    then you must either (1) cause the Corresponding Source to be so
    available, or (2) arrange to deprive yourself of the benefit of the
    patent license for this particular work, or (3) arrange, in a manner
    consistent with the requirements of this License, to extend the patent
    license to downstream recipients. “Knowingly relying” means you have
    actual knowledge that, but for the patent license, your conveying the
    covered work in a country, or your recipient’s use of the covered work
    in a country, would infringe one or more identifiable patents in that
    country that you have reason to believe are valid.

    如果你“明知需要”专利许可证才能传递受保护作品,且作品的源代码尚未向公众开放,在按照本授权通过公共网络服务或者其他方式也不容易免费获取,你必须选择(1)开放对应源代码;或(2)放弃已享有的专利授权,或(3)按本授权的要求,将专利授权许可延伸到下游的接收者。“明知需要”指你明确的知道,在没有专利许可的情况下,在某些国家你传递一个受保护作品或者你的接收者使用此受保护作品,将会侵犯一项或者多项在该国明显存在的专利,而且你也有理由知道这些专利是有效的。

    If, pursuant to or in connection with a single transaction or
    arrangement, you convey, or propagate by procuring conveyance of, a
    covered work, and grant a patent license to some of the parties
    receiving the covered work authorizing them to use, propagate, modify
    or convey a specific copy of the covered work, then the patent license
    you grant is automatically extended to all recipients of the covered
    work and works based on it.

    如果在一个单独的交易或者相关安排中,你传递或者传播(通过取得传递授权的方式)一个受保护作品,并授予部分接收方专利许可,授权他们使用、复制、修改或者传递某份受保护作品的一个特殊副本,那么你授权的专利许可,将自动延伸至此受保护作品或者衍生作品的所有接收方手中。

    A patent license is “discriminatory” if it does not include within
    the scope of its coverage, prohibits the exercise of, or is
    conditioned on the non-exercise of one or more of the rights that are
    specifically granted under this License. You may not convey a covered
    work if you are a party to an arrangement with a third party that is
    in the business of distributing software, under which you make payment
    to the third party based on the extent of your activity of conveying
    the work, and under which the third party grants, to any of the
    parties who would receive the covered work from you, a discriminatory
    patent license (a) in connection with copies of the covered work
    conveyed by you (or copies made from those copies), or (b) primarily
    for and in connection with specific products or compilations that
    contain the covered work, unless you entered into that arrangement,
    or that patent license was granted, prior to 28 March 2007.

    一项专利许可证如果不包含:禁止行使(专利权)或者有条件的禁止(针对)本授权许可证特别授予的一项或者多项权利(来行使专利权),则该专利许可证是“歧视性的”。如果你是软件商业发行协议中的一方,则你不得传递受保护作品,因为该商业发行协议规定你按照发行量向第三方支付价款,而第三方则向他人授予的是一份歧视性的专利许可,许可范围(a)限于你传递的副本(或者此副本的副本),或者(b)限于特殊产品或者含有受保护作品的软件集合包,除非你签订了此协议或者此种专利授权许可是在2007年3月28日之前作出的。

    Nothing in this License shall be construed as excluding or limiting
    any implied license or other defenses to infringement that may
    otherwise be available to you under applicable patent law.

    本授权许可证的全部条款,都不应当被理解成排除或者限制你适用专利权法律中的默认部分,或针对侵权采取防御措施。

    1. No Surrender of Others’ Freedom.

    12.保护他人的自由

    If conditions are imposed on you (whether by court order, agreement or
    otherwise) that contradict the conditions of this License, they do not
    excuse you from the conditions of this License. If you cannot convey a
    covered work so as to satisfy simultaneously your obligations under this
    License and any other pertinent obligations, then as a consequence you may
    not convey it at all. For example, if you agree to terms that obligate you
    to collect a royalty for further conveying from those to whom you convey
    the Program, the only way you could satisfy both those terms and this
    License would be to refrain entirely from conveying the Program.

    如果现实情况与本授权许可证相冲突(无论是法院命令、协议或者其他),你不得以此为由豁免本授权许可证的义务。如果你不能一并同时履行本授权许可证项下的义务及其他相关义务,则不得传递相关受保护作品。例如,如果你承担某种合同义务,合同条款要求你向接收方再次传递作品的行为收取版权费用,唯一让你满足此条款义务和本授权许可证义务的做法只有完全停止传递该程序。

    1. Use with the GNU Affero General Public License.

    13.与GNU Affero通用公共许可证同时使用

    Notwithstanding any other provision of this License, you have
    permission to link or combine any covered work with a work licensed
    under version 3 of the GNU Affero General Public License into a single
    combined work, and to convey the resulting work. The terms of this
    License will continue to apply to the part which is the covered work,
    but the special requirements of the GNU Affero General Public License,
    section 13, concerning interaction through a network will apply to the
    combination as such.

    尽管本授权许可证有众多条款,你可以将任何一个受保护作品和一个适用GNU Affero通用许可证的作品连接或者结合成一个合成作品,并传递该合成作品。本授权许可证条款将对所辖的作品这一部分继续有效,但是GNU Affero通用许可证里的特别要求,即第13节关于网络交互的部分将适用于整个合成作品。

    1. Revised Versions of this License.

    14.本授权许可证的修改版本

    The Free Software Foundation may publish revised and/or new versions of
    the GNU General Public License from time to time. Such new versions will
    be similar in spirit to the present version, but may differ in detail to
    address new problems or concerns.

    自由软件基金会可能会不定期对GNU通用授权许可证进行修改和/或更新。新版本将在精神上与当前版本类似,但是可能细节之处有所不同,以期解决和关注新的问题。

    Each version is given a distinguishing version number. If the
    Program specifies that a certain numbered version of the GNU General
    Public License “or any later version” applies to it, you have the
    option of following the terms and conditions either of that numbered
    version or of any later version published by the Free Software
    Foundation. If the Program does not specify a version number of the
    GNU General Public License, you may choose any version ever published
    by the Free Software Foundation.

    每一个版本都有一个独立的版本号。如果程序作品标明适用一个特定版本的GNU通用公共授权许可证,同时也标明任何其他后续版本也同样适用,则你可以在适用的版本中自由选择,既可以适用标明特定版本号的版本,也可以选择由自由软件基金会之后发布的后续版本。如果程序没有特别标明版本号,则你可以选择适用任何一个由自由软件基金会发布的版本。

    If the Program specifies that a proxy can decide which future
    versions of the GNU General Public License can be used, that proxy’s
    public statement of acceptance of a version permanently authorizes you
    to choose that version for the Program.

    如果程序中标明程序的代理人可以决定适用未来的哪一个版本的GNU通用公共授权许可证,则代理人公开声明的版本,将永久性的授权给你使用。

    Later license versions may give you additional or different
    permissions. However, no additional obligations are imposed on any
    author or copyright holder as a result of your choosing to follow a
    later version.

    后续版本可能有补充性的或者不同的许可,但是,你对后续版本的选择,并不会增加作者或者版权所有人的额外责任。

    1. Disclaimer of Warranty.

    15.免担保声明

    THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
    APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
    HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY
    OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
    THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
    IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
    ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

    在相关法律的许可范围内,本程序不提供任何担保。除非有书面声明,版权所有者和/或其他提供程序的第三方,“概”不提供任何形式的担保,无论是明示的还是默认的,包括但不限于从商用和健康角度出发的的默认担保。程序的全部质量、性能风险由您承担。程序出现瑕疵时,你自行承担全部的服务、修理或修正费用。

    1. Limitation of Liability.

    16.责任限制

    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
    THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
    GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
    USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
    DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
    PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
    EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGES.

    除非相关法律要求或者以书面形式达成一致,版权持有人或者依据前述规定修改和/或传递程序的其他人,不必为你因为使用或者不能使用本程序所造成的一般性、特殊性、意外性或者间接性损害承担责任(包括但不限于数据损失,数据执行不准确、你和第三方的损失及程序与其他程序不兼容的损失),即便版权持有人或者其他人已经被提示过前述损失可能存在。

    1. Interpretation of Sections 15 and 16.

    17.第15节和第16节的解释

    If the disclaimer of warranty and limitation of liability provided
    above cannot be given local legal effect according to their terms,
    reviewing courts shall apply local law that most closely approximates
    an absolute waiver of all civil liability in connection with the
    Program, unless a warranty or assumption of liability accompanies a
    copy of the Program in return for a fee.

    如果前述拒绝担保和责任限制的条款不能被当地法律支持,受理法院应当以最接近放弃一切与本程序有关的民事责任的方式,来适用当地法律,除非程序是以收费的方式,连同保证书或责任书一起发行的的。

    END OF TERMS AND CONDITIONS.

    条款和适用条件结束

    How to Apply These Terms to Your New Programs.

    如何在你的新程序中应用这些条款

    If you develop a new program, and you want it to be of the greatest
    possible use to the public, the best way to achieve this is to make it
    free software which everyone can redistribute and change under these terms.

    如果你开发了一个新程序,而你又希望公众最大化使用它,最好的方式是让它成为自由软件,那么任何人都可以对程序进行再次分发和修改。

    To do so, attach the following notices to the program. It is safest
    to attach them to the start of each source file to most effectively
    state the exclusion of warranty; and each file should have at least
    the “copyright” line and a pointer to where the full notice is found.

    为了做到这一单,将如下声明附在程序里。最有效的排除担保的做法,是将它们放置在每份源文件的开始位置,这样最稳健。每一个文件至少需要一列“版权”行,并标明哪里可以找到完整的声明。

    <one line to give the program’s name and a brief idea of what it does.>
    Copyright ©

    <一行给程序取个名字,写一个程序用途的简介>
    版权所有(c)<年> <作者姓名>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    本程序为自由软件,你可以依据GNU通用授权许可证的条款(自由软件基金会公布的第三版或者是你自己选择的其他后续版本),再分发和/或修改它,

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    该程序以具有实际用途为目的发布,但是并不包含任何担保,也不包基于特定商用或健康用途的默认担保。具体细节请查看GNU通用授权许可证

    You should have received a copy of the GNU General Public License
    along with this program. If not, see http://www.gnu.org/licenses/.

    你在收到本程序的同时,也应当收到一份GNU通用公共授权许可证副本,如果没有,请查看http://www.gnu.org/licenses/.

    Also add information on how to contact you by electronic and paper mail.

    同时添加你的书信或者电子邮箱地址。

    If the program does terminal interaction, make it output a short
    notice like this when it starts in an interactive mode:

    如果程序可以进行终端交互,使其在开始交互时,输出一份类似的声明。

    Copyright © This program comes with ABSOLUTELY NO WARRANTY; for details type ‘show w’.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c’ for details.

    <程序>版权所有©<年><作者姓名>
    本程序不含任何担保,查看细节输入“show w’
    本程序为自由软件,你可以根据特定条件再分发,输入`show c’查看细节

    The hypothetical commands ‘show w’ and `show c’ should show the appropriate
    parts of the General Public License. Of course, your program’s commands
    might be different; for a GUI interface, you would use an “about box”.

    设定的指令show w’ 和 `show c’应当显示公共通用授权许可证的对应部分。当然,你的程序指令可能有所差异,对于GUI界面而言,你可以使用“对话框”。

    You should also get your employer (if you work as a programmer) or school,
    if any, to sign a “copyright disclaimer” for the program, if necessary.
    For more information on this, and how to apply and follow the GNU GPL, see
    http://www.gnu.org/licenses/.

    如果有必要,你应当联系你的雇主(如果你的工作是一名程序员)或者学校等等,来为本程序签订一份“版权放弃书”。更多信息,以及如何适用及遵循GNU通用公共授权许可证,查看http://www.gnu.org/licenses/

    The GNU General Public License does not permit incorporating your program
    into proprietary programs. If your program is a subroutine library, you
    may consider it more useful to permit linking proprietary applications with
    the library. If this is what you want to do, use the GNU Lesser General
    Public License instead of this License. But first, please read
    http://www.gnu.org/philosophy/why-not-lgpl.html.

    GNU通用公共授权许可证并不允许将你的程序合并到私有程序中。如果你的程序是一个子程序库,你可以考虑允许私有应用程序链接该库,可能更为实用。如果你希望这么做,请使用GNU宽松通用公共授权许可协议替代本协议。但是首先,请阅读http://www.gnu.org/philosophy/why-not-lgpl.html

    展开全文
  • leetcode中文版我开始关注这个 repo 来练习编码。 我的练习文件在 leetcode 文件夹中 编程面试大学 我最初创建它是为了成为一名软件工程师的学习主题的简短待办事项列表,但它增长到您今天看到的大列表。 完成这个...
  • MapReduce论文中文翻译

    千次阅读 多人点赞 2015-03-18 11:34:45
    MapReduce论文中文翻译

    原文地址:
    http://labs.google.com/papers/mapreduce.html

    译者: alex

    摘要

    MapReduce是一个编程模型,也是一个处理和生成超大数据集的算法模型的相关实现。用户首先创建一个Map函数处理一个基于key/value pair的数据集合,输出中间的基于key/value pair的数据集合;然后再创建一个Reduce函数用来合并所有的具有相同中间key值的中间value值。现实世界中有很多满足上述处理模型的例子,本论文将详细描述这个模型。

    MapReduce架构的程序能够在大量的普通配置的计算机上实现并行化处理。这个系统在运行时只关心:如何分割输入数据,在大量计算机组成的集群上的调度,集群中计算机的错误处理,管理集群中计算机之间必要的通信。采用MapReduce架构可以使那些没有并行计算和分布式处理系统开发经验的程序员有效利用分布式系统的丰富资源。

    我们的MapReduce实现运行在规模可以灵活调整的由普通机器组成的集群上:一个典型的MapReduce计算往往由几千台机器组成、处理以TB计算的数据。程序员发现这个系统非常好用:已经实现了数以百计的MapReduce程序,在Google的集群上,每天都有1000多个MapReduce程序在执行。

    介绍

    在过去的5年里,包括本文作者在内的Google的很多程序员,为了处理海量的原始数据,已经实现了数以百计的、专用的计算方法。这些计算方法用来处理大量的原始数据,比如,文档抓取(类似网络爬虫的程序)、Web请求日志等等;也为了计算处理各种类型的衍生数据,比如倒排索引、Web文档的图结构的各种表示形势、每台主机上网络爬虫抓取的页面数量的汇总、每天被请求的最多的查询的集合等等。大多数这样的数据处理运算在概念上很容易理解。然而由于输入的数据量巨大,因此要想在可接受的时间内完成运算,只有将这些计算分布在成百上千的主机上。如何处理并行计算、如何分发数据、如何处理错误?所有这些问题综合在一起,需要大量的代码处理,因此也使得原本简单的运算变得难以处理。

    为了解决上述复杂的问题,我们设计一个新的抽象模型,使用这个抽象模型,我们只要表述我们想要执行的简单运算即可,而不必关心并行计算、容错、数据分布、负载均衡等复杂的细节,这些问题都被封装在了一个库里面。设计这个抽象模型的灵感来自Lisp和许多其他函数式语言的Map和Reduce的原语。我们意识到我们大多数的运算都包含这样的操作:在输入数据的“逻辑”记录上应用Map操作得出一个中间key/value pair集合,然后在所有具有相同key值的value值上应用Reduce操作,从而达到合并中间的数据,得到一个想要的结果的目的。使用MapReduce模型,再结合用户实现的Map和Reduce函数,我们就可以非常容易的实现大规模并行化计算;通过MapReduce模型自带的“再次执行”(re-execution)功能,也提供了初级的容灾实现方案。

    这个工作(实现一个MapReduce框架模型)的主要贡献是通过简单的接口来实现自动的并行化和大规模的分布式计算,通过使用MapReduce模型接口实现在大量普通的PC机上高性能计算。

    第二部分描述基本的编程模型和一些使用案例。第三部分描述了一个经过裁剪的、适合我们的基于集群的计算环境的MapReduce实现。第四部分描述我们认为在MapReduce编程模型中一些实用的技巧。第五部分对于各种不同的任务,测量我们MapReduce实现的性能。第六部分揭示了在Google内部如何使用MapReduce作为基础重写我们的索引系统产品,包括其它一些使用MapReduce的经验。第七部分讨论相关的和未来的工作。

    编程模型

    MapReduce编程模型的原理是:利用一个输入key/value pair集合来产生一个输出的key/value pair集合。MapReduce库的用户用两个函数表达这个计算:Map和Reduce。

    用户自定义的Map函数接受一个输入的key/value pair值,然后产生一个中间key/value pair值的集合。MapReduce库把所有具有相同中间key值I的中间value值集合在一起后传递给reduce函数。

    用户自定义的Reduce函数接受一个中间key的值I和相关的一个value值的集合。Reduce函数合并这些value值,形成一个较小的value值的集合。一般的,每次Reduce函数调用只产生0或1个输出value值。通常我们通过一个迭代器把中间value值提供给Reduce函数,这样我们就可以处理无法全部放入内存中的大量的value值的集合。

    例子

    例如,计算一个大的文档集合中每个单词出现的次数,下面是伪代码段:

    map(String key, String value):
        // key: document name
        // value: document contents
        for each word w in value:
            EmitIntermediate(w, “1″);
    reduce(String key, Iterator values):
        // key: a word
        // values: a list of counts
        int result = 0;
        for each v in values:
            result += ParseInt(v);
        Emit(AsString(result));

    Map函数输出文档中的每个词、以及这个词的出现次数(在这个简单的例子里就是1)。Reduce函数把Map函数产生的每一个特定的词的计数累加起来。

    另外,用户编写代码,使用输入和输出文件的名字、可选的调节参数来完成一个符合MapReduce模型规范的对象,然后调用MapReduce函数,并把这个规范对象传递给它。用户的代码和MapReduce库链接在一起(用C++实现)。附录A包含了这个实例的全部程序代码。

    类型

    尽管在前面例子的伪代码中使用了以字符串表示的输入输出值,但是在概念上,用户定义的Map和Reduce函数都有相关联的类型:

    map(k1,v1) ->list(k2,v2)
    reduce(k2,list(v2)) ->list(v2)

    比如,输入的key和value值与输出的key和value值在类型上推导的域不同。此外,中间key和value值与输出key和value值在类型上推导的域相同。
    (alex注:原文中这个domain的含义不是很清楚,我参考Hadoop、KFS等实现,map和reduce都使用了泛型,因此,我把domain翻译成类型推导的域)。
    我们的C++中使用字符串类型作为用户自定义函数的输入输出,用户在自己的代码中对字符串进行适当的类型转换。

    更多的例子

    这里还有一些有趣的简单例子,可以很容易的使用MapReduce模型来表示:

    • 分布式的Grep:Map函数输出匹配某个模式的一行,Reduce函数是一个恒等函数,即把中间数据复制到输出。
    • 计算URL访问频率:Map函数处理日志中web页面请求的记录,然后输出(URL,1)。Reduce函数把相同URL的value值都累加起来,产生(URL,记录总数)结果。
    • 倒转网络链接图:Map函数在源页面(source)中搜索所有的链接目标(target)并输出为(target,source)。Reduce函数把给定链接目标(target)的链接组合成一个列表,输出(target,list(source))。
    • 每个主机的检索词向量:检索词向量用一个(词,频率)列表来概述出现在文档或文档集中的最重要的一些词。Map函数为每一个输入文档输出(主机名,检索词向量),其中主机名来自文档的URL。Reduce函数接收给定主机的所有文档的检索词向量,并把这些检索词向量加在一起,丢弃掉低频的检索词,输出一个最终的(主机名,检索词向量)。
    • 倒排索引:Map函数分析每个文档输出一个(词,文档号)的列表,Reduce函数的输入是一个给定词的所有(词,文档号),排序所有的文档号,输出(词,list(文档号))。所有的输出集合形成一个简单的倒排索引,它以一种简单的算法跟踪词在文档中的位置。
    • 分布式排序:Map函数从每个记录提取key,输出(key,record)。Reduce函数不改变任何的值。这个运算依赖分区机制(在4.1描述)和排序属性(在4.2描述)。

    实现

    MapReduce模型可以有多种不同的实现方式。如何正确选择取决于具体的环境。例如,一种实现方式适用于小型的共享内存方式的机器,另外一种实现方式则适用于大型NUMA架构的多处理器的主机,而有的实现方式更适合大型的网络连接集群。
    本章节描述一个适用于Google内部广泛使用的运算环境的实现:用以太网交换机连接、由普通PC机组成的大型集群。在我们的环境里包括:

    • x86架构、运行Linux操作系统、双处理器、2-4GB内存的机器。
    • 普通的网络硬件设备,每个机器的带宽为百兆或者千兆,但是远小于网络的平均带宽的一半。 (alex注:这里需要网络专家解释一下了)
    • 集群中包含成百上千的机器,因此,机器故障是常态。
    • 存储为廉价的内置IDE硬盘。一个内部分布式文件系统用来管理存储在这些磁盘上的数据。文件系统通过数据复制来在不可靠的硬件上保证数据的可靠性和有效性。
    • 用户提交工作(job)给调度系统。每个工作(job)都包含一系列的任务(task),调度系统将这些任务调度到集群中多台可用的机器上。

    执行概括

    通过将Map调用的输入数据自动分割为M个数据片段的集合,Map调用被分布到多台机器上执行。输入的数据片段能够在不同的机器上并行处理。使用分区函数将Map调用产生的中间key值分成R个不同分区(例如,hash(key) mod R),Reduce调用也被分布到多台机器上执行。分区数量(R)和分区函数由用户来指定。
    这里写图片描述
    图1展示了我们的MapReduce实现中操作的全部流程。当用户调用MapReduce函数时,将发生下面的一系列动作(下面的序号和图1中的序号一一对应):

    • 用户程序首先调用的MapReduce库将输入文件分成M个数据片度,每个数据片段的大小一般从 16MB到64MB(可以通过可选的参数来控制每个数据片段的大小)。然后用户程序在机群中创建大量的程序副本。 (alex:copies of the program还真难翻译)
    • 这些程序副本中的有一个特殊的程序–master。副本中其它的程序都是worker程序,由master分配任务。有M个Map任务和R个Reduce任务将被分配,master将一个Map任务或Reduce任务分配给一个空闲的worker。
    • 被分配了map任务的worker程序读取相关的输入数据片段,从输入的数据片段中解析出key/value pair,然后把key/value pair传递给用户自定义的Map函数,由Map函数生成并输出的中间key/value pair,并缓存在内存中。
    • 缓存中的key/value pair通过分区函数分成R个区域,之后周期性的写入到本地磁盘上。缓存的key/value pair在本地磁盘上的存储位置将被回传给master,由master负责把这些存储位置再传送给Reduce worker。
    • 当Reduce worker程序接收到master程序发来的数据存储位置信息后,使用RPC从Map worker所在主机的磁盘上读取这些缓存数据。当Reduce worker读取了所有的中间数据后,通过对key进行排序后使得具有相同key值的数据聚合在一起。由于许多不同的key值会映射到相同的Reduce任务上,因此必须进行排序。如果中间数据太大无法在内存中完成排序,那么就要在外部进行排序。
    • Reduce worker程序遍历排序后的中间数据,对于每一个唯一的中间key值,Reduce worker程序将这个key值和它相关的中间value值的集合传递给用户自定义的Reduce函数。Reduce函数的输出被追加到所属分区的输出文件。
    • 当所有的Map和Reduce任务都完成之后,master唤醒用户程序。在这个时候,在用户程序里的对MapReduce调用才返回。

    在成功完成任务之后,MapReduce的输出存放在R个输出文件中(对应每个Reduce任务产生一个输出文件,文件名由用户指定)。一般情况下,用户不需要将这R个输出文件合并成一个文件–他们经常把这些文件作为另外一个MapReduce的输入,或者在另外一个可以处理多个分割文件的分布式应用中使用。

    Master数据结构

    Master持有一些数据结构,它存储每一个Map和Reduce任务的状态(空闲、工作中或完成),以及Worker机器(非空闲任务的机器)的标识。

    Master就像一个数据管道,中间文件存储区域的位置信息通过这个管道从Map传递到Reduce。因此,对于每个已经完成的Map任务,master存储了Map任务产生的R个中间文件存储区域的大小和位置。当Map任务完成时,Master接收到位置和大小的更新信息,这些信息被逐步递增的推送给那些正在工作的Reduce任务。

    容错

    因为MapReduce库的设计初衷是使用由成百上千的机器组成的集群来处理超大规模的数据,所以,这个库必须要能很好的处理机器故障。

    worker故障

    master周期性的ping每个worker。如果在一个约定的时间范围内没有收到worker返回的信息,master将把这个worker标记为失效。所有由这个失效的worker完成的Map任务被重设为初始的空闲状态,之后这些任务就可以被安排给其他的worker。同样的,worker失效时正在运行的Map或Reduce任务也将被重新置为空闲状态,等待重新调度。

    当worker故障时,由于已经完成的Map任务的输出存储在这台机器上,Map任务的输出已不可访问了,因此必须重新执行。而已经完成的Reduce任务的输出存储在全局文件系统上,因此不需要再次执行。

    当一个Map任务首先被worker A执行,之后由于worker A失效了又被调度到worker B执行,这个“重新执行”的动作会被通知给所有执行Reduce任务的worker。任何还没有从worker A读取数据的Reduce任务将从worker B读取数据。

    MapReduce可以处理大规模worker失效的情况。比如,在一个MapReduce操作执行期间,在正在运行的集群上进行网络维护引起80台机器在几分钟内不可访问了,MapReduce master只需要简单的再次执行那些不可访问的worker完成的工作,之后继续执行未完成的任务,直到最终完成这个MapReduce操作。

    master失败

    一个简单的解决办法是让master周期性的将上面描述的数据结构(alex注:指3.2节)的写入磁盘,即检查点(checkpoint)。如果这个master任务失效了,可以从最后一个检查点(checkpoint)开始启动另一个master进程。然而,由于只有一个master进程,master失效后再恢复是比较麻烦的,因此我们现在的实现是如果master失效,就中止MapReduce运算。客户可以检查到这个状态,并且可以根据需要重新执行MapReduce操作。

    在失效方面的处理机制

    (alex注:原文为”semantics in the presence of failures”)
    当用户提供的Map和Reduce操作是输入确定性函数(即相同的输入产生相同的输出)时,我们的分布式实现在任何情况下的输出都和所有程序没有出现任何错误、顺序的执行产生的输出是一样的。

    我们依赖对Map和Reduce任务的输出是原子提交的来完成这个特性。每个工作中的任务把它的输出写到私有的临时文件中。每个Reduce任务生成一个这样的文件,而每个Map任务则生成R个这样的文件(一个Reduce任务对应一个文件)。当一个Map任务完成的时,worker发送一个包含R个临时文件名的完成消息给master。如果master从一个已经完成的Map任务再次接收到到一个完成消息,master将忽略这个消息;否则,master将这R个文件的名字记录在数据结构里。

    当Reduce任务完成时,Reduce worker进程以原子的方式把临时文件重命名为最终的输出文件。如果同一个Reduce任务在多台机器上执行,针对同一个最终的输出文件将有多个重命名操作执行。我们依赖底层文件系统提供的重命名操作的原子性来保证最终的文件系统状态仅仅包含一个Reduce任务产生的数据。

    使用MapReduce模型的程序员可以很容易的理解他们程序的行为,因为我们绝大多数的Map和Reduce操作是确定性的,而且存在这样的一个事实:我们的失效处理机制等价于一个顺序的执行的操作。当Map或/和Reduce操作是不确定性的时候,我们提供虽然较弱但是依然合理的处理机制。当使用非确定操作的时候,一个Reduce任务R1的输出等价于一个非确定性程序顺序执行产生时的输出。但是,另一个Reduce任务R2的输出也许符合一个不同的非确定顺序程序执行产生的R2的输出。

    考虑Map任务M和Reduce任务R1、R2的情况。我们设定e(Ri)是Ri已经提交的执行过程(有且仅有一个这样的执行过程)。当e(R1)读取了由M一次执行产生的输出,而e(R2)读取了由M的另一次执行产生的输出,导致了较弱的失效处理。

    存储位置

    在我们的计算运行环境中,网络带宽是一个相当匮乏的资源。我们通过尽量把输入数据(由GFS管理)存储在集群中机器的本地磁盘上来节省网络带宽。GFS把每个文件按64MB一个Block分隔,每个Block保存在多台机器上,环境中就存放了多份拷贝(一般是3个拷贝)。MapReduce的master在调度Map任务时会考虑输入文件的位置信息,尽量将一个Map任务调度在包含相关输入数据拷贝的机器上执行;如果上述努力失败了,master将尝试在保存有输入数据拷贝的机器附近的机器上执行Map任务(例如,分配到一个和包含输入数据的机器在一个switch里的worker机器上执行)。当在一个足够大的cluster集群上运行大型MapReduce操作的时候,大部分的输入数据都能从本地机器读取,因此消耗非常少的网络带宽。

    任务粒度

    如前所述,我们把Map拆分成了M个片段、把Reduce拆分成R个片段执行。理想情况下,M和R应当比集群中worker的机器数量要多得多。在每台worker机器都执行大量的不同任务能够提高集群的动态的负载均衡能力,并且能够加快故障恢复的速度:失效机器上执行的大量Map任务都可以分布到所有其他的worker机器上去执行。

    但是实际上,在我们的具体实现中对M和R的取值都有一定的客观限制,因为master必须执行O(M+R)次调度,并且在内存中保存O(M*R)个状态(对影响内存使用的因素还是比较小的:O(M*R)块状态,大概每对Map任务/Reduce任务1个字节就可以了)。

    更进一步,R值通常是由用户指定的,因为每个Reduce任务最终都会生成一个独立的输出文件。实际使用时我们也倾向于选择合适的M值,以使得每一个独立任务都是处理大约16M到64M的输入数据(这样,上面描写的输入数据本地存储优化策略才最有效),另外,我们把R值设置为我们想使用的worker机器数量的小的倍数。我们通常会用这样的比例来执行MapReduce:M=200000,R=5000,使用2000台worker机器。

    备用任务

    影响一个MapReduce的总执行时间最通常的因素是“落伍者”:在运算过程中,如果有一台机器花了很长的时间才完成最后几个Map或Reduce任务,导致MapReduce操作总的执行时间超过预期。出现“落伍者”的原因非常多。比如:如果一个机器的硬盘出了问题,在读取的时候要经常的进行读取纠错操作,导致读取数据的速度从30M/s降低到1M/s。如果cluster的调度系统在这台机器上又调度了其他的任务,由于CPU、内存、本地硬盘和网络带宽等竞争因素的存在,导致执行MapReduce代码的执行效率更加缓慢。我们最近遇到的一个问题是由于机器的初始化代码有bug,导致关闭了的处理器的缓存:在这些机器上执行任务的性能和正常情况相差上百倍。

    我们有一个通用的机制来减少“落伍者”出现的情况。当一个MapReduce操作接近完成的时候,master调度备用(backup)任务进程来执行剩下的、处于处理中状态(in-progress)的任务。无论是最初的执行进程、还是备用(backup)任务进程完成了任务,我们都把这个任务标记成为已经完成。我们调优了这个机制,通常只会占用比正常操作多几个百分点的计算资源。我们发现采用这样的机制对于减少超大MapReduce操作的总处理时间效果显著。例如,在5.3节描述的排序任务,在关闭掉备用任务的情况下要多花44%的时间完成排序任务。

    技巧

    虽然简单的Map和Reduce函数提供的基本功能已经能够满足大部分的计算需要,我们还是发掘出了一些有价值的扩展功能。本节将描述这些扩展功能。

    分区函数

    MapReduce的使用者通常会指定Reduce任务和Reduce任务输出文件的数量(R)。我们在中间key上使用分区函数来对数据进行分区,之后再输入到后续任务执行进程。一个缺省的分区函数是使用hash方法(比如,hash(key) mod R)进行分区。hash方法能产生非常平衡的分区。然而,有的时候,其它的一些分区函数对key值进行的分区将非常有用。比如,输出的key值是URLs,我们希望每个主机的所有条目保持在同一个输出文件中。为了支持类似的情况,MapReduce库的用户需要提供专门的分区函数。例如,使用“hash(Hostname(urlkey)) mod R”作为分区函数就可以把所有来自同一个主机的URLs保存在同一个输出文件中。

    顺序保证

    我们确保在给定的分区中,中间key/value pair数据的处理顺序是按照key值增量顺序处理的。这样的顺序保证对每个分成生成一个有序的输出文件,这对于需要对输出文件按key值随机存取的应用非常有意义,对在排序输出的数据集也很有帮助。

    Combiner函数

    在某些情况下,Map函数产生的中间key值的重复数据会占很大的比重,并且,用户自定义的Reduce函数满足结合律和交换律。在2.1节的词数统计程序是个很好的例子。由于词频率倾向于一个zipf分布(齐夫分布),每个Map任务将产生成千上万个这样的记录the,1.所有的这些记录将通过网络被发送到一个单独的Reduce任务,然后由这个Reduce任务把所有这些记录累加起来产生一个数字。我们允许用户指定一个可选的combiner函数,combiner函数首先在本地将这些记录进行一次合并,然后将合并的结果再通过网络发送出去。

    Combiner函数在每台执行Map任务的机器上都会被执行一次。一般情况下,Combiner和Reduce函数是一样的。Combiner函数和Reduce函数之间唯一的区别是MapReduce库怎样控制函数的输出。Reduce函数的输出被保存在最终的输出文件里,而Combiner函数的输出被写到中间文件里,然后被发送给Reduce任务。

    部分的合并中间结果可以显著的提高一些MapReduce操作的速度。附录A包含一个使用combiner函数的例子。

    输入和输出的类型

    MapReduce库支持几种不同的格式的输入数据。比如,文本模式的输入数据的每一行被视为是一个key/value pair。key是文件的偏移量,value是那一行的内容。另外一种常见的格式是以key进行排序来存储的key/value pair的序列。每种输入类型的实现都必须能够把输入数据分割成数据片段,该数据片段能够由单独的Map任务来进行后续处理(例如,文本模式的范围分割必须确保仅仅在每行的边界进行范围分割)。虽然大多数MapReduce的使用者仅仅使用很少的预定义输入类型就满足要求了,但是使用者依然可以通过提供一个简单的Reader接口实现就能够支持一个新的输入类型。

    Reader并非一定要从文件中读取数据,比如,我们可以很容易的实现一个从数据库里读记录的Reader,或者从内存中的数据结构读取数据的Reader。
    类似的,我们提供了一些预定义的输出数据的类型,通过这些预定义类型能够产生不同格式的数据。用户采用类似添加新的输入数据类型的方式增加新的输出类型。

    副作用

    在某些情况下,MapReduce的使用者发现,如果在Map和/或Reduce操作过程中增加辅助的输出文件会比较省事。我们依靠程序writer把这种“副作用”变成原子的和幂等的(alex注:幂等的指一个总是产生相同结果的数学运算)。通常应用程序首先把输出结果写到一个临时文件中,在输出全部数据之后,在使用系统级的原子操作rename重新命名这个临时文件。

    如果一个任务产生了多个输出文件,我们没有提供类似两阶段提交的原子操作支持这种情况。因此,对于会产生多个输出文件、并且对于跨文件有一致性要求的任务,都必须是确定性的任务。但是在实际应用过程中,这个限制还没有给我们带来过麻烦。

    跳过损坏的记录

    有时候,用户程序中的bug导致Map或者Reduce函数在处理某些记录的时候crash掉,MapReduce操作无法顺利完成。惯常的做法是修复bug后再次执行MapReduce操作,但是,有时候找出这些bug并修复它们不是一件容易的事情;这些bug也许是在第三方库里边,而我们手头没有这些库的源代码。而且在很多时候,忽略一些有问题的记录也是可以接受的,比如在一个巨大的数据集上进行统计分析的时候。我们提供了一种执行模式,在这种模式下,为了保证保证整个处理能继续进行,MapReduce会检测哪些记录导致确定性的crash,并且跳过这些记录不处理。

    每个worker进程都设置了信号处理函数捕获内存段异常(segmentation violation)和总线错误(bus error)。在执行Map或者Reduce操作之前,MapReduce库通过全局变量保存记录序号。如果用户程序触发了一个系统信号,消息处理函数将用“最后一口气”通过UDP包向master发送处理的最后一条记录的序号。当master看到在处理某条特定记录不止失败一次时,master就标志着条记录需要被跳过,并且在下次重新执行相关的Map或者Reduce任务的时候跳过这条记录。

    本地执行

    调试Map和Reduce函数的bug是非常困难的,因为实际执行操作时不但是分布在系统中执行的,而且通常是在好几千台计算机上执行,具体的执行位置是由master进行动态调度的,这又大大增加了调试的难度。为了简化调试、profile和小规模测试,我们开发了一套MapReduce库的本地实现版本,通过使用本地版本的MapReduce库,MapReduce操作在本地计算机上顺序的执行。用户可以控制MapReduce操作的执行,可以把操作限制到特定的Map任务上。用户通过设定特别的标志来在本地执行他们的程序,之后就可以很容易的使用本地调试和测试工具(比如gdb)。

    状态信息

    master使用嵌入式的HTTP服务器(如Jetty)显示一组状态信息页面,用户可以监控各种执行状态。状态信息页面显示了包括计算执行的进度,比如已经完成了多少任务、有多少任务正在处理、输入的字节数、中间数据的字节数、输出的字节数、处理百分比等等。页面还包含了指向每个任务的stderr和stdout文件的链接。用户根据这些数据预测计算需要执行大约多长时间、是否需要增加额外的计算资源。这些页面也可以用来分析什么时候计算执行的比预期的要慢。

    另外,处于最顶层的状态页面显示了哪些worker失效了,以及他们失效的时候正在运行的Map和Reduce任务。这些信息对于调试用户代码中的bug很有帮助。

    计数器

    MapReduce库使用计数器统计不同事件发生次数。比如,用户可能想统计已经处理了多少个单词、已经索引的多少篇German文档等等。

    为了使用这个特性,用户在程序中创建一个命名的计数器对象,在Map和Reduce函数中相应的增加计数器的值。例如:

    Counter* uppercase;
    uppercase = GetCounter(“uppercase”);
    map(String name, String contents):
     for each word w in contents:
      if (IsCapitalized(w)):
       uppercase->Increment();
      EmitIntermediate(w, “1″);

    这些计数器的值周期性的从各个单独的worker机器上传递给master(附加在ping的应答包中传递)。master把执行成功的Map和Reduce任务的计数器值进行累计,当MapReduce操作完成之后,返回给用户代码。

    计数器当前的值也会显示在master的状态页面上,这样用户就可以看到当前计算的进度。当累加计数器的值的时候,master要检查重复运行的Map或者Reduce任务,避免重复累加(之前提到的备用任务和失效后重新执行任务这两种情况会导致相同的任务被多次执行)。

    有些计数器的值是由MapReduce库自动维持的,比如已经处理的输入的key/value pair的数量、输出的key/value pair的数量等等。

    计数器机制对于MapReduce操作的完整性检查非常有用。比如,在某些MapReduce操作中,用户需要确保输出的key value pair精确的等于输入的key value pair,或者处理的German文档数量在处理的整个文档数量中属于合理范围。

    性能

    本节我们用在一个大型集群上运行的两个计算来衡量MapReduce的性能。一个计算在大约1TB的数据中进行特定的模式匹配,另一个计算对大约1TB的数据进行排序。

    这两个程序在大量的使用MapReduce的实际应用中是非常典型的 — 一类是对数据格式进行转换,从一种表现形式转换为另外一种表现形式;另一类是从海量数据中抽取少部分的用户感兴趣的数据。

    集群配置

    所有这些程序都运行在一个大约由1800台机器构成的集群上。每台机器配置2个2G主频、支持超线程的Intel Xeon处理器,4GB的物理内存,两个160GB的IDE硬盘和一个千兆以太网卡。这些机器部署在一个两层的树形交换网络中,在root节点大概有100-200GBPS的传输带宽。所有这些机器都采用相同的部署(对等部署),因此任意两点之间的网络来回时间小于1毫秒。

    在4GB内存里,大概有1-1.5G用于运行在集群上的其他任务。测试程序在周末下午开始执行,这时主机的CPU、磁盘和网络基本上处于空闲状态。

    GREP

    这个分布式的grep程序需要扫描大概10的10次方个由100个字节组成的记录,查找出现概率较小的3个字符的模式(这个模式在92337个记录中出现)。输入数据被拆分成大约64M的Block(M=15000),整个输出数据存放在一个文件中(R=1)。
    这里写图片描述
    图2显示了这个运算随时间的处理过程。其中Y轴表示输入数据的处理速度。处理速度随着参与MapReduce计算的机器数量的增加而增加,当1764台worker参与计算的时,处理速度达到了30GB/s。当Map任务结束的时候,即在计算开始后80秒,输入的处理速度降到0。整个计算过程从开始到结束一共花了大概150秒。这包括了大约一分钟的初始启动阶段。初始启动阶段消耗的时间包括了是把这个程序传送到各个worker机器上的时间、等待GFS文件系统打开1000个输入文件集合的时间、获取相关的文件本地位置优化信息的时间。

    排序

    排序程序处理10的10次方个100个字节组成的记录(大概1TB的数据)。这个程序模仿TeraSort benchmark[10]。

    排序程序由不到50行代码组成。只有三行的Map函数从文本行中解析出10个字节的key值作为排序的key,并且把这个key和原始文本行作为中间的key/value pair值输出。我们使用了一个内置的恒等函数作为Reduce操作函数。这个函数把中间的key/value pair值不作任何改变输出。最终排序结果输出到两路复制的GFS文件系统(也就是说,程序输出2TB的数据)。

    如前所述,输入数据被分成64MB的Block(M=15000)。我们把排序后的输出结果分区后存储到4000个文件(R=4000)。分区函数使用key的原始字节来把数据分区到R个片段中。

    在这个benchmark测试中,我们使用的分区函数知道key的分区情况。通常对于排序程序来说,我们会增加一个预处理的MapReduce操作用于采样key值的分布情况,通过采样的数据来计算对最终排序处理的分区点。
    这里写图片描述
    图三(a)显示了这个排序程序的正常执行过程。左上的图显示了输入数据读取的速度。数据读取速度峰值会达到13GB/s,并且所有Map任务完成之后,即大约200秒之后迅速滑落到0。值得注意的是,排序程序输入数据读取速度小于分布式grep程序。这是因为排序程序的Map任务花了大约一半的处理时间和I/O带宽把中间输出结果写到本地硬盘。相应的分布式grep程序的中间结果输出几乎可以忽略不计。

    左边中间的图显示了中间数据从Map任务发送到Reduce任务的网络速度。这个过程从第一个Map任务完成之后就开始缓慢启动了。图示的第一个高峰是启动了第一批大概1700个Reduce任务(整个MapReduce分布到大概1700台机器上,每台机器1次最多执行1个Reduce任务)。排序程序运行大约300秒后,第一批启动的Reduce任务有些完成了,我们开始执行剩下的Reduce任务。所有的处理在大约600秒后结束。

    左下图表示Reduce任务把排序后的数据写到最终的输出文件的速度。在第一个排序阶段结束和数据开始写入磁盘之间有一个小的延时,这是因为worker机器正在忙于排序中间数据。磁盘写入速度在2-4GB/s持续一段时间。输出数据写入磁盘大约持续850秒。计入初始启动部分的时间,整个运算消耗了891秒。这个速度和TeraSort benchmark[18]的最高纪录1057秒相差不多。

    还有一些值得注意的现象:输入数据的读取速度比排序速度和输出数据写入磁盘速度要高不少,这是因为我们的输入数据本地化优化策略起了作用 — 绝大部分数据都是从本地硬盘读取的,从而节省了网络带宽。排序速度比输出数据写入到磁盘的速度快,这是因为输出数据写了两份(我们使用了2路的GFS文件系统,写入复制节点的原因是为了保证数据可靠性和可用性)。我们把输出数据写入到两个复制节点的原因是因为这是底层文件系统的保证数据可靠性和可用性的实现机制。如果底层文件系统使用类似容错编码[14](erasure coding)的方式而不是复制的方式保证数据的可靠性和可用性,那么在输出数据写入磁盘的时候,就可以降低网络带宽的使用。

    高效的backup任务

    图三(b)显示了关闭了备用任务后排序程序执行情况。执行的过程和图3(a)很相似,除了输出数据写磁盘的动作在时间上拖了一个很长的尾巴,而且在这段时间里,几乎没有什么写入动作。在960秒后,只有5个Reduce任务没有完成。这些拖后腿的任务又执行了300秒才完成。整个计算消耗了1283秒,多了44%的执行时间。

    失效的机器

    在图三(c)中演示的排序程序执行的过程中,我们在程序开始后几分钟有意的kill了1746个worker中的200个。集群底层的调度立刻在这些机器上重新开始新的worker处理进程(因为只是worker机器上的处理进程被kill了,机器本身还在工作)。

    图三(c)显示出了一个“负”的输入数据读取速度,这是因为一些已经完成的Map任务丢失了(由于相应的执行Map任务的worker进程被kill了),需要重新执行这些任务。相关Map任务很快就被重新执行了。整个运算在933秒内完成,包括了初始启动时间(只比正常执行多消耗了5%的时间)。

    经验

    我们在2003年1月完成了第一个版本的MapReduce库,在2003年8月的版本有了显著的增强,这包括了输入数据本地优化、worker机器之间的动态负载均衡等等。从那以后,我们惊喜的发现,MapReduce库能广泛应用于我们日常工作中遇到的各类问题。它现在在Google内部各个领域得到广泛应用,包括:

    • 大规模机器学习问题
    • Google News和Froogle产品的集群问题
    • 从公众查询产品(比如Google的Zeitgeist)的报告中抽取数据。
    • 从大量的新应用和新产品的网页中提取有用信息(比如,从大量的位置搜索网页中抽取地理位置信息)。
    • 大规模的图形计算。
      这里写图片描述
      图四显示了在我们的源代码管理系统中,随着时间推移,独立的MapReduce程序数量的显著增加。从2003年早些时候的0个增长到2004年9月份的差不多900个不同的程序。MapReduce的成功取决于采用MapReduce库能够在不到半个小时时间内写出一个简单的程序,这个简单的程序能够在上千台机器的组成的集群上做大规模并发处理,这极大的加快了开发和原形设计的周期。另外,采用MapReduce库,可以让完全没有分布式和/或并行系统开发经验的程序员很容易的利用大量的资源,开发出分布式和/或并行处理的应用。

    在每个任务结束的时候,MapReduce库统计计算资源的使用状况。在表1,我们列出了2004年8月份MapReduce运行的任务所占用的相关资源。

    大规模索引

    到目前为止,MapReduce最成功的应用就是重写了Google网络搜索服务所使用到的index系统。索引系统的输入数据是网络爬虫抓取回来的海量的文档,这些文档数据都保存在GFS文件系统里。这些文档原始内容(alex注:raw contents,我认为就是网页中的剔除html标记后的内容、pdf和word等有格式文档中提取的文本内容等)的大小超过了20TB。索引程序是通过一系列的MapReduce操作(大约5到10次)来建立索引。使用MapReduce(替换上一个特别设计的、分布式处理的索引程序)带来这些好处:

    • 实现索引部分的代码简单、小巧、容易理解,因为对于容错、分布式以及并行计算的处理都是MapReduce库提供的。比如,使用MapReduce库,计算的代码行数从原来的3800行C++代码减少到大概700行代码。
    • MapReduce库的性能已经足够好了,因此我们可以把在概念上不相关的计算步骤分开处理,而不是混在一起以期减少数据传递的额外消耗。概念上不相关的计算步骤的隔离也使得我们可以很容易改变索引处理方式。比如,对之前的索引系统的一个小更改可能要耗费好几个月的时间,但是在使用MapReduce的新系统上,这样的更改只需要花几天时间就可以了。
    • 索引系统的操作管理更容易了。因为由机器失效、机器处理速度缓慢、以及网络的瞬间阻塞等引起的绝大部分问题都已经由MapReduce库解决了,不再需要操作人员的介入了。另外,我们可以通过在索引系统集群中增加机器的简单方法提高整体处理性能。

    相关工作

    很多系统都提供了严格的编程模式,并且通过对编程的严格限制来实现并行计算。例如,一个结合函数可以通过把N个元素的数组的前缀在N个处理器上使用并行前缀算法,在log N的时间内计算完[6,9,13](alex注:完全没有明白作者在说啥,具体参考相关6、9、13文档)。MapReduce可以看作是我们结合在真实环境下处理海量数据的经验,对这些经典模型进行简化和萃取的成果。更加值得骄傲的是,我们还实现了基于上千台处理器的集群的容错处理。相比而言,大部分并发处理系统都只在小规模的集群上实现,并且把容错处理交给了程序员。

    Bulk Synchronous Programming[17]和一些MPI原语[11]提供了更高级别的并行处理抽象,可以更容易写出并行处理的程序。MapReduce和这些系统的关键不同之处在于,MapReduce利用限制性编程模式实现了用户程序的自动并发处理,并且提供了透明的容错处理。

    我们数据本地优化策略的灵感来源于active disks[12,15]等技术,在active disks中,计算任务是尽量推送到数据存储的节点处理(alex注:即靠近数据源处理),这样就减少了网络和IO子系统的吞吐量。我们在挂载几个硬盘的普通机器上执行我们的运算,而不是在磁盘处理器上执行我们的工作,但是达到的目的一样的。

    我们的备用任务机制和Charlotte System[3]提出的eager调度机制比较类似。Eager调度机制的一个缺点是如果一个任务反复失效,那么整个计算就不能完成。我们通过忽略引起故障的记录的方式在某种程度上解决了这个问题。

    MapReduce的实现依赖于一个内部的集群管理系统,这个集群管理系统负责在一个超大的、共享机器的集群上分布和运行用户任务。虽然这个不是本论文的重点,但是有必要提一下,这个集群管理系统在理念上和其它系统,如Condor[16]是一样。

    MapReduce库的排序机制和NOW-Sort[1]的操作上很类似。读取输入源的机器(map workers)把待排序的数据进行分区后,发送到R个Reduce worker中的一个进行处理。每个Reduce worker在本地对数据进行排序(尽可能在内存中排序)。当然,NOW-Sort没有给用户自定义的Map和Reduce函数的机会,因此不具备MapReduce库广泛的实用性。

    River[2]提供了一个编程模型:处理进程通过分布式队列传送数据的方式进行互相通讯。和MapReduce类似,River系统尝试在不对等的硬件环境下,或者在系统颠簸的情况下也能提供近似平均的性能。River是通过精心调度硬盘和网络的通讯来平衡任务的完成时间。MapReduce库采用了其它的方法。通过对编程模型进行限制,MapReduce框架把问题分解成为大量的“小”任务。这些任务在可用的worker集群上动态的调度,这样快速的worker就可以执行更多的任务。通过对编程模型进行限制,我们可用在工作接近完成的时候调度备用任务,缩短在硬件配置不均衡的情况下缩小整个操作完成的时间(比如有的机器性能差、或者机器被某些操作阻塞了)。

    BAD-FS[5]采用了和MapReduce完全不同的编程模式,它是面向广域网(alex注:wide-area network)的。不过,这两个系统有两个基础功能很类似。(1)两个系统采用重新执行的方式来防止由于失效导致的数据丢失。(2)两个都使用数据本地化调度策略,减少网络通讯的数据量。

    TACC[7]是一个用于简化构造高可用性网络服务的系统。和MapReduce一样,它也依靠重新执行机制来实现的容错处理。

    结束语

    MapReduce编程模型在Google内部成功应用于多个领域。我们把这种成功归结为几个方面:首先,由于MapReduce封装了并行处理、容错处理、数据本地化优化、负载均衡等等技术难点的细节,这使得MapReduce库易于使用。即便对于完全没有并行或者分布式系统开发经验的程序员而言;其次,大量不同类型的问题都可以通过MapReduce简单的解决。比如,MapReduce用于生成Google的网络搜索服务所需要的数据、用来排序、用来数据挖掘、用于机器学习,以及很多其它的系统;第三,我们实现了一个在数千台计算机组成的大型集群上灵活部署运行的MapReduce。这个实现使得有效利用这些丰富的计算资源变得非常简单,因此也适合用来解决Google遇到的其他很多需要大量计算的问题。

    我们也从MapReduce开发过程中学到了不少东西。首先,约束编程模式使得并行和分布式计算非常容易,也易于构造容错的计算环境;其次,网络带宽是稀有资源。大量的系统优化是针对减少网络传输量为目的的:本地优化策略使大量的数据从本地磁盘读取,中间文件写入本地磁盘、并且只写一份中间文件也节约了网络带宽;第三,多次执行相同的任务可以减少性能缓慢的机器带来的负面影响(alex注:即硬件配置的不平衡),同时解决了由于机器失效导致的数据丢失问题。

    感谢

    (alex注:还是原汁原味的感谢词比较好,这个就不翻译了)Josh Levenberg has been instrumental in revising and extending the user-level MapReduce API with a number of new features based on his experience with using MapReduce and other people’s suggestions for enhancements. MapReduce reads its input from and writes its output to the Google File System [8]. We would like to thank Mohit Aron, Howard Gobioff, Markus Gutschke, David Kramer, Shun-Tak Leung, and Josh Redstone for their work in developing GFS. We would also like to thank Percy Liang and Olcan Sercinoglu for their work in developing the cluster management system used by MapReduce. Mike Burrows, Wilson Hsieh, Josh Levenberg, Sharon Perl, Rob Pike, and Debby Wallach provided helpful comments on earlier drafts of this paper.The anonymous OSDI reviewers, and our shepherd, Eric Brewer, provided many useful suggestions of areas where the paper could be improved. Finally, we thank all the users of MapReduce within Google’s engineering organization for providing helpful feedback, suggestions, and bug reports.
    10、参考资料
    [1] Andrea C. Arpaci-Dusseau, Remzi H. Arpaci-Dusseau,David E. Culler, Joseph M. Hellerstein, and David A. Patterson.High-performance sorting on networks of workstations.In Proceedings of the 1997 ACM SIGMOD InternationalConference on Management of Data, Tucson,Arizona, May 1997.
    [2] Remzi H. Arpaci-Dusseau, Eric Anderson, NoahTreuhaft, David E. Culler, Joseph M. Hellerstein, David Patterson, and Kathy Yelick. Cluster I/O with River:Making the fast case common. In Proceedings of the Sixth Workshop on Input/Output in Parallel and Distributed Systems (IOPADS ’99), pages 10.22, Atlanta, Georgia, May 1999.
    [3] Arash Baratloo, Mehmet Karaul, Zvi Kedem, and Peter Wyckoff. Charlotte: Metacomputing on the web. In Proceedings of the 9th International Conference on Parallel and Distributed Computing Systems, 1996. [4] Luiz A. Barroso, Jeffrey Dean, and Urs H¨olzle. Web search for a planet: The Google cluster architecture. IEEE Micro, 23(2):22.28, April 2003.
    [5] John Bent, Douglas Thain, Andrea C.Arpaci-Dusseau, Remzi H. Arpaci-Dusseau, and Miron Livny. Explicit control in a batch-aware distributed file system. In Proceedings of the 1st USENIX Symposium on Networked Systems Design and Implementation NSDI, March 2004.
    [6] Guy E. Blelloch. Scans as primitive parallel operations.IEEE Transactions on Computers, C-38(11), November 1989.
    [7] Armando Fox, Steven D. Gribble, Yatin Chawathe, Eric A. Brewer, and Paul Gauthier. Cluster-based scalable network services. In Proceedings of the 16th ACM Symposium on Operating System Principles, pages 78. 91, Saint-Malo, France, 1997.
    [8] Sanjay Ghemawat, Howard Gobioff, and Shun-Tak Leung. The Google file system. In 19th Symposium on Operating Systems Principles, pages 29.43, Lake George, New York, 2003. To appear in OSDI 2004 12
    [9] S. Gorlatch. Systematic efficient parallelization of scan and other list homomorphisms. In L. Bouge, P. Fraigniaud, A. Mignotte, and Y. Robert, editors, Euro-Par’96. Parallel Processing, Lecture Notes in Computer Science 1124, pages 401.408. Springer-Verlag, 1996.
    [10] Jim Gray. Sort benchmark home page. http://research.microsoft.com/barc/SortBenchmark/.
    [11] William Gropp, Ewing Lusk, and Anthony Skjellum. Using MPI: Portable Parallel Programming with the Message-Passing Interface. MIT Press, Cambridge, MA, 1999.
    [12] L. Huston, R. Sukthankar, R.Wickremesinghe, M. Satyanarayanan, G. R. Ganger, E. Riedel, and A. Ailamaki. Diamond: A storage architecture for early discard in interactive search. In Proceedings of the 2004 USENIX File and Storage Technologies FAST Conference, April 2004.
    [13] Richard E. Ladner and Michael J. Fischer. Parallel prefix computation. Journal of the ACM, 27(4):831.838, 1980.
    [14] Michael O. Rabin. Efficient dispersal of information for security, load balancing and fault tolerance. Journal of the ACM, 36(2):335.348, 1989.
    [15] Erik Riedel, Christos Faloutsos, Garth A. Gibson, and David Nagle. Active disks for large-scale data processing. IEEE Computer, pages 68.74, June 2001.
    [16] Douglas Thain, Todd Tannenbaum, and Miron Livny. Distributed computing in practice: The Condor experience. Concurrency and Computation: Practice and Experience, 2004.
    [17] L. G. Valiant. A bridging model for parallel computation. Communications of the ACM, 33(8):103.111, 1997.
    [18] Jim Wyllie. Spsort: How to sort a terabyte quickly. http://alme1.almaden.ibm.com/cs/spsort.pdf.

    附录A、单词频率统计

    本节包含了一个完整的程序,用于统计在一组命令行指定的输入文件中,每一个不同的单词出现频率。

    #include “mapreduce/mapreduce.h”
    // User’s map function
    class WordCounter : public Mapper {
     public:
      virtual void Map(const MapInput& input) {
       const string& text = input.value();
       const int n = text.size();
       for (int i = 0; i < n; ) {
        // Skip past leading whitespace
        while ((i < n) && isspace(text[i]))
         i++;
       // Find word end
       int start = i;
       while ((i < n) && !isspace(text[i]))
        i++;
       if (start < i)
        Emit(text.substr(start,i-start),”1″);
      }
     }
    };
    REGISTER_MAPPER(WordCounter);
    // User’s reduce function
    class Adder : public Reducer {
     virtual void Reduce(ReduceInput* input) {
      // Iterate over all entries with the
      // same key and add the values
      int64 value = 0;
      while (!input->done()) {
       value += StringToInt(input->value());
       input->NextValue();
      }
      // Emit sum for input->key()
      Emit(IntToString(value));
     }
    };
    REGISTER_REDUCER(Adder);
    int main(int argc, char** argv) {
     ParseCommandLineFlags(argc, argv);
    
     MapReduceSpecification spec;
    
     // Store list of input files into “spec”
     for (int i = 1; i < argc; i++) {
      MapReduceInput* input = spec.add_input();
      input->set_format(“text”);
      input->set_filepattern(argv[i]);
      input->set_mapper_class(“WordCounter”);
     }
     // Specify the output files:
     // /gfs/test/freq-00000-of-00100
     // /gfs/test/freq-00001-of-00100
     // …
     MapReduceOutput* out = spec.output();
     out->set_filebase(“/gfs/test/freq”);
     out->set_num_tasks(100);
     out->set_format(“text”);
     out->set_reducer_class(“Adder”);
    
     // Optional: do partial sums within map
     // tasks to save network bandwidth
     out->set_combiner_class(“Adder”);
     // Tuning parameters: use at most 2000
     // machines and 100 MB of memory per task
     spec.set_machines(2000);
     spec.set_map_megabytes(100);
     spec.set_reduce_megabytes(100);
    
     // Now run it
     MapReduceResult result;
     if (!MapReduce(spec, &result)) abort();
    
     // Done: ‘result’ structure contains info
     // about counters, time taken, number of
     // machines used, etc.
     return 0;
    }
    展开全文
  • CMake官方教程中文翻译

    千次阅读 2019-11-07 11:23:12
    看见一博主翻译的一篇官方cmke教程,觉得很不错就转载并稍作一些细小修改,我提供的3.16版本的文档是最新的,博主之前翻译的是3.7的,内容比3.16少一点点,想看3.16完整内容,下面也有链接。 提供的CMake简介 ...
  • 本文是目录,每条目录有两部分内容:英文+中文(仅已翻译的有); 其英文附有原来链接;中文附有中文文章链接。 Cython project homepage Getting Started Cython - an overview Installing Cyt...
  • git --help(中文翻译

    千次阅读 2019-06-24 20:36:58
    翻译一下git的帮助文档,帮助记忆 首先在命令行输入 git --help 会得到下面的帮助信息 下面是逐行翻译 These are common Git commands used in various situations: 下面是各种情况下比较常见的git命令 ...
  • 给自己一个动力去看英语论文,每天翻译一节,纯属自己翻译,小白一只,如果您能提出建议或者翻译修改,将非常感谢,首先谢谢! How Generative Adversarial Networks and Their Variants Work: An Overview ...
  • Raft协议中文翻译(1)

    千次阅读 2017-12-09 16:00:48
    一直以来, 对Raft协议的理解感觉都没有非常到位, 本着眼过千遍, 不如手过一遍的原则, 利用空闲时间, 就自己把Raft翻译一遍, 加深自己的理解, 也方便其他同学参考。 Raft协议英文版 参考资料: ...
  • 工作说明书(Statement of Work,简称SOW)是对项目所要提供的产品或服务的叙述性的描述。对内部项目而言,项目发起者或投资人基于业务需求,或产品或服务的需求提出工作说明书。对外部项目而言,工作说明书作为投标...
  • 为了进一步学习,最近重新仔细看了一下比特币白皮书,并趁着自己还理解,将其翻译中文。为了尽量保持原意,几乎全文采用直译,由于中英文的表达方式本身就存在差异,所以有些地方翻译起来会显得生硬,但已经尽量...
  • 1-how yaffs work.docx

    2019-07-06 10:02:41
    yaffs文件系统官方文件中文翻译,本文旨在对大多数核心机制给出合理的解释,这是Yaffs工作原理;本文件应作为理解Yaffs的第一站,讲述yaffs如何运作。
  • Android Studio 2.0 正式版发布啦 (首次中文翻译) 增加了一些新特性:  1. 更加完善的 Instant Run  2. 更快的 Android Emulator  3.GPU Debugger Preview  4. 包含了 IntelliJ 15 的更新 Android Studio ...
  • 大数据技术的经典书籍,介绍了多个大数据分析的应用实例,目前已有中文
  • <p> <img style="float:right" src="images/image.gif" width="100" height="100">Lorem ipsum dolor sit amet, consectetuer... </p>

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,295
精华内容 6,118
关键字:

work的中文翻译