diff --git a/atom.xml b/atom.xml index f3d6040..a31395a 100644 --- a/atom.xml +++ b/atom.xml @@ -1,16 +1,576 @@ - <![CDATA[My Octopress Blog]]> + <![CDATA[Homerun]]> - 2012-10-14T00:37:06+08:00 + 2012-10-14T01:14:55+08:00 http://dongw.github.com/ - + Octopress + + <![CDATA[Scala的structual type的确慢不少]]> + + 2012-03-13T01:12:00+08:00 + http://dongw.github.com/blog/2012/03/13/scala-structual-type-is-slow + Scala的Structual Type是用反射机制实现的。所以会慢一些。但我关心的是我经常用到的一个情况会慢多少。比如:

+ +
1
+2
+3
+4
+
type HasId = {
+  def id: Long
+}
+case class Foo(val id: Long)
+
+ + +

我经常会这样用:

+ +
1
+2
+3
+4
+5
+
def loop1(foos: Seq[HasId]) = {
+  var i = 0L
+  foos.foreach(f => i += f.id)
+  println(i)
+}
+
+ + +

这样所有有id的方法我都可以用这个loop1方法了。如果不用Structual Type,可以这样做:

+ +
1
+2
+3
+4
+5
+
def loop2(foos: Seq[Foo]) = {
+  var i = 0L
+  foos.foreach(f => i += f.id)
+  println(i)
+}
+
+ + +

在我的工作站上,如果给定一个10000000这么大的Seq[Foo],loop1用了550ms,loop2用了262ms。

+ +

结论

+ +

在这种情况下,用Structual Type的效率降低一半左右。

+]]>
+
+ + + <![CDATA[在Scala中如何避免导入implicit相关的定义]]> + + 2011-03-11T13:06:00+08:00 + http://dongw.github.com/blog/2011/03/11/avoid-explicit-importing-implicit-in-scala + Scala中的implicit能够让代码变的简洁很多。很多时候我们倾向于把这些implicit相关定义放到一个统一的地方,然后在各个package中应用。但每次用的时候,都需要做类是这样的import:

+ +
1
+
import com.readventure.MyImplicits._
+
+ + +

下面我告诉大家一个简单的方式,可以避免这种没有必要的imports。

+ +

方法

+ +

我们可以在com/readventure/下面定义下面这个trait:

+ +
1
+2
+3
+4
+5
+6
+
package com.readventure
+
+trait MyImplicits {
+  implicit def str2opt(s: String) = Option(s)
+  ...
+}
+
+ + +

然后在每个要用到这些implicit的包下面(比如com.readventure.test1)放置这样叫package.scala的文件(文件名不重要):

+ +
1
+2
+
package com.readventure  // not 'package.readventure.test1'
+package object test1 extends a.Implicits { /* your other stuff goes here */}
+
+ + +

注意,这里的package的名字,以及package object的名字很重要,必须和相对应的路径对应。这样在MyImplicits里面的所有东西在com.readventure.test1的任何一个类里面都可以用了,不用显式import任何东西。比如:

+ +
1
+2
+3
+4
+5
+
package com.readventure.test1
+
+class SomeClass {
+  def testImplicit(str: String): Option[String] = str
+}
+
+ + +

Package object是Scala2.8的新特性,如果有兴趣,可以看看这里

+]]>
+
+ + + <![CDATA[有关Scala的 @Specialized Annotation]]> + + 2011-03-11T11:00:00+08:00 + http://dongw.github.com/blog/2011/03/11/scala-annotation-specialized + @specialized 主要用在Scala的范型上,使得Scala编译器能够在编译的时候,为某些primative类型提供一个更加高效的实现。这种高效源于避免了对指定primative类型的boxing和unboxing(int -> Int -> int)[1]。根据一个相关研究[2],boxing和unboxing还是很花时间的。比如

+ +
1
+2
+3
+4
+5
+
class My [A] {
+        def iden(x: A): A = x
+}
+val a = new My[Int]
+for (i <- 1 to 1000000) a.iden(i)
+
+ + +

需要花40纳秒。改为:

+ +
1
+2
+3
+4
+5
+
class My [@specialized(Int) A] {
+    def iden(x: A): A = x
+}
+val a = new My[Int]
+for (i <- 1 to 1000000) a.iden(i)
+
+ + +

