新一代操作系统的构想

当一堆机器通过puppet、pssh这样的软件在管理的时候,你真想,如果它们共同运行一个操作系统,由那个操作系统统一替你协调这些机器该多好,而这个操作系统不能是openstack那种以机器(运行Linux、Windows这样的操作系统的环境)为单位的,而应该是以我的“程序”为单位的。软件架构,不能太过离散。

日志、定时器事件、用户、安装包……为什么不统一规划呢?以前的OS是面向单机的,现在的OS是面向群集的,这可能就是Elastos该出手的地方。

 | 196 views | 0 comments | 2 flags

Java为什么需要保留基本数据类型

http://www.importnew.com/11915.html

基本数据类型对以数值计算为主的应用程序来说是必不可少的。

自从1996年Java发布以来,基本数据类型就是Java语言的一部分。John Moore通过对使用基本类型和不使用基本类型做java基准测试给Java中为什么要保留基本数据类型做了一个很有力的说明。然后,他还在特定类型的应用中把Java和Scala、C++和JavaScript的性能做了对比。在这些应用中,使用基本数据类型应用性能会有很显著的不同。

问:影响买房最重要的三个因素是什么?
答:位置!位置!还是位置!!

这是个很古老但却经常被提及的谚语。意思是,当购买房产的时候,位置因素是绝对的主导因素。与此类似,考虑在Java中使用基本数据类型的3个最重要的因素,那就是性能!性能!还是性能!!房产与基本数据类型有两个不同之处。首先,位置主导几乎适用于所有买房的情况。但是使用基本类型带来的性能提升,对不同类型的应用程序来大不相同。其次,尽管其他的因素与位置相比在买房时都显得不太重要,但是也是应该被考虑的因素。而使用基本数据类型的原因却只有一个——那就是性能,并且只适用于那些使用后能提升性能的应用。

基本数据类型对大多数业务相关或网络应用程序没有太大的用处,这些应用一般是采用客户端/服务器模式,后端有数据库。然而,使用基本数据类型对那种以数值计算为主的应用提升性能有很大好处。

在Java语言中包含基本数据类型是最有争议的设计决定之一,关于这个决定讨论的文章和帖子比比皆是。2011年9月,Simon Ritter在JAX London上的演讲说,要认真考虑在Java未来的某个版本中删除基本数据类型。接下来,我会简要介绍基本数据类型和Java的双类型系统。通过示例代码和简单的基准测试说明为什么基本数据类型对于某些类型的应用程序是必须的。我也会对Java与Scala、C++和JavaScript性能做一些比较。

测量软件性能

软件性能经常用时间和空间来衡量。时间可以是实际的运行时间,比如3.7分钟,或者是基于输入规模的时间复杂度,比如O(n2)。对于空间的衡量也是如此。经常用内存消耗来衡量,有时候也会扩大到磁盘的使用。改善性能经常要做时间和空间的折衷,缩短时间经常会对空间造成损害,反之亦然。时间复杂度取决于算法,把包装类型切换成基本数据类型对结果不会有任何改变,但是使用基本数据类型取代对象类型能改善时间和空间的性能。

基本数据类型vs对象类型

当你阅读这篇文章的时候,可能已经知道了Java是双类型的系统,也就是基本数据类型和对象类型,简称基本类型和对象。Java中有8个预定义的基本类型,它们的名字都是保留的关键字。常见的基本类型有int、double和boolean。Java中所有其他的类型包括用户自定义的类型,它们必然也是对象类型(我说”必然”是因为数组类型有点例外,与基本类型比数组更像是对象类型)。每一个基本类型都有一个对应的对象包装类,比如int的包装类是Integer,double的包装类是Double,boolean的包装类是Boolean。

基本类型基于值,而对象类型则基于引用。与基本类型相关的争议都源于此。为了说明它们的不同,先来看一下两个声明语句。第一个语句使用的是基本类型,第二个使用的是包装类。

1
2
int n1 = 100;
Integer n2 = new Integer(100);

使用新添加到JDK5的特性自动装箱以后,第二个声明可以简化成:

1
Integer n2 = 100;

但是,底层的语义并没有发生改变。自动装箱简化了包装类的使用,减少了程序员的编码量,但是对运行时并没有任何的改变。

图1展示了基本类型n1和包装对象类型n2的区别。

 

图1. 基本类型vs对象类型的内存结构
图1. 基本类型vs对象类型的内存结构

 

n1持有一个整数的值,但是n2持有的是对一个对象的引用,即那个对象持有整数的值。除此之外,n2引用的对象也包含了一个对Double对象的引用。

基本数据类型存在的问题

在我试图说服你需要基本类型之前,首先我应该感谢不同意我观点的那些人。Sherman Alpert在”基本类型是有害的(Primitive types considered harmful)”这篇文章中说基本类型是有害的,因为“它们把函数式的语义混进了面向对象模型里面,让面向对象变得不纯。基本类型不是对象,但是它们却存在于以一流对象为根本的语言中”。基本类型和(包装类形式的)对象类型提供了两种处理逻辑上相似的类型的方式,但是在底层的语义上却有着非常大的不同。比如,两个实例如何来比较相等性?对于基本类型,使用==操作符,但是对于对象类型,更好的方式是调用equals()方法,而基本类型是没有这个操作的。相似的,在赋值和传参的语义上也是不同的。就连默认值也是不一样的,比如int的默认是值0,但是Integer的默认值是null。

关于这个话题的更多的背景可以参考Eric Bruno的博客”关于基本类型的讨论(A modern primitive discussion)”,里面总结了关于基本类型的正反两方面的意见。Stack Overflow上也有很多关于基本类型的讨论,包括”为什么人们仍然在Java中使用基本类型?(Why do people still use primitive types in Java?)”,”有没有只使用对象不使用基本类型的理由?(Is there a reason to always use Objects instead of primitives?)”。Programmers Stack Exchange上也有一个类似的叫做”Java中什么时候应该使用基本类型和对象类型?(When to use primitive vs class in Java?)”的讨论。

内存的使用

Java中的double总是占据内存的64个比特,但是引用类型的字节数取决于JVM。我的电脑运行64位Win7和64位JVM,因此在我的电脑上一个引用占用64个比特。根据图1,一个double比如n1要占用8个字节(64比特),一个Double比如n2要占用24个字节——对象的引用占8个字节,对象中的double的值占8个字节,对象中对Double对象的引用占8个字节。此外,Java需要使用额外的内存来支持对象的垃圾回收,但是基本类型不需要。下面让我们来验证下。

跟Glen McCluskey在”Java基本类型 VS 包装类型(Java primitive types vs. wrappers)”中使用的方式类似,列表1中的方法会测量一个n*n的double类型的矩阵(二维数组)所占的字节数。

列表1. 计算double类型的内存使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static long getBytesUsingPrimitives(int n)
  {
    System.gc();   // force garbage collection
    long memStart = Runtime.getRuntime().freeMemory();
    double[][] a = new double[n][n];
    // put some random values in the matrix
    for (int i = 0;  i < n;  ++i)
      {
        for (int j = 0; j < n;  ++j)
            a[i][j] = Math.random();
      }
    long memEnd = Runtime.getRuntime().freeMemory();
    return memStart - memEnd;
  }

修改列表1中的代码(没有列出来),改变矩阵的元素类型,我们也可以测量一个nn的Double类型的矩阵所占的字节数。在我的电脑上用10001000的矩阵来测试这两个方法,我得到了表1的结果。就像之前说的那样,基本类型版本的double矩阵中每一个元素占8个多字节,跟我预期的差不多,但是,对象类型版本的Double矩阵中每一个元素占28个字节还要多一点。因此,在这个例子中,Double的内存使用是double的3倍还要多,这对那些明白上面图1说的内存布局的人来说,并不是一件让人很吃惊的事情。

表1. double和Double的内存使用情况对比

版本 总字节数 平均字节数
使用double 8,380,768 8.381
使用Double 28,166,072 28.166

