From c38c9aba12105967c7aafbc1645970cd23eb8368 Mon Sep 17 00:00:00 2001 From: Cong Zhao Date: Sun, 10 Dec 2017 22:38:55 +0800 Subject: [PATCH] Initial optimise arrayOps in strawman --- .../immutable/ArrayBaselineBenchmark.scala | 13 ++- .../immutable/ArrayViewBenchmark.scala | 13 ++- .../scala/strawman/collection/ArrayOps.scala | 109 +++++++++++++++++- .../collection/immutable/NumericRange.scala | 5 +- .../scala/strawman/collection/package.scala | 17 ++- 5 files changed, 144 insertions(+), 13 deletions(-) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayBaselineBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayBaselineBenchmark.scala index 0192f7ed3c..3b2d8171f9 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayBaselineBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayBaselineBenchmark.scala @@ -7,17 +7,19 @@ import org.openjdk.jmh.infra.Blackhole import strawman.collection.{ArrayView, View} import scala.{Any, AnyRef, Int, Long, Unit, Array} import scala.Predef.intWrapper +import scala.Predef.genericArrayOps @BenchmarkMode(scala.Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(15) -@Warmup(iterations = 30) +@Fork(2) +@Warmup(iterations = 15) @Measurement(iterations = 15) @State(Scope.Benchmark) class ArrayBaselineBenchmark { @Param(scala.Array("39", "282", "73121", "7312102")) var size: Int = _ + var halfSize: Int = _ @Param(scala.Array("39")) var vLoSize: Int = _ @@ -40,6 +42,7 @@ class ArrayBaselineBenchmark { array } + halfSize = size / 2 v = fillArray(size) vLo = fillArray(vLoSize) } @@ -106,6 +109,12 @@ class ArrayBaselineBenchmark { bh.consume(ret) } + @Benchmark + def sliceLaterHalf(bh: Blackhole) = { + val ret = v.slice(halfSize, size) + bh.consume(ret) + } + @Benchmark def cart(bh: Blackhole)= { var d, dp = 0 diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayViewBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayViewBenchmark.scala index 242c93e5d2..c686011a6b 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayViewBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ArrayViewBenchmark.scala @@ -10,14 +10,15 @@ import scala.Predef.intWrapper @BenchmarkMode(scala.Array(Mode.AverageTime)) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(15) -@Warmup(iterations = 30) +@Fork(2) +@Warmup(iterations = 15) @Measurement(iterations = 15) @State(Scope.Benchmark) class ArrayViewBenchmark { @Param(scala.Array("39", "282", "73121", "7312102")) var size: Int = _ + var halfSize: Int = _ @Param(scala.Array("39")) var vLoSize: Int = _ @@ -39,7 +40,7 @@ class ArrayViewBenchmark { @Setup(Level.Trial) def initTrial(): Unit = { - + halfSize = size / 2 v = ArrayView(fillArray(size)) vLo = ArrayView(fillArray(vLoSize)) } @@ -67,6 +68,12 @@ class ArrayViewBenchmark { bh.consume(ret) } + @Benchmark + def sliceLaterHalf(bh: Blackhole) = { + val ret = v.slice(halfSize, size) + bh.consume(ret) + } + @Benchmark def maps (bh: Blackhole) = { val ret : Long = v diff --git a/collections/src/main/scala/strawman/collection/ArrayOps.scala b/collections/src/main/scala/strawman/collection/ArrayOps.scala index 88914f61f8..4e059f3110 100644 --- a/collections/src/main/scala/strawman/collection/ArrayOps.scala +++ b/collections/src/main/scala/strawman/collection/ArrayOps.scala @@ -1,19 +1,22 @@ package strawman package collection -import scala.{AnyVal, Array, ArrayIndexOutOfBoundsException, Char, Int, throws} +import java.util +import java.lang.System + +import scala.{AnyVal, AnyRef, Array, ArrayIndexOutOfBoundsException, Byte, Boolean, Short, Float, Double, Long, Char, Unit, Int, throws} import scala.Predef.??? import mutable.{ArrayBuffer, GrowableBuilder} import scala.reflect.ClassTag -class ArrayOps[A](val xs: Array[A]) - extends AnyVal - with IterableOnce[A] +trait ArrayOps[A] + extends IterableOnce[A] with IndexedSeqOps[A, immutable.IndexedSeq, Array[A]] with StrictOptimizedIterableOps[A, Seq, Array[A]] with ArrayLike[A] { + val xs: Array[A] def toIterable = ArrayView(xs) protected[this] def coll: Array[A] = xs override def toSeq: immutable.Seq[A] = fromIterable(toIterable) @@ -54,6 +57,104 @@ class ArrayOps[A](val xs: Array[A]) def zip[B: ClassTag](that: Iterable[B]): Array[(A, B)] = fromTaggedIterable(View.Zip(toIterable, that)) + override final def slice(from: Int, until: Int): Array[A] = { + val start = if (from < 0) 0 else from + if (until <= start || start >= length) + return emptyImpl + val end = if (until > length) length else until + sliceImpl(start, end) + } + + protected def emptyImpl: Array[A] + protected def sliceImpl(from: Int, until: Int): Array[A] +} + +object ArrayOps { + + private val emptyByteArray = new Array[Byte](0) + private val emptyShortArray = new Array[Short](0) + private val emptyIntArray = new Array[Int](0) + private val emptyLongArray = new Array[Long](0) + private val emptyFloatArray = new Array[Float](0) + private val emptyDoubleArray = new Array[Double](0) + private val emptyUnitArray = new Array[Unit](0) + private val emptyCharArray = new Array[Char](0) + private val emptyBooleanArray = new Array[Boolean](0) + + /** A class of `ArrayOps` for arrays containing reference types. */ + final class ofRef[A <: AnyRef](override val xs: Array[A]) extends ArrayOps[A] { + + protected override def emptyImpl:Array[A] = util.Arrays.copyOf[A](xs,0) + protected override def sliceImpl(from: Int, until: Int): Array[A] = util.Arrays.copyOfRange[A](xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `byte`s. */ + final class ofByte(override val xs: Array[Byte]) extends ArrayOps[Byte] { + + protected override def emptyImpl = emptyByteArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `short`s. */ + final class ofShort(override val xs: Array[Short]) extends ArrayOps[Short] { + + protected override def emptyImpl = emptyShortArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `char`s. */ + final class ofChar(override val xs: Array[Char]) extends ArrayOps[Char] { + + protected override def emptyImpl = emptyCharArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `int`s. */ + final class ofInt(override val xs: Array[Int]) extends ArrayOps[Int] { + + protected override def emptyImpl = emptyIntArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `long`s. */ + final class ofLong(override val xs: Array[Long]) extends ArrayOps[Long] { + + protected override def emptyImpl = emptyLongArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `float`s. */ + final class ofFloat(override val xs: Array[Float]) extends ArrayOps[Float] { + + protected override def emptyImpl = emptyFloatArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `double`s. */ + final class ofDouble(override val xs: Array[Double]) extends ArrayOps[Double] { + + protected override def emptyImpl = emptyDoubleArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays containing `boolean`s. */ + final class ofBoolean(override val xs: Array[Boolean]) extends ArrayOps[Boolean] { + + protected override def emptyImpl = emptyBooleanArray + protected override def sliceImpl(from: Int, until: Int) = util.Arrays.copyOfRange(xs, from, until) + } + + /** A class of `ArrayOps` for arrays of `Unit` types. */ + final class ofUnit(override val xs: Array[Unit]) extends ArrayOps[Unit] { + + protected override def emptyImpl = emptyUnitArray + protected override def sliceImpl(from: Int, until: Int) = { + // cant use util.Arrays.copyOfRange[Unit](repr, from, until) - Unit is special and doesnt compile + val res = new Array[Unit](until-from) + System.arraycopy(xs, from, res, 0, res.size) + res + } + } } case class ArrayView[A](xs: Array[A]) extends IndexedView[A] { diff --git a/collections/src/main/scala/strawman/collection/immutable/NumericRange.scala b/collections/src/main/scala/strawman/collection/immutable/NumericRange.scala index 707664b0ef..a6a4969237 100644 --- a/collections/src/main/scala/strawman/collection/immutable/NumericRange.scala +++ b/collections/src/main/scala/strawman/collection/immutable/NumericRange.scala @@ -1,7 +1,8 @@ package strawman.collection.immutable import strawman.collection -import strawman.collection.{SeqFactory, IterableFactory, IterableOnce, Iterator, StrictOptimizedIterableOps, arrayToArrayOps} +import strawman.collection.{SeqFactory, IterableFactory, IterableOnce, Iterator, StrictOptimizedIterableOps} +import strawman.collection.ArrayOpsDecorators._ import scala.{Any, Boolean, ClassCastException, IllegalArgumentException, IndexOutOfBoundsException, Int, Integral, NoSuchElementException, Numeric, Ordering, Serializable, StringContext, Unit, `inline`, math, specialized, throws} import scala.Predef.ArrowAssoc @@ -203,7 +204,7 @@ sealed class NumericRange[T]( override def containsTyped(el: A) = underlyingRange exists (x => fm(x) == el) override def toString = { - def simpleOf(x: Any): String = collection.arrayToArrayOps(x.getClass.getName.split("\\.")).last + def simpleOf(x: Any): String = collection.ArrayOpsDecorators.refArrayOps(x.getClass.getName.split("\\.")).last val stepped = simpleOf(underlyingRange.step) s"${super.toString} (using $underlyingRange of $stepped)" } diff --git a/collections/src/main/scala/strawman/collection/package.scala b/collections/src/main/scala/strawman/collection/package.scala index d19087c09b..8c01ba941b 100644 --- a/collections/src/main/scala/strawman/collection/package.scala +++ b/collections/src/main/scala/strawman/collection/package.scala @@ -1,6 +1,6 @@ package strawman -import scala.{Any, AnyVal, Array, Boolean, Char, IllegalArgumentException, IndexOutOfBoundsException, Int, NoSuchElementException, Unit, UnsupportedOperationException, PartialFunction, Option, None, Some} +import scala.{Any, AnyVal, AnyRef, Array, Byte, Boolean, Short, Long, Float, Double, Char, IllegalArgumentException, IndexOutOfBoundsException, Int, NoSuchElementException, Unit, UnsupportedOperationException, PartialFunction, Option, None, Some} import scala.Predef.{String, ArrowAssoc} import scala.reflect.ClassTag @@ -12,7 +12,20 @@ package object collection extends LowPriority { implicit def stringToStringOps(s: String): immutable.StringOps = new immutable.StringOps(s) /** Decorator to add collection operations to arrays. */ - implicit def arrayToArrayOps[A](as: Array[A]): ArrayOps[A] = new ArrayOps[A](as) + object ArrayOpsDecorators { + // implicit def arrayToArrayOps[A](as: Array[A]): ArrayOps[A] = new ArrayOps[A](as) + implicit def booleanArrayOps(xs: Array[Boolean]): ArrayOps.ofBoolean = new ArrayOps.ofBoolean(xs) + implicit def byteArrayOps(xs: Array[Byte]): ArrayOps.ofByte = new ArrayOps.ofByte(xs) + implicit def charArrayOps(xs: Array[Char]): ArrayOps.ofChar = new ArrayOps.ofChar(xs) + implicit def doubleArrayOps(xs: Array[Double]): ArrayOps.ofDouble = new ArrayOps.ofDouble(xs) + implicit def floatArrayOps(xs: Array[Float]): ArrayOps.ofFloat = new ArrayOps.ofFloat(xs) + implicit def intArrayOps(xs: Array[Int]): ArrayOps.ofInt = new ArrayOps.ofInt(xs) + implicit def longArrayOps(xs: Array[Long]): ArrayOps.ofLong = new ArrayOps.ofLong(xs) + implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps.ofRef[T] = new ArrayOps.ofRef[T](xs) + implicit def shortArrayOps(xs: Array[Short]): ArrayOps.ofShort = new ArrayOps.ofShort(xs) + implicit def unitArrayOps(xs: Array[Unit]): ArrayOps.ofUnit = new ArrayOps.ofUnit(xs) + } + implicit class toNewIterator[A](val it: scala.Iterator[A]) extends AnyVal { def toStrawman = new strawman.collection.Iterator[A] {