就只需要17纳秒。

+ +

下面看一个简单的用@specialized的例子:

+ +
1
+2
+3
+
class Example[@specialized(Int) T](value: T) {
+  def get(): T = value
+}
+
+ + +

用命令scalac -print test.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
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala 
+    package <empty> {
+      class Example extends java.lang.Object with ScalaObject {
+        <paramaccessor> protected[this] val value: java.lang.Object = _;
+        def get(): java.lang.Object = Example.this.value;
+        <specialized> def get$mcI$sp(): Int = scala.Int.unbox(Example.this.get());
+        def this(value: java.lang.Object): Example = {
+          Example.this.value = value;
+          Example.super.this();
+          ()
+        }
+      };
+      <specialized> class Example$mcI$sp extends Example {
+        <paramaccessor> <specialized> protected[this] val value$mcI$sp: Int = _;
+        override <specialized> def get(): Int = Example$mcI$sp.this.get$mcI$sp();
+        override <specialized> def get$mcI$sp(): Int = Example$mcI$sp.this.value$mcI$sp;
+        override <bridge> <specialized> def get(): java.lang.Object = scala.Int.box(Example$mcI$sp.this.get());
+        <specialized> def this(value$mcI$sp: Int): Example$mcI$sp = {
+          Example$mcI$sp.this.value$mcI$sp = value$mcI$sp;
+          Example$mcI$sp.super.this(scala.Int.box(value$mcI$sp));
+          ()
+        }
+      }
+    }
+
+ + +

可见有两个类被生成了。如果把源文件变为:

+ +
1
+2
+3
+
class Example[@specialized(Int, Long) T](value: T) {
+  def get(): T = value
+}
+
+ + +

则有第三个类被生成:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala
+    package <empty> {
+      ...
+      ...
+      <specialized> class Example$mcJ$sp extends Example {
+        <paramaccessor> <specialized> protected[this] val value$mcJ$sp: Long = _;
+        override <specialized> def get(): Long = Example$mcJ$sp.this.get$mcJ$sp();
+        override <specialized> def get$mcJ$sp(): Long = Example$mcJ$sp.this.value$mcJ$sp;
+        override <bridge> <specialized> def get(): java.lang.Object = scala.Long.box(Example$mcJ$sp.this.get());
+        <specialized> def this(value$mcJ$sp: Long): Example$mcJ$sp = {
+          Example$mcJ$sp.this.value$mcJ$sp = value$mcJ$sp;
+          Example$mcJ$sp.super.this(scala.Long.box(value$mcJ$sp));
+          ()
+        }
+      }
+    }
+
+ + +


+理想情况是很多范型都对所有primative类型做specialized,不过这样编译后的代码就会成倍的增加了。Scala 2.8.1中只有下面的类有用到@specialized[3]: +Function0, Function1, Function2, Tuple1, Tuple2, Product1, Product2, AbstractFunction0, AbstractFunction1, AbstractFunction2.

+]]>
+
+ + + <![CDATA[Scala的循环可能很慢!]]> + + 2011-03-10T10:39:00+08:00 + http://dongw.github.com/blog/2011/03/10/careful-with-scala-for-loop + 下面这Java段代码只要90纳秒[1]:

+ +
1
+2
+3
+4
+5
+
int s = 0
+for (int i = 1; i <= 2000; i++)
+  for (int j = 1; j <= 2000; j++)
+    for (int k = 1; k <= 2000; k++)
+      s += 1
+
+ + +

而相对应的Scala则是需要1.3秒

+ +
1
+2
+3
+4
+5
+
var s = 0
+for (i <- 1 to 2000;
+     j <- 1 to 2000;
+     k <- 1 to 2000)
+  s += 1
+
+ + +

同样,下面的代码和上面的完全等价:

+ +
1
+2
+3
+4
+5
+
(1 to 2000).foreach(
+  i => (1 to 2000).foreach(
+    j => (1 to 2000).foreach(k => s += 1)
+  )
+)
+
+ + +

改为简单Scala for循环就可以快到60纳秒:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+
var s = 0
+var i = 1; var j = 1; var k = 1
+
+while (i <= 2000) { j = 1
+  while (j <= 2000) { k = 1
+    while (k <= 2000) { k += 1 }
+    j += 1 }
+  i += 1 }
+
+ + +