运行时性能

为了比较基本类型和对象类型的运行时性能,我们需要一个数值计算占主导的算法。本文中,我选择了矩阵相乘,然后计算1000*1000的矩阵相乘所需要的时间。我用一种很直观的方式来编码double类型的矩阵相乘,就像列表2中展示的那样。可能会有更快的方式来实现矩阵相乘(比如使用并发),但那个与本文无关。我需要的仅仅是两个很简单的方法,一个使用基本类型的double,另一个使用包装类Double。Double类型的矩阵相乘的代码跟列表2非常相似,仅仅是改了类型。

列表2. 两种类型的浮点数的矩阵相乘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static double[][] multiply(double[][] a, double[][] b)
  {
    if (!checkArgs(a, b))
        throw new IllegalArgumentException("Matrices not compatible for multiplication");
    int nRows = a.length;
    int nCols = b[0].length;
    double[][] result = new double[nRows][nCols];
    for (int rowNum = 0;  rowNum < nRows;  ++rowNum)
      {
        for (int colNum = 0;  colNum < nCols;  ++colNum)
          {
                double sum = 0.0;
                for (int i = 0;  i < a[0].length;  ++i)
                    sum += a[rowNum][i]*b[i][colNum];
                result[rowNum][colNum] = sum;
          }
      }
    return result;
  }

在我的计算机上分别用两个方法对两个1000*1000的矩阵做多次乘法,测量执行时间。表2中列出了平均时间。可以看出来,在本例中,double的运行时性能是Double的四倍。这是一个不能忽略的很大的不同!

版本 秒数
使用double 11.31
使用Double 48.48

SciMark2.0基准测试

迄今为止,我使用了一个简单的矩阵相乘的例子来说明基本类型比对象类型有更高的计算性能。为了让我的观点更站得住脚,我会用更科学的基准点(来做测试)。SciMark 2.0是NIST的用来测试科学和数值计算能力的Java基准工具。我下载了它的源码,创建了2个版本的基准测试,一个是使用基本类型,另一个使用包装类。第二个测试中我把int替换成了Integer,把double替换成了Double,这样来看包装类带来的影响。这两个版本在本文的源码中都可以找到。

Java基准测试: 源码下载

SciMark基准测试会测量许多种常见计算的性能,然后用大概的Mflops(每秒百万浮点运算数)给出一个综合的分数。因此,对这样的基准测试来说数据越大越好。表3给出了这个基准测试在我的计算机上对每个版本多次运行的平均综合分数。就像表中展示的那样,这两个版本的SciMark基准测试的运行时性能跟上面矩阵相乘的结果是一致的,基本类型的性能几乎比包装类型快了5倍。

表3. SciMark基准测试的运行时性能

SciMark版本 性能(Mflops)
使用double 710.80
使用Double 143.73

你已经见过使用自己的基准测试和一个更科学的方式对Java程序做数值计算的一些方式,但是,Java和其他语言比起来会怎样呢?看下Java和其他三种编程语言Scala,C++,JavaScript的性能比较,然后我会做出结论。

Scala基准测试

Scale是运行在JVM上的编程语言,貌似因此变得很流行。Scale有统一的类型系统,也就是说它不区分基本类型和对象类型。根据Erik Osheim在Scala的数值类型(第一部分)中说的,Scala在可能的情况下会使用基本类型,但是在必须的时候会使用对象类型(Scala uses primitive types when possible but will use objects if necessary)。与此相似,Martin Odersky对Scale数组的描述说:“Scala的Array[Int]数组对应Java当中的int[],Array[Double]对应Java当中的double[]”。

难道这意味着Scale的统一类型系统和Java的基本类型的运行时性能差不多?让我们来看一下。

Scala性能改善

当我两年前第一次使用Scala运行矩阵相乘的基准测试的时候,平均差不多要用超过33秒的时间,性能大概在Java基本类型和对象类型之间。最近我又用新版本的Scale重新编译一下,然后我被新版本的重大性能改善所震惊了。

我用Scale不像用Java那样熟练,但是我尝试把Java版本的矩阵相乘的基准测试直接转化成Scale版本。结果如下面列表3所示。当我在我的计算机上执行Scale版本的基准测试的时候,平均花费12.30秒,这个跟Java使用基本类型时候的性能非常接近。结果比我预期的要好很多,这个也给Scale声明的对数值类型的处理做了很好的证明。

Scala基准测试:源码下载

列表3. Scala语言实现的矩阵相乘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def multiply(a : Array[Array[Double]], b : Array[Array[Double]]) : Array[Array[Double]] =
  {
    if (!checkArgs(a, b))
        throw new IllegalArgumentException("Matrices not compatible for multiplication");
    val nRows : Int = a.length;
    val nCols : Int = b(0).length;
    var result = Array.ofDim[Double](nRows, nCols);
    for (rowNum <- 0 until nRows)
      {
        for (colNum <- 0 until nCols)
          {
            val sum : Double = 0.0;
            for (i <- 0  until a(0).length)
                sum += a(rowNum)(i)*b(i)(colNum);
            result(rowNum)(colNum) = sum;
          }
      }
    return result;
  }

代码转化的风险

就像James Roper指出的那样,当把一种语言直接转化成另一种语言的时候,总是存在风险的。因为缺少Scala的经验,使我意识到转换基准测试的代码有可能是可行的,这会强制Scala在运行时使用更高效的基本类型,同时还可以保留算法的基本特性。因为它用Java编写,所以折衷比较也是有意义的。

C++基准测试

因为C++是直接运行在物理机而非虚拟机上,大家自然会认为C++的速度比Java快。此外,为了确保索引是在数组声明的边界之内,Java会检查数组的访问,这对性能也有轻微的损害,C++不会做这样的检查(这是C++的一个特性,它可以导致缓冲区溢出,这可能会被黑客利用)。我发现,C++在处理基本的二维数组的时候多少有点尴尬,幸运的是,可以把这种尴尬隐藏到类内部的私有部分。我创建了一个简单的C++版本的Matrix类,重载了*操作符,用来做矩阵相乘,基本的矩阵相乘的算法是用Java版本直接转化过来的。列表4列出了C++的源码:

C++基准测试:源码下载

列表4.C++语言实现的矩阵相乘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Matrix operator*(const Matrix& m1, const Matrix& m2) throw(invalid_argument)
  {
    if (!Matrix::checkArgs(m1, m2))
          throw invalid_argument("matrices not compatible for multiplication");
    Matrix result(m1.nRows, m2.nCols);
    for (int i = 0;  i < result.nRows;  ++i)
      {
        for (int j = 0;  j < result.nCols;  ++j)
          {
            double sum = 0.0;
            for (int k = 0;  k < m1.nCols;  ++k)
                sum += m1.p[i][k]*m2.p[k][j];
            result.p[i][j] = sum;
          }
      }
    return result;
  }

使用Eclipse CDT和MinGW C++编译器可以创建调试版和正式版的应用程序。为了测试C++的性能,我是多次运行正式版取平均值。正如预期的那样,在这个简单的测试中C++很明显要快得多,在我的计算机上平均是7.58秒。如果性能是选择一个编程语言的主要因素的话,那么C++是数值运算密集型应用的首选。

JavaScript基准测试

好吧,这个测试的结果让我感到震惊。因为Javascript是一种动态语言,我本以为它的性能是最差的,甚至要比Java包装类的性能还要差。但是实际上,Javascript的性能跟Java使用基本类型的性能很接近。为了测试Javascript的性能,我安装了Node.js——它是一个以效率著称的Javascript引擎。测试的平均结果是15.91秒。列表5展示了运行在Node.js下Javascript版本的矩阵相乘基准测试:

JavaScript基准测试:源码下载

