Skip to content
This repository has been archived by the owner on Dec 22, 2021. It is now read-only.

optimise arrayOps in strawman collection #317

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 = _
Expand All @@ -40,6 +42,7 @@ class ArrayBaselineBenchmark {
array
}

halfSize = size / 2
v = fillArray(size)
vLo = fillArray(vLoSize)
}
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = _
Expand All @@ -39,7 +40,7 @@ class ArrayViewBenchmark {

@Setup(Level.Trial)
def initTrial(): Unit = {

halfSize = size / 2
v = ArrayView(fillArray(size))
vLo = ArrayView(fillArray(vLoSize))
}
Expand Down Expand Up @@ -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
Expand Down
109 changes: 105 additions & 4 deletions collections/src/main/scala/strawman/collection/ArrayOps.scala
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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] {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)"
}
Expand Down
17 changes: 15 additions & 2 deletions collections/src/main/scala/strawman/collection/package.scala
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a fallback for Array[Any] that dispatches dynamically

// 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] {
Expand Down