想看看Scala的for循环可能有多可怕?如果我们编译:

+ +
1
+2
+3
+
object Test {
+  def test() {}
+}
+
+ + +

我们得到以下bytecode:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala
+    package <empty> {
+      final object Test extends java.lang.Object with ScalaObject {
+        def test(): Unit = ();
+        def this(): object Test = {
+          Test.super.this();
+          ()
+        }
+      }
+    }
+
+ + +

但是如果我们加了第一段代码到test方法中,我们就可以发现编译的结果中overhead是多么大:

+ +
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
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala
+    package <empty> {
+      final object Test extends java.lang.Object with ScalaObject {
+        def test(): Unit = {
+          var s$1: scala.runtime.IntRef = new scala.runtime.IntRef(0);
+          scala.this.Predef.intWrapper(1).to(2000).foreach$mVc$sp({
+            (new anonymous class Test$$anonfun$test$1(s$1): Function1)
+          })
+        };
+        def this(): object Test = {
+          Test.super.this();
+          ()
+        }
+      };
+      @SerialVersionUID(0) final <synthetic> class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2 extends scala.runtime.AbstractFunction1$mcVI$sp with Serializable {
+        final def apply(k: Int): Unit = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.apply$mcVI$sp(k);
+        <specialized> def apply$mcVI$sp(v1: Int): Unit = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.$outer .Test$$anonfun$$anonfun$$$outer().s$1.elem = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.$outer .Test$$anonfun$$anonfun$$$outer().s$1.elem.+(1);
+        <synthetic> <paramaccessor> private[this] val $outer: anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1 = _;
+        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = {
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.apply(scala.Int.unbox(v1));
+          scala.runtime.BoxedUnit.UNIT
+        };
+        def this($outer: anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1): anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2 = {
+          if ($outer.eq(null))
+            throw new java.lang.NullPointerException()
+          else
+            Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.$outer = $outer;
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.super.this();
+          ()
+        }
+      };
+      @SerialVersionUID(0) final <synthetic> class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1 extends scala.runtime.AbstractFunction1$mcVI$sp with Serializable {
+        final def apply(j: Int): Unit = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.apply$mcVI$sp(j);
+        <specialized> def apply$mcVI$sp(v1: Int): Unit = scala.this.Predef.intWrapper(1).to(2000).foreach$mVc$sp({
+          (new anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2(Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this): Function1)
+        });
+        <synthetic> <paramaccessor> private[this] val $outer: anonymous class Test$$anonfun$test$1 = _;
+        <synthetic> <stable> def Test$$anonfun$$anonfun$$$outer(): anonymous class Test$$anonfun$test$1 = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.$outer;
+        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = {
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.apply(scala.Int.unbox(v1));
+          scala.runtime.BoxedUnit.UNIT
+        };
+        def this($outer: anonymous class Test$$anonfun$test$1): anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1 = {
+          if ($outer.eq(null))
+            throw new java.lang.NullPointerException()
+          else
+            Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.$outer = $outer;
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.super.this();
+          ()
+        }
+      };
+      @SerialVersionUID(0) final <synthetic> class Test$$anonfun$test$1 extends scala.runtime.AbstractFunction1$mcVI$sp with Serializable {
+        final def apply(i: Int): Unit = Test$$anonfun$test$1.this.apply$mcVI$sp(i);
+        <specialized> def apply$mcVI$sp(v1: Int): Unit = scala.this.Predef.intWrapper(1).to(2000).foreach$mVc$sp({
+          (new anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1(Test$$anonfun$test$1.this): Function1)
+        });
+        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = {
+          Test$$anonfun$test$1.this.apply(scala.Int.unbox(v1));
+          scala.runtime.BoxedUnit.UNIT
+        };
+        <synthetic> <paramaccessor> val s$1: scala.runtime.IntRef = _;
+        def this(s$1: scala.runtime.IntRef): anonymous class Test$$anonfun$test$1 = {
+          Test$$anonfun$test$1.this.s$1 = s$1;
+          Test$$anonfun$test$1.super.this();
+          ()
+        }
+      }
+    }
+  
+
+ + +

结论

+ +

在scala没有解决相关的bug #1138前,还是小心用它的for循环,包括foreach。

+ +

一点更新:

+ +

我在自己的工作站上测试了下面两端代码的运行时间:

+ +