列表5. JavaScript语言实现的矩阵相乘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function multiply(a, b)
  {
    if (!checkArgs(a, b))
        throw ("Matrices not compatible for multiplication");
    var nRows = a.length;
    var nCols = b[0].length;
    var result = Array(nRows);
    for(var rowNum = 0;  rowNum < nRows; ++rowNum)
      {
        result[rowNum] = Array(nCols);
        for(var colNum = 0;  colNum < nCols;  ++colNum)
          {
            var sum = 0;
            for(var i = 0;  i < a[0].length;  ++i)
                sum += a[rowNum][i]*b[i][colNum];
            result[rowNum][colNum] = sum;
          }
      }
    return result;
  }

结论

当18年前Java第一次登上历史舞台的时候,对于以数值计算为主的应用程序从性能的角度来说,它并不是最好的语言。但时过境迁,随着其他领域技术的发展,比如即时编译(aka自适应或动态编译),当使用基本数据类型的时候,这类Java应用的性能已经可以匹敌编译成本地代码的那些语言。

并且,基本数据类型不需要垃圾回收,因此比对象类型多了另一个性能优势。表4总结了矩阵相乘基准测试在我的计算机上的运行时性能。还有其他的诸如可维护性可移植性和开发者擅长等因素让Java成为许多这类应用的很好地选择。

表4. 矩阵相乘基准测试的运行时性能

结果(以秒计)

C++ Java
(double)
Scala JavaScript Java
(Double)
7.58 11.31 12.30 15.91 48.48

就像前面讨论的那样,看上去Oracle似乎很严肃的考虑了是否在Java未来的版本中去掉基本数据类型。除非Java编译器能产生跟基本数据类型性能相当的代码,我认为把基本数据类型从Java中去掉会妨碍Java在某些类型应用中的使用,即那些以数值计算为主的应用。本文中,我对矩阵相乘做了基准测试,还用更科学的基准测试SciMark2.0来支持这一点。

附录

在本文发表在JavaWorld上几周以后,作者收到了来自Brian Goetz的邮件。Brian Goetz是Oracle的Java语言架构师,他说从Java中移除基本数据类型不在考虑范围之内。移除基本数据类型的说法源自于对Java未来版本的愿景讨论的误解或者是不准确的解释。

关于作者

John I. Moore, Jr.是Citadel的一位数学和计算机教授,他在工业领域和学术上都有很丰富的经验,在面向对象技术,软件工程和应用数学方面有独到的专长。在超过30年的时间里,他使用关系型数据库和很多高级语言来设计和开发软件,工作中广泛使用从1.1开始的Java的各个版本。他对计算机科学的很多高级话题都开设并教授了许多课程和研讨班。

了解更多:

1.Paul Krill在“Oracle的Java长期目标”写到Oracle对Java的长期的一些计划(JavaWorld,2012年3月)。那篇文章和相关的评论促使我写了这篇支持基本类型的文章。

Szymon Guz writes about his results in benchmarking primitive types and wrapper classes in “Primitives and objects benchmark in Java” (SimonOnSoftware, January 2011).
2.Szymon Guz在“Java中基本数据类型和对象类型的基准测试”(SimonOnSoftware,2011年1月)中写了对基本类型和包装类型做基准测试的结果。

3.C++编程准则和实践(Addison-Wesley, 2009)的支持站点上,C++的作者Bjarne提供了一个比本文要完善很多的矩阵类的实现。

4.John Rose,Brian Goetz和Guy Steele在“值的状态”一文中讨论了值的类型这个概念。值的类型可以被认为是没有标识的不变的用户自定义的类型集合,因此,可以把对象类型和基本类型的属性结合起来。值类型的好处是:像引用类型那样编码,像基本类型那样运行。

 | 99 views | 0 comments | 0 flags

jQuery的三击效果代码实现

http://www.gbtags.com/gb/share/3288.htm

大家可能常常需要在jQuery实现单机或者双击效果,是不是有时候需要三击效果呢?下面代码可以实现:

  1. $.event.special.tripleclick = {
  2. setup: function(data, namespaces) {
  3. var elem = this, $elem = jQuery(elem);
  4. $elem.bind(‘click’, jQuery.event.special.tripleclick.handler);
  5. },
  6. teardown: function(namespaces) {
  7. var elem = this, $elem = jQuery(elem);
  8. $elem.unbind(‘click’, jQuery.event.special.tripleclick.handler)
  9. },
  10. handler: function(event) {
  11. var elem = this, $elem = jQuery(elem), clicks = $elem.data(‘clicks’) || 0;
  12. clicks += 1;
  13. if ( clicks === 3 ) {
  14. clicks = 0;
  15. // set event type to “tripleclick”
  16. event.type = “tripleclick”;
  17. // let jQuery handle the triggering of “tripleclick” event handlers
  18. jQuery.event.handle.apply(this, arguments)
  19. }
  20. $elem.data(‘clicks’, clicks);
  21. }
  22. };

如何使用呢?

  1. $(“#whatever”).bind(“tripleclick”, function() {
  2. // do something
  3. }

是不是很好用? 别的地方搜刮来的代码,希望大家可以用的上!:D)

 | 134 views | 0 comments | 0 flags

程序的库设计

http://www.linuxeden.com/html/news/20140421/150949.html

最近在Stack Exchange上面看到一个帖子,是问程序库设计的指导原则的,“What guidelines should I follow while designing a library?”,有趣的是,很多人都在谈论面向设计,各路API设计,还有程序语言设计,唯独搜索“程序库设计”,无论中文还是英文,Google还是百度都找不到太多内容。但是我想,没有程序员会否认库设计的重要性吧,我想在这里结合这个帖子谈谈我的想法。

在这个帖子里面,votes最高的回答,提到了这样几类tips,我在下面简要叙述一下,其中基础的部分包括:

  • Pin Map,明确你期望库主要用来做什么,但不要把它定得太死,用户要可以比较方便地做出改变。
  • Working Library,一个工作的库,如果它连这点都达不到,一定要注明。没有人希望浪费时间在一个无法工作的程序库上面。
  • Basic Readme,清晰地描述库是用来做什么的,测试的情况等等。
  • Interfaces,接口必须清晰地定义,这可以帮助库的使用者。
  • Special Functions,特殊的功能,一定要注明,包括在readme文档中注明,以及在注释中注明。
  • Busy Waits,如果有一些场景需要使用busy wait(我不知道怎么翻译),其过程中可能会出现异常,使用interrupt或者其它妥善的方法来处理。
  • Comments,你做的任何的改变都要注释清楚,明确描述接口和其每个参数,方法是做什么的,又返回什么;如果有某个中间方法被调用到,就要注明。
  • Consistency,一致性,所有东西,包括注释。相关的方法要放在一个简单的代码文件里面,小但是逻辑一致。

其中的高级部分又包括Detailed Readme,Directory Structure,Licensing和Version Control。

这些都是需要注意的内容,并且大部分对于程序的库设计来说是基础要求,但是这些从重要性来说,并没有说到点上。《C++沉思录》里面有这样一句话:“库设计就是语言设计,语言设计就是库设计”,二者从先定义问题域到后解决问题的思路是类似的。我觉得比较重要的需要考虑的事情包括:

考虑库的目标用户。这听起来扯得有点泛,但实际上这是确切的问题,这是开源库还是你只是在小组内部使用的库,或者是公司内部使用的?用户的能力和需求是不一样的,要求当然不同。

要解决的核心问题。这是上文中Pin Map的一部分,不要重复发明轮子,那么每一个新库都有其存在的价值,这个问题既要通用又要具体,“通用”指的是库总有一个普适性,解决的实际问题对于不同的用户来说是不一样的;而“具体”是指库解决的问题对于程序员来说是非常清晰和直接的。例如设计一个库,根据某种规则把不同的数据类型(XML,BSON或者某种基于行的文本等等)都转换成JSON。

统一的编程风格。很多库都有自己精心设计的一套DSL,比如链式调用等等几种方式,当然,这也和使用的语言有关系。定义一种用起来舒服的编程风格对于程序库的推广是很有好处的。这也是一致性的一个体现。

内聚的调用入口。这和面向对象的“最少知识原则”有类似的地方,把那些不该暴露出去的库内部实现信息隐藏起来,在很多情况下,程序库不得不暴露和要求用户了解一些知识的时候,比如:

1
2
3
4
5
6
7
MappingConfig config = new MappingConfig();
config.put(MappingConfigConstants.ENCODE, "UTF-8");
FileBuilder fileBuilder = new StandardFileBuilder(mapping);
InputStream stream = fileBuilder.build().getInputStream();
DataTransformer<XMLNode> transformer = new XMLDataTransformer(...);
...
transformer.transform(stream);

这里引入了太多的概念,MappingConfig、FileBuilder、DataTransformer等等,整个过程大致是构造了一个数据源,还有一个数据转换器,然后这个数据转换器接受这个数据源来转换出最后结果的过程。那么:

这些象征着概念的接口和类最好以某种易于理解的形式组织起来,比如放在同一层比较浅的包里面,便于寻找;

也可以建立一个facade类,提供几种常用的组合,避免这些繁琐的对象构建和概念理解,例如:

1
XXFacade.buildCommonXMLTransformer();

向后兼容。当然,这一点也可以归纳到前文提到的一致性里面去。我曾经拿JDKHashTable举了一个例子,它的containsValue和contains方法其实是一样的,造成这种情况的一个原因就是为了保持向后兼容。

依赖管理。依赖管理很多情况下是一个脏活累活,但是却不得不考虑到。通常来说,任何一个库考虑自己的依赖库时都必须慎重,尤其是面对依赖的库需要升级的时候。如果依赖的库出了问题,自己设计的程序库也可能因此连累。

完善的测试用例。通常来说,程序库都配套有单元测试保证,无论是什么语言写的。

健全的文档组织。通常包括教程(tutorial)、开发者文档(developer guide)和接口API文档(API doc)。前者是帮助上手和建议使用的,中间的这个具备详尽的特性介绍,后者则是传统的API参考使用文档。

转自 http://blog.jobbole.com/65709/

 | 155 views | 0 comments | 0 flags

video post

this is a post with video

[jwplayer playlistid="185"]

 | 99 views | 0 comments | 0 flags

OAuth2 Authentication

http://developer.wordpress.com/docs/oauth2/

 

OAuth2 Authentication

 

OAuth2 is a protocol that allows applications to interact with blogs on WordPress.com and self-hosted WordPress sites running Jetpack.

The primary goal of OAuth is to allow developers to interact with WordPress.com and Jetpack sites without requiring them to store sensitive credentials. Our implementation also allows users to manage their own connections.

If you are new to the world of OAuth, you can read more at http://oauth.net.

If you are already familiar with OAuth, then all you really need to know about are the two authentication endpoints. The authorization endpoint and the token request endpoint.

These endpoints are

 

https://public-api.wordpress.com/oauth2/authorize

https://public-api.wordpress.com/oauth2/token

 

The same endpoints are used for WordPress.com blogs and Jetpack sites.

Before you begin to develop an application, you will need a client id and a client secret key. The client id and client secret key will be used to authenticate your application and verify that the API calls being are valid. You can sign up for an id and secret at ourapplications manager.

Receiving an Access Token

To act on a user’s behalf and make calls from our API you will need an access token. To get an access token you need to go through the access token flow and prompt the user to authorize your application to act on his or her behalf.

Access tokens are currently per blog per user for most of our endpoints. This means that you will need a separate access token for each blog that a user owns and that you want access to. There are certain endpoints like likes and follows where you can use a users token on any blog to act on their behalf.

To begin, you will need to send the user to the authorization endpoint.

https://public-api.wordpress.com/oauth2/authorize?client_id=your_client_id&redirect_uri=your_url&response_type=code

client_id should be set to your application’s client id. response_type should always be set to “code”. redirect_uri should be set to the URL that the user will be redirected back to after the request is authorized. The redirect_uri should be set in the applications manager.

The redirect to your application will include a code which you will need in the next step. If the user has denied access to your app, the redirect will include ?error=access_denied

Optionally you may also pass along a blog parameter (&blog=) with the URL to a WordPress.com blog or Jetpack site. If you do not pass along a URL, or if the user does not have administrative access to manage the blog you passed along, then the user will be prompted to select the blog they are granting you access to.

Once the user has authorized the request, he or she will be redirected to the redirect_url. The request will look like the following:

http://developer.wordpress.com/?code=cw9hk1xG9k

This is a time-limited code that your application can exchange for a full authorization token. To do this you will need to pass the code to the token endpoint by making a POSTrequest to the token endpoint: https://public-api.wordpress.com/oauth2/token.

1
2
3
4
5
6
7
8
9
10
11
12
13
curl_setopt( $curl, CURLOPT_POST, true );
curl_setopt( $curl, CURLOPT_POSTFIELDS, array(
    'client_id' => your_client_id,
    'redirect_uri' => your_redirect_url,
    'client_secret' => your_client_secret_key,
    'code' => $_GET['code'], // The code from the previous request
    'grant_type' => 'authorization_code'
) );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1);
$auth = curl_exec( $curl );
$secret = json_decode($auth);
$access_key = $secret->access_token;

You are required to pass client_id, client_secret, and redirect_uri for web applications. These parameters have to match the details for your application, and the redirect_urimust match the redirect_uri used during the Authorize step (above). grant_type has to be set to “authorization_code”. code must match the code you received in the redirect.

If everything works correctly and the user grants authorization, you will get back a JSON-encoded string containing the token and some basic information about the blog:

{ "access_token": "YOUR_API_TOKEN", "blog_id": "blog id", "blog_url": "blog url", "token_type": "bearer" }

You now have an access token which should be stored securely with the blog id and blog url. This access token allows our application to act on the behalf of the user on this specific blog.

Making an API Call

Our API is JSON based. You can view all of the available endpoints at our API documentation. You can also make API calls with our legacy XML-RPC API.

In order to make an authenticated call to your APIS, you need to include your access token with the call. OAuth2 uses a BEARER token that is passed along in an Authorization header.

1
2
3
4
5
6
<?php
$access_key = "YOUR_API_TOKEN";
curl_setopt( $curl, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $access_key ) );
curl_exec( $curl );
?>

The above example would return blog post ’43′ from our developer blog. You can make similar calls to the other available endpoints.

 | 255 views | 0 comments | 0 flags

关于MapReduce的思考

MapReduce为什么基本计算数据单元是<Key,Value>,因为它要通过Key来找计算结点,有了Key它才能Hash,才能让计算结点间负载均衡。
MapReduce通过把各计算结点的计算能力统一管理、协调,实现群集计算,这要求各计算结点间,无状态依赖,就是把你的输入处理完,给出输出,咱俩就没关系了。如果各计算结点是以class API的形式暴露的,就是Elastos了。Elastos在物联网模式,各计算结点耦合力比web强,比单机弱,就是这样的计算模型。
一个群集内,各计算结点都声明好自己的输入(供请求的API),同时再声明自己需要的API,就象是一个PE文件的导入、导出表,采用MapReduce这种思想,就构成了一个计算群集,参数传递自己序列化marshling/unmarshling,这样的OS有价值吗?Elastos就是这样的。

 | 196 views | 0 comments | 0 flags

【转】很多学习笔记而且整理得很好

1 index

