Mechanical Sympathy 译文
Mechanical Sympathy(需要翻墙才能访问)是Martin Thompson的博客,这个博客主要讲的是底层硬件是如何运作的,以及如何编程能够与地层硬件良好的协作。英文名称的为未翻译的文章,有兴趣的同学可以领取翻译。
Mechanical Sympathy(需要翻墙才能访问)是Martin Thompson的博客,这个博客主要讲的是底层硬件是如何运作的,以及如何编程能够与地层硬件良好的协作。英文名称的为未翻译的文章,有兴趣的同学可以领取翻译。
原文链接 作者:Jakob Jenkov 译者:余绍亮 校对:丁一
所谓Slipped conditions,就是说, 从一个线程检查某一特定条件到该线程操作此条件期间,这个条件已经被其它线程改变,导致第一个线程在该条件上执行了错误的操作。这里有一个简单的例子:
[code lang=”java”]
public class Lock {
private boolean isLocked = true;
public void lock(){
synchronized(this){
while(isLocked){
try{
this.wait();
} catch(InterruptedException e){
//do nothing, keep waiting
}
}
}
synchronized(this){
isLocked = true;
}
}
public synchronized void unlock(){
isLocked = false;
this.notify();
}
}
[/code]
阅读全文
原文地址 作者:Mechanical Sympathy 译者:潘曦 校对:Simon-SZ ,方腾飞
即使是资深的技术人员,我经常听到他们谈论某些操作是如何导致一个CPU缓存的刷新。看来这是关于CPU缓存如何工作和缓存子系统如何与执行核心交互的一个常见误区。本文将致力于解释CPU缓存的功能以及执行程序指令的CPU核心如何与缓存交互。我将以最新的Intel x86 CPU为例进行说明,其他CPU也使用相似技术以达到相同目的。
绝大部分常见的现代系统都被设计成在多处理器上共享内存。共享内存的系统都有一个单独的内存资源,它会被两个或者更多的独立CPU核心同时访问。核心到主存的延迟变化范围很大,大约在10-100纳秒。在100ns内,一个3GH的CPU可以处理多达1200条指令。每一个Sandy Bridge的CPU核心,在每个CPU时钟内可以并行处理4条指令。CPU使用缓存子系统避免了处理核心直接访问主存的延迟,这样能使CPU更高效的处理指令。一些缓存很小、非常快速并且集成在每个核心之内;而另一些则慢一些、更大、在各个核心间共享。这些缓存与寄存器和主内存一起构成了非持久性的内存体系。 阅读全文
原文地址:http://mechanical-sympathy.blogspot.com/2012/08/memory-access-patterns-are-important.html(因墙转载)
In high-performance computing it is often said that the cost of a cache-miss is the largest performance penalty for an algorithm. For many years the increase in speed of our processors has greatly outstripped latency gains to main-memory. Bandwidth to main-memory has greatly increased via wider, and multi-channel, buses however the latency has not significantly reduced. To hide this latency our processors employ evermore complex cache sub-systems that have many layers.
The 1994 paper “Hitting the memory wall: implications of the obvious” describes the problem and goes on to argue that caches do not ultimately help because of compulsory cache-misses. I aim to show that by using access patterns which display consideration for the cache hierarchy, this conclusion is not inevitable.
阅读全文
原文地址:http://mechanical-sympathy.blogspot.com/2011/07/memory-barriersfences.html(因墙转载)
In this article I’ll discuss the most fundamental technique in concurrent programming known as memory barriers, or fences, that make the memory state within a processor visible to other processors.
CPUs have employed many techniques to try and accommodate the fact that CPU execution unit performance has greatly outpaced main memory performance. In my “Write Combining” article I touched on just one of these techniques. The most common technique employed by CPUs to hide memory latency is to pipeline instructions and then spend significant effort, and resource, on trying to re-order these pipelines to minimise stalls related to cache misses.
When a program is executed it does not matter if its instructions are re-ordered provided the same end result is achieved. For example, within a loop it does not matter when the loop counter is updated if no operation within the loop uses it. The compiler and CPU are free to re-order the instructions to best utilise the CPU provided it is updated by the time the next iteration is about to commence. Also over the execution of a loop this variable may be stored in a register and never pushed out to cache or main memory, thus it is never visible to another CPU. 阅读全文
先抛个问题,下面的语句是否都合理(编译通过),哪些会引起模式匹配?
scala> val a = 100
scala> val A = 100
scala> val a@b = 100
scala> val (a,b) = (100,200)
scala> val (a,B) = (100,200) //第二个变量大写
scala> val Array(a,b) = Array(100,200)
scala> val Array(a,B) = Array(100,200)
scala> object Test { val 2 = 2 }
scala> object Test { val 2 = 3 }
我们先看看其他语言(对scala有影响的)有关赋值语句的定义:
val P = E
表示定义了模式P
中的变量,并赋予它们表达式E
中相应的值。
在这两种语言中,赋值语句都明确的定义为模式匹配,那么scala中,所有的赋值语句是否都是模式匹配呢?
尤其scala可以说在函数式风格上与ML(及其家族)语言有某种血缘,在这一点上是否也与ML完全一致呢?
先分析一下上面的每条赋值语句:val a = 100
和 val A = 100
是直观且没有歧义的。
val a@b = 100
是什么意思?回忆一下第一篇里讲过的“变量绑定模式”,当时的例子有点复杂,重新理解一下:
//给"hello"字符串对象用v1这个变量名
scala> "hello" match { case v1 => println(v1) }
//变量绑定模式,把变量v2 绑定在v1这个模式上
scala> "hello" match { case v2@v1 => println(v2) }
上面的例子中,第一行中v1是个变量模式。 第二行中v2是一个新的变量,只有在v1这个模式匹配成功的情况下,才会把自己绑定到v1上,而v1因为是一个变量模式,它总能匹配成功,所以这里v2也会绑定到”hello”对象上。变量绑定模式通常不会这么使用,更多用在绑定到一个复合结构的模式上,如:
scala> List(1,List(2,3)) match { case List(_, x@List(2,_*)) => println(x.size) }
2
把变量x绑定到了嵌套的 List(2,3) 这个对象上
但赋值语句val a@b = 100
跟上面的有关系么?我们通过ToolBox看看它”脱糖”后的语法树:
scala> tb.parse("val a@b=100")
res13: tb.u.Tree =
{
<synthetic> private[this] val x$3 = 100: @scala.unchecked match {
case (a @ (b @ _)) => scala.Tuple2(a, b) //这一句
};
val a = x$3._1;
val b = x$3._2
}
有注释的那一句里面把a,b两个局部变量绑定到通配符”_”上,而这个通配符模式case _ =>
可以匹配任何对象,所以相当于把a,b两个变量绑定到了100这个对象上,并产生了一个二元组记录这两个局部变量值。最终把二元组里的值分别赋给了我们定义的a,b两个变量。
接下来的val (a,b) = (100,200)
这个赋值也容易理解,把二元组里的值分别赋给a,b两个变量么,也是经过模式匹配的么?继续用ToolBox分析:
scala> tb.parse("val (a,b)=(100,200)")
res14: tb.u.Tree =
{
<synthetic> private[this] val x$4 = scala.Tuple2(100, 200): @scala.unchecked match {
case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2(a, b)
};
val a = x$4._1;
val b = x$4._2
}
看到了,是一个构造器模式与变量绑定模式的混合模式匹配。
再下一个val (a,B) = (100,200)
这个与上一个有区别么?回顾一下第一篇里讲到的“常量模式”:当变量大写时将被对待为常量模式,也就是说 大写B 和上面的 小写b 是两种不同的模式!!
scala> tb.parse("val (a,B)=(100,200)")
res15: tb.u.Tree =
val a = scala.Tuple2(100, 200): @scala.unchecked match {
case scala.Tuple2((a @ _), B) => a
}
大写B在这里当作常量来解析,但又找不到B这个变量(除非之前有定义过),就报错了:
scala> val (a,B) = (100,200)
<console>:8: error: not found: value B
val (a,B) = (100,200)
^
后边两个Array的赋值语句与这两个类似,小括号写法只是元组(Tuple)的语法糖而已。
最后,真正有趣,且会让新手崩溃的情况 object Test { val 2 = 2 }
为什么这个编译和初始化都没问题?
scala> object Test { val 2 = 2 }
defined module Test
scala> Test
res16: Test.type = Test$@3042dc22
简直逆天,难道这个背后也与模式匹配有关系么?
scala> tb.parse(" object Test { val 2 = 2 }")
res0: tb.u.Tree =
object Test extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
<synthetic> private[this] val x$1 = 2: @scala.unchecked match {
case 2 => ()
}
}
确实又是一个常量模式匹配,2匹配2,成功。
同理,下一个 object Test { val 2 = 3 }
也是个常量模式匹配,但为何明显不匹配,却可以编译时成功,而运行时时才报错呢?
scala> object Test { val 2 = 3 }
defined module Test
scala> Test
scala.MatchError: 3 (of class java.lang.Integer)
at Test$.<init>(<console>:8)
这是因为object 是惰性初始化的原因(lazy),如下:
// 对下面的单例
object Test { val a = 2 }
$ scalac -Xprint:jvm A.scala
package <empty> {
object Test extends Object {
private[this] val a: Int = _;
<stable> <accessor> def a(): Int = Test.this.a;
def <init>(): Test.type = {
Test.super.<init>();
Test.this.a = 2; //在初始化时才对成员赋值
()
}
}
}
在对多个变量赋值,或变量中有@符合,导致模式匹配还好理解,但”2=2″也引起模式匹配就会让我产生疑问:
是否所有的赋值语句都是模式匹配?
为了验证,通过编译选项查看val a=2
这样对单个变量的赋值却没有看到模式匹配。
另外,如果单个变量也是模式匹配,为何大写字母val A=2
没问题?假设对单个变量赋值也是模式匹配,那岂不无法定义大写的变量了;肯定是有区别的,但又怎么区分的?
我最初遇到这个困惑,在邮件列表里问了这个问题,得到了一些回复,并且有人给了一个老帖子链接说早就讨论过val 1=2
这个话题了:http://thread.gmane.org/gmane.comp.lang.scala.user/44036
在那个帖子里,martin也回复了为何 val 1=2
是模式匹配,并且为何不把这种情况作为错误给修复掉:
A value definition is of the form
val <pattern> = <expression> // 这个同ML和Erlang语言
1 is a <pattern>
There is one edge case:
If the pattern is a single variable (upper or lower case or backquoted), then it is always treated as a variable, not a constant. Otherwise, there would be no way to define such a value.只有一种边缘情况:如果模式是一个单独的变量(大写、小写、或用反引号引起来的),那么它总被当作变量,而非常量。否则就没法定义这样的一个值。
所以1=2
, "a"="b"
这样的赋值语句虽然是一个变量,但变量名称不符合上面的约束,产生了模式匹配。至于为何不修复这个问题(直接在编译时报错),也可以从这个帖子的线索中找到原因。
http://www.artima.com/scalazine/articles/pattern_matching.html
这篇文章是odersky谈scala中的模式匹配的一段对话,我做了部分片段翻译(不是连贯的):
模式可以嵌套,就像表达式嵌套,你可以定义深层的模式,通常一个模式看起来就像一个表达式。它基本上就是同一类事情。
它看起来像一个复杂的对象树构造表达式,只是漏掉了new关键字。事实上在scala当你构造一个对象,你不需要new关键字
然后你可以在一些地方用变量做站位符替代对象树上实际的东西。
本质上,当你需要通过外部来构造对象图,模式匹配是必要的,因为你不能对这些对象添加方法
有很多场景的例子,XML是一个,所有解析过的数据落入不同的分类。
举例,一个标准的场合是当你用编译器解析抽象语法树的时候模式匹配是必要的。 阅读全文
在这篇martin和另外两位模式匹配领域专家的论文里说了模式匹配的几种实现方式,以及scala是选择哪种方式来实现的。
http://lampwww.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf
我引用了里面的一些描述。
在面向对象的程序中数据被组织为一级一级的类(class)
面向对象语言在模式匹配方面的问题在于如何从外部探测这个层级。
有6种实现模式匹配的方法:
1) 面向对象的分解 (decomposition)
2) 访问器模式 (visitor)
3) 类型测试/类型造型 (type-test/type-cast)
4) typecase
5) 样本类 (case class)
6) 抽取器 (extractor) 阅读全文
一些刚从java转到scala的同学在开发的过程中犹如深陷沼泽,因为很多的概念或风格不确定,scala里有很多的坑,模式匹配也算一个。我整理了一下自己所理解的概念,以及一些例子。这个系列最好有些scala的基本经验,或者接触过一些其他函数式语言。
要理解模式匹配(pattern-matching),先把这两个单词拆开,先理解什么是模式(pattern),这里所的模式并不是设计模式里的模式。
而是数据结构上的,这个模式用于描述一个结构的组成。
我们很容易联想到“正则表达”里的模式,不错,这个pattern和正则里的pattern相似,不过适用范围更广,可以针对各种类型的数据结构,不像正则表达只是针对字符串。
原文地址:http://ifeve.com/dissecting-disruptor-wiring-up/
作者:Trisha 译者:廖涵 校对:方腾飞
现在我已经讲了 RingBuffer 本身,如何从它 读取 以及如何向它 写入。从逻辑上来说,下一件要做的事情就是把所有的东西拼装到在一起。
我前面提到过多生产者的情况——他们通过 ProducerBarrier 保证写入操作顺序与可控。我也提到过简单场景下的多消费者数据访问。更多的消费者的场景会变得更加复杂,我们 实现了一些聪明的机制允许多个消费者在访问 Ring Buffer 的时候互相等待(依赖)。像很多应用里,有一连串的工作需要在实际执行业务逻辑之前完成 (happen before) —— 例如,在做任何操作之前,我们都必须先保证消息写入磁盘。
Disruptor 论文 和性能测试里包含了你可能想到的一些基本结构。我准备讲一下其中最有趣的那个,这多半是因为我需要练习如何使用画图板。 阅读全文