三层循环,耗时7570毫秒:

+ +
1
+2
+3
+4
+5
+
var s = 0
+for (i <- 1 to 2000;
+     j <- 1 to 2000;
+     k <- 1 to 2000)
+  s += 1
+
+ + +

一层循环,耗时1毫秒:

+ +
1
+2
+3
+
var s = 0
+for (i <- 1 to 2000*2000*2000)
+  s += 1
+
+ + +

看来对于单层循环,scala的效率还可以接受。

+]]>
+
+
diff --git a/blog/2011/03/10/careful-with-scala-for-loop/index.html b/blog/2011/03/10/careful-with-scala-for-loop/index.html new file mode 100644 index 0000000..d516bab --- /dev/null +++ b/blog/2011/03/10/careful-with-scala-for-loop/index.html @@ -0,0 +1,508 @@ + + + + + + + + Scala的循环可能很慢! - Homerun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

Homerun

+ +

My name is Dong Wang, a X-Googler and Scala enthusiast.

+ +
+
+ +
+ +
+
+
+
+ +
+ + +

Scala的循环可能很慢!

+ + + +

+ + + + + + + + + + + + + + +

+ +
+ + +

下面这Java段代码只要90纳秒[1]:

+ +
1
+2
+3
+4
+5
+
int s = 0
+for (int i = 1; i <= 2000; i++)
+  for (int j = 1; j <= 2000; j++)
+    for (int k = 1; k <= 2000; k++)
+      s += 1
+
+ + +

而相对应的Scala则是需要1.3秒

+ +
1
+2
+3
+4
+5
+
var s = 0
+for (i <- 1 to 2000;
+     j <- 1 to 2000;
+     k <- 1 to 2000)
+  s += 1
+
+ + +

同样,下面的代码和上面的完全等价:

+ +
1
+2
+3
+4
+5
+
(1 to 2000).foreach(
+  i => (1 to 2000).foreach(
+    j => (1 to 2000).foreach(k => s += 1)
+  )
+)
+
+ + +

改为简单Scala for循环就可以快到60纳秒:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+
var s = 0
+var i = 1; var j = 1; var k = 1
+
+while (i <= 2000) { j = 1
+  while (j <= 2000) { k = 1
+    while (k <= 2000) { k += 1 }
+    j += 1 }
+  i += 1 }
+
+ + +

想看看Scala的for循环可能有多可怕?如果我们编译:

+ +
1
+2
+3
+
object Test {
+  def test() {}
+}
+
+ + +

我们得到以下bytecode:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala
+    package <empty> {
+      final object Test extends java.lang.Object with ScalaObject {
+        def test(): Unit = ();
+        def this(): object Test = {
+          Test.super.this();
+          ()
+        }
+      }
+    }
+
+ + +

但是如果我们加了第一段代码到test方法中,我们就可以发现编译的结果中overhead是多么大:

+ +
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
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala
+    package <empty> {
+      final object Test extends java.lang.Object with ScalaObject {
+        def test(): Unit = {
+          var s$1: scala.runtime.IntRef = new scala.runtime.IntRef(0);
+          scala.this.Predef.intWrapper(1).to(2000).foreach$mVc$sp({
+            (new anonymous class Test$$anonfun$test$1(s$1): Function1)
+          })
+        };
+        def this(): object Test = {
+          Test.super.this();
+          ()
+        }
+      };
+      @SerialVersionUID(0) final <synthetic> class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2 extends scala.runtime.AbstractFunction1$mcVI$sp with Serializable {
+        final def apply(k: Int): Unit = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.apply$mcVI$sp(k);
+        <specialized> def apply$mcVI$sp(v1: Int): Unit = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.$outer .Test$$anonfun$$anonfun$$$outer().s$1.elem = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.$outer .Test$$anonfun$$anonfun$$$outer().s$1.elem.+(1);
+        <synthetic> <paramaccessor> private[this] val $outer: anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1 = _;
+        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = {
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.apply(scala.Int.unbox(v1));
+          scala.runtime.BoxedUnit.UNIT
+        };
+        def this($outer: anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1): anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2 = {
+          if ($outer.eq(null))
+            throw new java.lang.NullPointerException()
+          else
+            Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.this.$outer = $outer;
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2.super.this();
+          ()
+        }
+      };
+      @SerialVersionUID(0) final <synthetic> class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1 extends scala.runtime.AbstractFunction1$mcVI$sp with Serializable {
+        final def apply(j: Int): Unit = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.apply$mcVI$sp(j);
+        <specialized> def apply$mcVI$sp(v1: Int): Unit = scala.this.Predef.intWrapper(1).to(2000).foreach$mVc$sp({
+          (new anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1$$anonfun$apply$mcVI$sp$2(Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this): Function1)
+        });
+        <synthetic> <paramaccessor> private[this] val $outer: anonymous class Test$$anonfun$test$1 = _;
+        <synthetic> <stable> def Test$$anonfun$$anonfun$$$outer(): anonymous class Test$$anonfun$test$1 = Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.$outer;
+        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = {
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.apply(scala.Int.unbox(v1));
+          scala.runtime.BoxedUnit.UNIT
+        };
+        def this($outer: anonymous class Test$$anonfun$test$1): anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1 = {
+          if ($outer.eq(null))
+            throw new java.lang.NullPointerException()
+          else
+            Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.this.$outer = $outer;
+          Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1.super.this();
+          ()
+        }
+      };
+      @SerialVersionUID(0) final <synthetic> class Test$$anonfun$test$1 extends scala.runtime.AbstractFunction1$mcVI$sp with Serializable {
+        final def apply(i: Int): Unit = Test$$anonfun$test$1.this.apply$mcVI$sp(i);
+        <specialized> def apply$mcVI$sp(v1: Int): Unit = scala.this.Predef.intWrapper(1).to(2000).foreach$mVc$sp({
+          (new anonymous class Test$$anonfun$test$1$$anonfun$apply$mcVI$sp$1(Test$$anonfun$test$1.this): Function1)
+        });
+        final <bridge> def apply(v1: java.lang.Object): java.lang.Object = {
+          Test$$anonfun$test$1.this.apply(scala.Int.unbox(v1));
+          scala.runtime.BoxedUnit.UNIT
+        };
+        <synthetic> <paramaccessor> val s$1: scala.runtime.IntRef = _;
+        def this(s$1: scala.runtime.IntRef): anonymous class Test$$anonfun$test$1 = {
+          Test$$anonfun$test$1.this.s$1 = s$1;
+          Test$$anonfun$test$1.super.this();
+          ()
+        }
+      }
+    }
+  
+
+ + +

结论

+ +

在scala没有解决相关的bug #1138前,还是小心用它的for循环,包括foreach。

+ +

一点更新:

+ +

我在自己的工作站上测试了下面两端代码的运行时间:

+ +

三层循环,耗时7570毫秒:

+ +
1
+2
+3
+4
+5
+
var s = 0
+for (i <- 1 to 2000;
+     j <- 1 to 2000;
+     k <- 1 to 2000)
+  s += 1
+
+ + +

一层循环,耗时1毫秒:

+ +
1
+2
+3
+
var s = 0
+for (i <- 1 to 2000*2000*2000)
+  s += 1
+
+ + +

看来对于单层循环,scala的效率还可以接受。

+
+ + + +
+ +
+ + + + +
+
+ + + + + + + + + + + + + + + + + diff --git a/blog/2011/03/11/avoid-explicit-importing-implicit-in-scala/index.html b/blog/2011/03/11/avoid-explicit-importing-implicit-in-scala/index.html new file mode 100644 index 0000000..30c9268 --- /dev/null +++ b/blog/2011/03/11/avoid-explicit-importing-implicit-in-scala/index.html @@ -0,0 +1,278 @@ + + + + + + + + 在Scala中如何避免导入implicit相关的定义 - Homerun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

Homerun

+ +

My name is Dong Wang, a X-Googler and Scala enthusiast.

+ +
+
+ +
+ +
+
+
+
+ +
+ + +

在Scala中如何避免导入implicit相关的定义

+ + + +

+ + + + + + + + + + + + + + +

+ +
+ + +

Scala中的implicit能够让代码变的简洁很多。很多时候我们倾向于把这些implicit相关定义放到一个统一的地方,然后在各个package中应用。但每次用的时候,都需要做类是这样的import:

+ +
1
+
import com.readventure.MyImplicits._
+
+ + +

下面我告诉大家一个简单的方式,可以避免这种没有必要的imports。

+ +

方法

+ +