1.1 Operating System

  • Computer System Reading 关于单机系统方面的一些文章以及阅读心得。
  • APUE Unix环境高级编程(Advanced Programming Unix Environment),W. Richard Stevens的神作。笔记里面没有包含书最后的几个部分,比如终端,打印机等,因为我觉得可能大家都不太需要这个东西了。我还尝试将一些跨章节的概念整合到一起,这样比较容易从总体把握Unix编程环境。
  • Linux 主要是介绍Linux下面一些工具使用以及和内核相关的知识。(将原来APUE 和 UNP 中的一部分内容放在这里面来了,这样可以保持这两篇内容比较稳定)
  • PIC 分析了一下PIC位置无关代码内部的原理以及和动态库之间的关系。通过阅读<深入理解计算机系统>并且结合实际的例子总结出来的。
  • GCC-Assembly 如何编写GCC内嵌汇编,以及一些关于GCC内嵌汇编的文章,主要是参考了<GCC Manual>. 但是自己对这个依然不是很了解。
  • Encoding 介绍了GB2312/GBK/GB18030/Unicode/UTF16/UTF32/UTF8这几种字符编码格式。还是觉得UTF8在设计以及实现上都相对更加合理。
  • CPU CPU相关
    • SIMD SIMD(Single Instruction Muitple Data)单指令多数据。这个笔记其实是Intel Reference Manual中关于SIMD指令的总结。里面包含了一些理解SIMD指令需要的知识,以及对SIMD指令进行了分类。遗憾的是里面没有什么过多的例子,毕竟这个是结合场景来使用的。
  • Concurrency 并发
  • Coroutine 协程,轻量级线程。
  • Lock 锁的原理与不同实现
  • Memory 内存相关
    • NUMA Non-Uniform Memory Access. 非一致性内存访问
    • Memory Barrier 内存屏障
    • TCMalloc Google的开源线程缓存内存分配器,解决多线程下面内存分配效率问题。
  • Continuation 延续,异步编程一种实现。
  • System-Peformance 系统性能相关
    • OProfile OProfile(系统级profiler)的原理和应用。不过说实话没有分析过源代码(或者是自己技术背景不行)终究觉得对这个东西理解不够深入,而且自己也仅仅是使用OProfile功能的子集。
    • SystemTap 通过将观察语句编译成为内核驱动,和linux内核提供的接口匹配,来深度地观察linux操作系统。
    • gperftools Google的性能分析工具,TCMalloc 实现也在里面。用来观察应用程序似乎是个不错的选择。
    • Perf Linux系统自带的性能分析工具,支持硬件以及软件事件计数器,支持profile kernel以及user code.
  • Akka Akka is a toolkit and runtime for building highly concurrent, distributed, and fault tolerant event-driven applications on the JVM.
  • Azkaban Linkedin的工作流系统,和 Oozie 功能相似但是相比好用很多。UI不错,概念也比较清晰。

1.2 Compression Technology

  • Snappy Google的开源压缩解压库。在满足一定压缩比率的条件下着重提升压缩和解压速度。
  • Lzf Redis 使用的开源压缩解压库。轻量(两个文件), 可以很容易地独立纳入项目。
  • Lzma Lempel-Ziv-Markov chain algorithm ,压缩速度相对较慢但是压缩比超高。

1.3 Tool and Desktop

  • Tool
    • License 一些常见的开源协议。不知道自己以后是否可以用得上:)
    • Web Misc Web开发遇到的一些问题。因为自己对于Web开发不太了解,所以内容上的话可能显得有点弱智。
    • SWIG C/C++多语言扩展接口生成器,使用起来非常方便(至于生成的代码没有看过效率如何).个人觉得比较适合quick & dirty的方案。
    • Git 分布式版本控制系统。很多项目都在使用Git进行版本管理包括Linux Kernel, Ruby on Rails, WINE, X.org等。
    • Folly Folly is an open-source C++ library developed and used at Facebook.
    • Flex/Bison 可以用来书写词法和语法分析器,Bison支持的语法是LALR(1)。
    • Valgrind CPU和内核模拟器,通过暴露接口可以构建动态分析工具,比如内存调试,检测内存泄露,多线程分析和性能分析等。
    • Protocol-Buffers Google的数据交换格式,能够高效地序列化和反序列化对象,同时考虑向后兼容问题。
    • Thrift Facebook的数据交换格式,能够高效地序列化和反序列化对象,同时考虑向后兼容问题。
    • GDB GNU Debugger, mainly on C/C++. 不过现在比较习惯打印日志方式来调试。
    • lcov the LTP(Linux Testing Project) GCOV extension.用来完成C/C++测试覆盖。
    • BuildSystem 在实现Baidu in-house的构建工具Comake2之前,做过构建系统的调研总结出来的。在语法表达上面(非常重要)借鉴了SCons,考虑了其他构建系统提供的功能。
      • Maven 主要针对Java开发的基于工程对象模型(POM, Project Object Model)构建系统,主要记录了一些使用方面的问题和解决办法。
      • SBT Simple(or Scala?) Build Tool
  • Desktop
    • Ubuntu 如何更好地使用Ubuntu. 之前有过痛苦的经历 。主要记录自己使用出现的问题。
    • FVWM F* Virtual Windows Manager.Linux下面高度可定制化的窗口管理器。
    • Macintosh 如何更好地使用Mac。以前使用Windows,后来转向Ubuntu,再后来买了个MBA. 熟悉它又需要一段时间了。
    • Emacs 如何更好地使用Emacs。自己依然只是使用一些简单功能,身边有很多牛人用Emacs那是相当的出神入化。
    • Eclipse 虽然我喜欢Emacs,但是似乎java方面开发似乎还是离不开它。
    • Intellij Eclipse太慢了,看看Intellij怎么样。

1.4 Algorithm Related

1.5 Network Programming

  • UNP Unix网络编程(Unix Network Programming),W. Richard Stevens的又一神作。笔记里面只是对于TCP原理有比较详细讨论,这部分笔记也包含了TCP Illustrated v1里面和TCP相关的章节内容。对于编程方面也着重TCP socket使用,毕竟在大部分应用场景下面我们选用TCP模型更多,并且TCP里面有很多非常琐碎的知识。
  • itachi 自己两天时间写完的异步网络编程框架,当然有很多地方需要改进,但是内核基本稳定了。并且在上面做了asocket封装,编写网络程序非常方便。TODO(dirlt):考虑写篇文章介绍一些
  • libev 开源的事件触发器。被认为是更高效的libevent. itachi 这个项目开始想自己编写事件触发器,但是发现工作比较琐碎枯燥,所以底层还是选择使用libev.
  • HPServer 开源的网络编程框架。可以当做一个入门级的网络编程框架阅读。这个是我最开始阅读的网络编程框架(1st).
  • NMSTL 开源的网络编程框架。一个很早期的作品,代码十分简洁。主要是内部实现了SEDA的思想。这个是我第二个阅读的网络编程框架(2nd).
  • Muduo 开源的网络编程框架。作者理想中的网络编程框架实现,里面有很多mina/netty的影子。这个是我三个阅读的网络编程框架(3rd).
  • Kylin Baidu in-house的异步编程框架,是linsd(百度首席架构师林仕鼎)的神作,通过阅读这个框架的代码让我理解了异步编程模型。这个是我第四个阅读的网络编程框架(4th).
  • ZeroMQ 开源的消息传输系统。颠覆我们思考和编写网络通信程序的方式。TODO(dirlt):只是阅读了文档和API,可以考虑阅读一下代码.
  • ACE 开源的网络编程框架。非常重量级,也被人诟病为学术产物而不是适合生产实践。TODO(dirlt):只是有大概的了解,有待更深入的研究.
  • Apache 历史悠久的开源HTTP服务器。 an effort to develop and maintain an open-source HTTP server for modern operating systems including UNIX and Windows NT
  • Netty an asynchronous event-driven network application framework in Java based on Java NIO.
  • Finagle an extensible RPC system for the JVM, used to construct high-concurrency servers.
  • HAProxy 高性能的负载均衡器,可以提供4(TCP),7(HTTP)层两种代理。

1.6 Storage System

  • Storage System Reading 关于存储系统方面的一些文章以及阅读心得。
    • Backblaze Storage Pod 构建廉价存储服务器的厂商。将其设计以及使用公开并且做了比较深入的说明。
  • DBMS DBMS(database management system)现在正在研究。打算首先阅读一下数据库系统基础教程(A First Course in Database Systems by Jeffrey D. Ullman),然后看看另外一本也是Jeffrey D. Ullman写的数据库系统实现(Database System Implementation).主要是了解DBMS方面的理论和大致实现,之后会稍微结合现有数据库实现阅读代码(MySQL/PostgreSQL).
  • LevelDB Google的开源kv存储系统。支持billion级别的数据量,适合于写少读多的情况。当时阅读的时候是从github上面clone下来的,可能还存相当多的bug.
  • MongoDB 面向文档的分布式存储系统,但是却可以针对文档字段索引来加快查询。功能上比通常我们认为的NoSQL强但是弱于RDBMS.
  • Redis 内存存储系统,支持丰富的数据类型以及相应的计算(支持持久化)。外围包装网络访问接口(并且提供了丰富的客户端),可以比较方便地在分布式系统内或者是环境下面使用.
  • MySQL 开源关系型数据库。The world’s most popular open source database.
  • SSD solid state disk.固态硬盘
  • RAID Redundant Array of Inexpensive Disk. 廉价磁盘冗余阵列
  • Memcached an in-memory key-value store for small chunks of arbitrary data (strings, objects) 可以用来搭建分布式缓存服务,没有持久化存储。
  • Gizzard a library for creating distributed datastores 可以认为是数据库中间层,完成partition/replication,也做fault-tolerant migration.
  • RocksDB A persistent key-value store for fast storage environments. 基于 LevelDB 构建,借鉴了 HBase 的思想。

1.7 Distributed System

1.8 Programming Language

  • Programming Language including following languages:
    • C/C++ # C++ (pronounced “cee plus plus”) is a statically typed, free-form, multi-paradigm, compiled, general-purpose programming language. It is regarded as an intermediate-level language, as it comprises a combination of both high-level and low-level language features. Wikipedia
    • Scheme # Scheme is a functional programming language and one of the two main dialects of the programming language Lisp. Wikipedia
    • Java # Java is a programming language originally developed by James Gosling at Sun Microsystems (which has since merged into Oracle Corporation) and released in 1995 as a core component of Sun Microsystems’ Java platform. The language derives much of its syntax from C and C++ but has a simpler object model and fewer low-level facilities. Wikipedia
      • JNI Java Native Interface
      • JVM Java Virtual Machine
    • Clojure # Clojure (pronounced like “closure”) is a recent dialect of the Lisp programming language created by Rich Hickey. It is a functional general-purpose language. Its focus on programming with immutable values and explicit progression-of-time constructs are intended to facilitate the development of more robust programs, particularly multithreaded ones. Wikipedia
    • Python # Python is a general-purpose, high-level programming language whose design philosophy emphasizes code readability. Its syntax is said to be clear and expressive. Python has a large and comprehensive standard library. Wikipedia
    • Go # Go is a compiled, garbage-collected, concurrent programming language developed by Google Inc. Wikipedia
    • Scala # Scala is an object-functional programming and scripting language for general software applications, statically typed, designed to concisely express solutions in an elegant, type-safe and lightweight (low ceremonial) manner. Wikipedia

1.9 Software Design

1.10 About Me

My name is Zhang Yan. I get MS. at Shandong University on Computer Science and BEng. at Shandong University on Electronic Engineering. I currently work as Software Architect in Data Platform Team at Umeng which focus on mobile analytics since 2012.5 where I manily work on building the infrastructure of big data processing. From 2010.6 to 2012.6, I have been a Senior Software Engineer at Baidu Infrastructure Tream where I wrote libraries, tools, services and distributed systems. From 2008.7 to 2010.6, I have been a Software Engineering Intern at Baidu Component Tream where I wrote libraries and tools. You can dig me more on my linkedin(or resume)

Here are my some notes written casually to record my life footprint. I think it will be fun to read them when I became old. Also there are some words on my blog

My code name is dirtysalt or dirlt. It’s translated from my chinese name. The translation works as following:

  • Zhang pronounce like ‘dirty’ in cn
  • Yan pronounce like in ‘salt’ in cn
  • then my code name is the combination of ‘dirty’ and ‘salt’ as ‘dirtysalt’
  • ‘dirtysalt’ can be abbreviated to ‘dirlt’.

Here is my contact info. I think email is the easiest way to get me.

My favouritest words comes from theidea of Valve : “Open your mind, Open your eyes” (放眼未来,自由想象), with a image from its game “Half Life”

Especially thanks to xuchaoqian for encouraging me to write and providing me host initially so I can publish my writings. And also thanks people who help me, support me, and accompany me, you complete me.

 | 217 views | 0 comments | 0 flags

【转】并发编程之内存屏障

http://hugozhu.myalert.info/2013/03/28/22-memory-barriers-or-fences.html

原文地址:http://mechanical-sympathy.blogspot.com/2011/07/memory-barriersfences.html 或http://ifeve.com/memory-barriersfences/

关键词:Load Barrier, Store Barrier, Full Barrier

 

目录:

 

 

 

本文我将和大家讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术。

CPU使用了很多优化技术来达成一个事实:CPU执行单元的速度要远超主存访问速度。在我上一篇文章 “Write Combing – 合并写”中我已经介绍了其中的一项技术。CPU避免内存访问延迟最常见的技术是将指令管道化,然后尽量重排这些管道的执行以最大利用缓存而把因为缓存未命中引起的延迟降到最小。

当一个程序执行时指令是否被重排并不重要,只要最终的结果是一样的。例如,在一个循环里,如果循环体内没用到这个计数器,循环的计数器什么时候更新(在循环开始,中间还是最后)并不重要。编译器和CPU可以自由的重排指令以最佳的利用CPU,只要下一次循环前更新该计数器即可。并且在循环执行中,这个变量可能一直存在寄存器上,并没有被推到缓存或主存,这样这个变量对其他CPU来说一直都是不可见的。

CPU核内部包含了多个执行单元。例如,现代Intel CPU包含了6个执行单元,可以组合进行算术运算,逻辑条件判断及内存操作。每个执行单元可以执行上述任务的某种组合。这些执行单元是并行执行的,这样指令也就是在并行执行。但如果站在另一个CPU角度看,这也就产生了程序顺序的另一种不确定性。

最后,当一个缓存失效发生时,现代CPU可以先假设一个内存载入的值并根据这个假设值继续执行,直到内存载入返回确切的值。

  1. CPU核
  2. |
  3. V
  4. 寄存器
  5. |
  6. V
  7. 执行单元 -> Load/Store缓冲区->L1 Cache --->L3 Cache-->内存控制器-->主存
  8. | |
  9. +-> Write Combine缓冲区->L2 Cache ---+

代码顺序并不是真正的执行顺序,CPU和编译器可以各种优化只要有空间提高性能。缓存和主存的读取会利用load, store和write-combining缓冲区来缓冲和重排。这些缓冲区是查找速度很快的关联队列,当一个后来发生的load需要读取上一个store的值,而该值还没有到达缓存,查找是必需的,上图描绘的是一个简化的现代多核CPU,从上图可以看出执行单元可以利用本地寄存器和缓冲区来管理和缓存子系统的交互。

在多线程环境里需要使用技术来使得程序结果尽快可见。这篇文章里我不会涉及到 Cache Conherence 的概念。请先假定一个事实:一旦内存数据被推送到缓存,就会有消息协议来确保所有的缓存会对所有的共享数据同步并保持一致。这个使内存数据对CPU核可见的技术被称为内存屏障或内存栅栏。

内存屏障提供了两个功能。首先,它们通过确保从另一个CPU来看屏障的两边的所有指令都是正确的程序顺序,而保持程序顺序的外部可见性;其次它们可以实现内存数据可见性,确保内存数据会同步到CPU缓存子系统。

大多数的内存屏障都是复杂的话题。在不同的CPU架构上内存屏障的实现非常不一样。相对来说Intel CPU的强内存模型比DEC Alpha的弱复杂内存模型(缓存不仅分层了,还分区了)更简单。因为x86处理器是在多线程编程中最常见的,下面我尽量用x86的架构来阐述。

Store Barrier