我们可以在com/readventure/下面定义下面这个trait:

+ +
1
+2
+3
+4
+5
+6
+
package com.readventure
+
+trait MyImplicits {
+  implicit def str2opt(s: String) = Option(s)
+  ...
+}
+
+ + +

然后在每个要用到这些implicit的包下面(比如com.readventure.test1)放置这样叫package.scala的文件(文件名不重要):

+ +
1
+2
+
package com.readventure  // not 'package.readventure.test1'
+package object test1 extends a.Implicits { /* your other stuff goes here */}
+
+ + +

注意,这里的package的名字,以及package object的名字很重要,必须和相对应的路径对应。这样在MyImplicits里面的所有东西在com.readventure.test1的任何一个类里面都可以用了,不用显式import任何东西。比如:

+ +
1
+2
+3
+4
+5
+
package com.readventure.test1
+
+class SomeClass {
+  def testImplicit(str: String): Option[String] = str
+}
+
+ + +

Package object是Scala2.8的新特性,如果有兴趣,可以看看这里

+
+ + + +
+ +
+ + + + +
+
+ + + + + + + + + + + + + + + + + diff --git a/blog/2011/03/11/scala-annotation-specialized/index.html b/blog/2011/03/11/scala-annotation-specialized/index.html new file mode 100644 index 0000000..0d9c9bd --- /dev/null +++ b/blog/2011/03/11/scala-annotation-specialized/index.html @@ -0,0 +1,371 @@ + + + + + + + + 有关Scala的 @Specialized Annotation - Homerun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

Homerun

+ +

My name is Dong Wang, a X-Googler and Scala enthusiast.

+ +
+
+ +
+ +
+
+
+
+ +
+ + +

有关Scala的 @Specialized Annotation

+ + + +

+ + + + + + + + + + + + + + +

+ +
+ + +

@specialized 主要用在Scala的范型上,使得Scala编译器能够在编译的时候,为某些primative类型提供一个更加高效的实现。这种高效源于避免了对指定primative类型的boxing和unboxing(int -> Int -> int)[1]。根据一个相关研究[2],boxing和unboxing还是很花时间的。比如

+ +
1
+2
+3
+4
+5
+
class My [A] {
+        def iden(x: A): A = x
+}
+val a = new My[Int]
+for (i <- 1 to 1000000) a.iden(i)
+
+ + +

需要花40纳秒。改为:

+ +
1
+2
+3
+4
+5
+
class My [@specialized(Int) A] {
+    def iden(x: A): A = x
+}
+val a = new My[Int]
+for (i <- 1 to 1000000) a.iden(i)
+
+ + +

就只需要17纳秒。

+ +

下面看一个简单的用@specialized的例子:

+ +
1
+2
+3
+
class Example[@specialized(Int) T](value: T) {
+  def get(): T = value
+}
+
+ + +

用命令scalac -print test.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
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala 
+    package <empty> {
+      class Example extends java.lang.Object with ScalaObject {
+        <paramaccessor> protected[this] val value: java.lang.Object = _;
+        def get(): java.lang.Object = Example.this.value;
+        <specialized> def get$mcI$sp(): Int = scala.Int.unbox(Example.this.get());
+        def this(value: java.lang.Object): Example = {
+          Example.this.value = value;
+          Example.super.this();
+          ()
+        }
+      };
+      <specialized> class Example$mcI$sp extends Example {
+        <paramaccessor> <specialized> protected[this] val value$mcI$sp: Int = _;
+        override <specialized> def get(): Int = Example$mcI$sp.this.get$mcI$sp();
+        override <specialized> def get$mcI$sp(): Int = Example$mcI$sp.this.value$mcI$sp;
+        override <bridge> <specialized> def get(): java.lang.Object = scala.Int.box(Example$mcI$sp.this.get());
+        <specialized> def this(value$mcI$sp: Int): Example$mcI$sp = {
+          Example$mcI$sp.this.value$mcI$sp = value$mcI$sp;
+          Example$mcI$sp.super.this(scala.Int.box(value$mcI$sp));
+          ()
+        }
+      }
+    }
+
+ + +

可见有两个类被生成了。如果把源文件变为:

+ +
1
+2
+3
+
class Example[@specialized(Int, Long) T](value: T) {
+  def get(): T = value
+}
+
+ + +

则有第三个类被生成:

+ +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
    [[syntax trees at end of cleanup]]// Scala source: test.scala
+    package <empty> {
+      ...
+      ...
+      <specialized> class Example$mcJ$sp extends Example {
+        <paramaccessor> <specialized> protected[this] val value$mcJ$sp: Long = _;
+        override <specialized> def get(): Long = Example$mcJ$sp.this.get$mcJ$sp();
+        override <specialized> def get$mcJ$sp(): Long = Example$mcJ$sp.this.value$mcJ$sp;
+        override <bridge> <specialized> def get(): java.lang.Object = scala.Long.box(Example$mcJ$sp.this.get());
+        <specialized> def this(value$mcJ$sp: Long): Example$mcJ$sp = {
+          Example$mcJ$sp.this.value$mcJ$sp = value$mcJ$sp;
+          Example$mcJ$sp.super.this(scala.Long.box(value$mcJ$sp));
+          ()
+        }
+      }
+    }
+
+ + +


+理想情况是很多范型都对所有primative类型做specialized,不过这样编译后的代码就会成倍的增加了。Scala 2.8.1中只有下面的类有用到@specialized[3]: +Function0, Function1, Function2, Tuple1, Tuple2, Product1, Product2, AbstractFunction0, AbstractFunction1, AbstractFunction2.

+
+ + + +
+ +
+ + + + +
+
+ + + + + + + + + + + + + + + + + diff --git a/blog/2012/03/13/scala-structual-type-is-slow/index.html b/blog/2012/03/13/scala-structual-type-is-slow/index.html new file mode 100644 index 0000000..dc0648c --- /dev/null +++ b/blog/2012/03/13/scala-structual-type-is-slow/index.html @@ -0,0 +1,280 @@ + + + + + + + + Scala的structual type的确慢不少 - Homerun + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

Homerun

+ +

My name is Dong Wang, a X-Googler and Scala enthusiast.

+ +
+
+ +
+ +
+
+
+
+ +
+ + +

Scala的structual Type的确慢不少

+ + + +

+ + + + + + + + + + + + + + +

+ +
+ + +

Scala的Structual Type是用反射机制实现的。所以会慢一些。但我关心的是我经常用到的一个情况会慢多少。比如:

+ +
1
+2
+3
+4
+
type HasId = {
+  def id: Long
+}
+case class Foo(val id: Long)
+
+ + +

我经常会这样用:

+ +
1
+2
+3
+4
+5
+
def loop1(foos: Seq[HasId]) = {
+  var i = 0L
+  foos.foreach(f => i += f.id)
+  println(i)
+}
+
+ + +

这样所有有id的方法我都可以用这个loop1方法了。如果不用Structual Type,可以这样做:

+ +
1
+2
+3
+4
+5
+
def loop2(foos: Seq[Foo]) = {
+  var i = 0L
+  foos.foreach(f => i += f.id)
+  println(i)
+}
+
+ + +

在我的工作站上,如果给定一个10000000这么大的Seq[Foo],loop1用了550ms,loop2用了262ms。

+ +

结论

+ +

在这种情况下,用Structual Type的效率降低一半左右。

+
+ + + +
+ +
+ + + + +
+
+ + + + + + + + + + + + + + + + + diff --git a/blog/archives/index.html b/blog/archives/index.html index 2259521..adce9c4 100644 --- a/blog/archives/index.html +++ b/blog/archives/index.html @@ -5,11 +5,13 @@ - Blog Archive - My Octopress Blog - + Blog Archive - Homerun + - + @@ -24,7 +26,7 @@ - + @@ -42,9 +44,9 @@
}
-

My Octopress Blog

+

Homerun

-

A blogging framework for hackers.

+

My name is Dong Wang, a X-Googler and Scala enthusiast.

@@ -80,6 +82,68 @@

Blog Archive

+ + + +

2012

+ +
+ +

Scala的structual type的确慢不少

+ + + + + +
+ + + + +

2011

+ +
+ +

在Scala中如何避免导入implicit相关的定义

+ + + + + +
+ + + +
+ +

有关Scala的 @Specialized Annotation

+ + + + + +
+ + + +
+ +

Scala的循环可能很慢!

+ + + + + +
+
@@ -93,6 +157,22 @@

Blog Archive

Recent Posts

@@ -108,7 +188,7 @@

Recent Posts