Store屏障,是x86的”sfence“指令,强制所有在store屏障指令之前的store指令,都在该store屏障指令执行之前被执行,并把store缓冲区的数据都刷到CPU缓存。这会使得程序状态对其它CPU可见,这样其它CPU可以根据需要介入。一个实际的好例子是Disruptor中的BatchEventProcessor。当序列Sequence被一个消费者更新时,其它消费者(Consumers)和生产者(Producers)知道该消费者的进度,因此可以采取合适的动作。所以屏障之前发生的内存更新都可见了。

  1. private volatile long sequence = RingBuffer.INITIAL_CURSOR_VALUE;
  2. // from inside the run() method
  3. T event = null;
  4. long nextSequence = sequence.get() + 1L;
  5. while (running)
  6. {
  7. try
  8. {
  9. final long availableSequence = barrier.waitFor(nextSequence);
  10. while (nextSequence <= availableSequence)
  11. {
  12. event = ringBuffer.get(nextSequence);
  13. boolean endOfBatch = nextSequence == availableSequence;
  14. eventHandler.onEvent(event, nextSequence, endOfBatch);
  15. nextSequence++;
  16. }
  17. sequence.set(nextSequence - 1L);
  18. // store barrier inserted here !!!
  19. }
  20. catch (final Exception ex)
  21. {
  22. exceptionHandler.handle(ex, nextSequence, event);
  23. sequence.set(nextSequence);
  24. // store barrier inserted here !!!
  25. nextSequence++;
  26. }
  27. }

Load Barrier

Load屏障,是x86上的”ifence“指令,强制所有在load屏障指令之后的load指令,都在该load屏障指令执行之后被执行,并且一直等到load缓冲区被该CPU读完才能执行之后的load指令。这使得从其它CPU暴露出来的程序状态对该CPU可见,这之后CPU可以进行后续处理。一个好例子是上面的BatchEventProcessor的sequence对象是放在屏障后被生产者或消费者使用。

Full Barrier

Full屏障,是x86上的”mfence“指令,复合了load和save屏障的功能。

Java内存模型

Java内存模型volatile变量在写操作之后会插入一个store屏障,在读操作之前会插入一个load屏障。类的final字段会在初始化后插入一个store屏障来确保final字段在构造函数完成可被使用时可见。

原子指令和Software Locks

原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。

内存屏障的性能影响

内存屏障阻碍了CPU采用优化技术来降低内存操作延迟,必须考虑因此带来的性能损失。为了达到最佳性能,最好是把要解决的问题模块化,这样处理器可以按单元执行任务,然后在任务单元的边界放上所有需要的内存屏障。采用这个方法可以让处理器不受限的执行一个任务单元。合理的内存屏障组合还有一个好处是:缓冲区在第一次被刷后开销会减少,因为再填充改缓冲区不需要额外工作了。

 | 184 views | 0 comments | 0 flags

SMP、NUMA、MPP体系结构介绍

http://www.cnblogs.com/yubo/archive/2010/04/23/1718810.html

从系统架构来看,目前的商用服务器大体可以分为三类,即对称多处理器结构 (SMP : Symmetric Multi-Processor) ,非一致存储访问结构 (NUMA : Non-Uniform Memory Access) ,以及海量并行处理结构 (MPP : Massive Parallel Processing) 。它们的特征分别描述如下:

1. SMP(Symmetric Multi-Processor)

SMP (Symmetric Multi Processing),对称多处理系统内有许多紧耦合多处理器,在这样的系统中,所有的CPU共享全部资源,如总线,内存和I/O系统等,操作系统或管理数据库的复本只有一个,这种系统有一个最大的特点就是共享所有资源。多个CPU之间没有区别,平等地访问内存、外设、一个操作系统。操作系统管理着一个队列,每个处理器依次处理队列中的进程。如果两个处理器同时请求访问一个资源(例如同一段内存地址),由硬件、软件的锁机制去解决资源争用问题。Access to RAM is serialized; this and cache coherency issues causes performance to lag slightly behind the number of additional processors in the system.

clip_image001

所谓对称多处理器结构,是指服务器中多个 CPU 对称工作,无主次或从属关系。各 CPU 共享相同的物理内存,每个 CPU 访问内存中的任何地址所需时间是相同的,因此 SMP 也被称为一致存储器访问结构 (UMA : Uniform Memory Access) 。对 SMP 服务器进行扩展的方式包括增加内存、使用更快的 CPU 、增加 CPU 、扩充 I/O( 槽口数与总线数 ) 以及添加更多的外部设备 ( 通常是磁盘存储 ) 。

SMP 服务器的主要特征是共享,系统中所有资源 (CPU 、内存、 I/O 等 ) 都是共享的。也正是由于这种特征,导致了 SMP 服务器的主要问题,那就是它的扩展能力非常有限。对于 SMP 服务器而言,每一个共享的环节都可能造成 SMP 服务器扩展时的瓶颈,而最受限制的则是内存。由于每个 CPU 必须通过相同的内存总线访问相同的内存资源,因此随着 CPU 数量的增加,内存访问冲突将迅速增加,最终会造成 CPU 资源的浪费,使 CPU 性能的有效性大大降低。实验证明, SMP 服务器 CPU 利用率最好的情况是 2 至 4 个 CPU 。

clip_image002

图 1.SMP 服务器 CPU 利用率状态

2. NUMA(Non-Uniform Memory Access)

由于 SMP 在扩展能力上的限制,人们开始探究如何进行有效地扩展从而构建大型系统的技术, NUMA 就是这种努力下的结果之一。利用 NUMA 技术,可以把几十个 CPU( 甚至上百个 CPU) 组合在一个服务器内。其 CPU 模块结构如图 2 所示:

clip_image003

图 2.NUMA 服务器 CPU 模块结构

NUMA 服务器的基本特征是具有多个 CPU 模块,每个 CPU 模块由多个 CPU( 如 4 个 ) 组成,并且具有独立的本地内存、 I/O 槽口等。由于其节点之间可以通过互联模块 ( 如称为 Crossbar Switch) 进行连接和信息交互,因此每个 CPU 可以访问整个系统的内存 ( 这是 NUMA 系统与 MPP 系统的重要差别 ) 。显然,访问本地内存的速度将远远高于访问远地内存 ( 系统内其它节点的内存 ) 的速度,这也是非一致存储访问 NUMA 的由来。由于这个特点,为了更好地发挥系统性能,开发应用程序时需要尽量减少不同 CPU 模块之间的信息交互。

利用 NUMA 技术,可以较好地解决原来 SMP 系统的扩展问题,在一个物理服务器内可以支持上百个 CPU 。比较典型的 NUMA 服务器的例子包括 HP 的 Superdome 、 SUN15K 、 IBMp690 等。

但 NUMA 技术同样有一定缺陷,由于访问远地内存的延时远远超过本地内存,因此当 CPU 数量增加时,系统性能无法线性增加。如 HP 公司发布 Superdome 服务器时,曾公布了它与 HP 其它 UNIX 服务器的相对性能值,结果发现, 64 路 CPU 的 Superdome (NUMA 结构 ) 的相对性能值是 20 ,而 8 路 N4000( 共享的 SMP 结构 ) 的相对性能值是 6.3 。从这个结果可以看到, 8 倍数量的 CPU 换来的只是 3 倍性能的提升。

3. MPP(Massive Parallel Processing)

和 NUMA 不同, MPP 提供了另外一种进行系统扩展的方式,它由多个 SMP 服务器通过一定的节点互联网络进行连接,协同工作,完成相同的任务,从用户的角度来看是一个服务器系统。其基本特征是由多个 SMP 服务器 ( 每个 SMP 服务器称节点 ) 通过节点互联网络连接而成,每个节点只访问自己的本地资源 ( 内存、存储等 ) ,是一种完全无共享 (Share Nothing) 结构,因而扩展能力最好,理论上其扩展无限制,目前的技术可实现 512 个节点互联,数千个 CPU 。目前业界对节点互联网络暂无标准,如 NCR 的 Bynet , IBM 的 SPSwitch ,它们都采用了不同的内部实现机制。但节点互联网仅供 MPP 服务器内部使用,对用户而言是透明的。

在 MPP 系统中,每个 SMP 节点也可以运行自己的操作系统、数据库等。但和 NUMA 不同的是,它不存在异地内存访问的问题。换言之,每个节点内的 CPU 不能访问另一个节点的内存。节点之间的信息交互是通过节点互联网络实现的,这个过程一般称为数据重分配 (Data Redistribution) 。

但是 MPP 服务器需要一种复杂的机制来调度和平衡各个节点的负载和并行处理过程。目前一些基于 MPP 技术的服务器往往通过系统级软件 ( 如数据库 ) 来屏蔽这种复杂性。举例来说, NCR 的 Teradata 就是基于 MPP 技术的一个关系数据库软件,基于此数据库来开发应用时,不管后台服务器由多少个节点组成,开发人员所面对的都是同一个数据库系统,而不需要考虑如何调度其中某几个节点的负载。

MPP (Massively Parallel Processing),大规模并行处理系统,这样的系统是由许多松耦合的处理单元组成的,要注意的是这里指的是处理单元而不是处理器。每个单元内的CPU都有自己私有的资源,如总线,内存,硬盘等。在每个单元内都有操作系统和管理数据库的实例复本。这种结构最大的特点在于不共享资源。

clip_image004

4. 三种体系架构之间的差异

4.1 SMP系统与MPP系统比较

既然有两种结构,那它们各有什么特点呢?采用什么结构比较合适呢?通常情况下,MPP系统因为要在不同处理单元之间传送信息(请注意上图),所以它的效率要比SMP要差一点,但是这也不是绝对的,因为MPP系统不共享资源,因此对它而言,资源比SMP要多,当需要处理的事务达到一定规模时,MPP的效率要比SMP好。这就是看通信时间占用计算时间的比例而定,如果通信时间比较多,那MPP系统就不占优势了,相反,如果通信时间比较少,那MPP系统可以充分发挥资源的优势,达到高效率。当前使用的OTLP程序中,用户访问一个中心数据库,如果采用SMP系统结构,它的效率要比采用MPP结构要快得多。而MPP系统在决策支持和数据挖掘方面显示了优势,可以这样说,如果操作相互之间没有什么关系,处理单元之间需要进行的通信比较少,那采用MPP系统就要好,相反就不合适了。

通过上面两个图我们可以看到,对于SMP来说,制约它速度的一个关键因素就是那个共享的总线,因此对于DSS程序来说,只能选择MPP,而不能选择SMP,当大型程序的处理要求大于共享总线时,总线就没有能力进行处理了,这时SMP系统就不行了。当然了,两个结构互有优缺点,如果能够将两种结合起来取长补短,当然最好了。
clip_image005
clip_image006

4.2 NUMA 与 MPP 的区别

从架构来看, NUMA 与 MPP 具有许多相似之处:它们都由多个节点组成,每个节点都具有自己的 CPU 、内存、 I/O ,节点之间都可以通过节点互联机制进行信息交互。那么它们的区别在哪里?通过分析下面 NUMA 和 MPP 服务器的内部架构和工作原理不难发现其差异所在。

首先是节点互联机制不同, NUMA 的节点互联机制是在同一个物理服务器内部实现的,当某个 CPU 需要进行远地内存访问时,它必须等待,这也是 NUMA 服务器无法实现 CPU 增加时性能线性扩展的主要原因。而 MPP 的节点互联机制是在不同的 SMP 服务器外部通过 I/O 实现的,每个节点只访问本地内存和存储,节点之间的信息交互与节点本身的处理是并行进行的。因此 MPP 在增加节点时性能基本上可以实现线性扩展。

其次是内存访问机制不同。在 NUMA 服务器内部,任何一个 CPU 可以访问整个系统的内存,但远地访问的性能远远低于本地内存访问,因此在开发应用程序时应该尽量避免远地内存访问。在 MPP 服务器中,每个节点只访问本地内存,不存在远地内存访问的问题。

clip_image007

图 3.MPP 服务器架构图

数据仓库的选择

哪种服务器更加适应数据仓库环境?这需要从数据仓库环境本身的负载特征入手。众所周知,典型的数据仓库环境具有大量复杂的数据处理和综合分析,要求系统具有很高的 I/O 处理能力,并且存储系统需要提供足够的 I/O 带宽与之匹配。而一个典型的 OLTP 系统则以联机事务处理为主,每个交易所涉及的数据不多,要求系统具有很高的事务处理能力,能够在单位时间里处理尽量多的交易。显然这两种应用环境的负载特征完全不同。

从 NUMA 架构来看,它可以在一个物理服务器内集成许多 CPU ,使系统具有较高的事务处理能力,由于远地内存访问时延远长于本地内存访问,因此需要尽量减少不同 CPU 模块之间的数据交互。显然, NUMA 架构更适用于 OLTP 事务处理环境,当用于数据仓库环境时,由于大量复杂的数据处理必然导致大量的数据交互,将使 CPU 的利用率大大降低。

相对而言, MPP 服务器架构的并行处理能力更优越,更适合于复杂的数据综合分析与处理环境。当然,它需要借助于支持 MPP 技术的关系数据库系统来屏蔽节点之间负载平衡与调度的复杂性。另外,这种并行处理能力也与节点互联网络有很大的关系。显然,适应于数据仓库环境的 MPP 服务器,其节点互联网络的 I/O 性能应该非常突出,才能充分发挥整个系统的性能。

4.3 NUMAMPPSMP之间性能的区别

 

NUMA的节点互联机制是在同一个物理服务器内部实现的,当某个CPU需要进行远地内存访问时,它必须等待,这也是NUMA服务器无法实现CPU增加时性能线性扩展。

MPP的节点互联机制是在不同的SMP服务器外部通过I/O实现的,每个节点只访问本地内存和存储,节点之间的信息交互与节点本身的处理是并行进行的。因此MPP在增加节点时性能基本上可以实现线性扩展。

SMP所有的CPU资源是共享的,因此完全实现线性扩展。

4.4 NUMA、MPP、SMP之间扩展的区别

 

NUMA理论上可以无限扩展,目前技术比较成熟的能够支持上百个CPU进行扩展。如HP的SUPERDOME。

MPP理论上也可以实现无限扩展,目前技术比较成熟的能够支持512个节点,数千个CPU进行扩展。

SMP扩展能力很差,目前2个到4个CPU的利用率最好,但是IBM的BOOK技术,能够将CPU扩展到8个。

MPP是由多个SMP构成,多个SMP服务器通过一定的节点互联网络进行连接,协同工作,完成相同的任务。

4.5 MPPSMPNUMA应用之间的区别

 

MPP的优势:

 

MPP系统不共享资源,因此对它而言,资源比SMP要多,当需要处理的事务达到一定规模时,MPP的效率要比SMP好。由于MPP系统因为要在不同处理单元之间传送信息,在通讯时间少的时候,那MPP系统可以充分发挥资源的优势,达到高效率。也就是说:操作相互之间没有什么关系,处理单元之间需要进行的通信比较少,那采用MPP系统就要好。因此,MPP系统在决策支持和数据挖掘方面显示了优势。

 

SMP的优势:

 

MPP系统因为要在不同处理单元之间传送信息,所以它的效率要比SMP要差一点。在通讯时间多的时候,那MPP系统可以充分发挥资源的优势。因此当前使用的OTLP程序中,用户访问一个中心数据库,如果采用SMP系统结构,它的效率要比采用MPP结构要快得多。

NUMA架构的优势:

 

NUMA架构来看,它可以在一个物理服务器内集成许多CPU,使系统具有较高的事务处理能力,由于远地内存访问时延远长于本地内存访问,因此需要尽量减少不同CPU模块之间的数据交互。显然,NUMA架构更适用于OLTP事务处理环境,当用于数据仓库环境时,由于大量复杂的数据处理必然导致大量的数据交互,将使CPU的利用率大大降低。

 | 310 views | 0 comments | 0 flags