-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Some new performance features #14
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
package com.certora.collect | ||
|
||
import com.certora.forkjoin.* | ||
|
||
/** | ||
Base class for TreapSet implementations. Provides the Set operations; derived classes deal with type-specific | ||
behavior such as hash collisions. See `Treap` for an overview of all of this. | ||
|
@@ -62,6 +64,7 @@ internal sealed class AbstractTreapSet<@Treapable E, S : AbstractTreapSet<E, S>> | |
abstract fun shallowRemoveAll(predicate: (E) -> Boolean): S? | ||
abstract fun shallowContainsAll(elements: S): Boolean | ||
abstract fun shallowContainsAny(elements: S): Boolean | ||
abstract fun <R : Any> shallowMapReduce(map: (E) -> R, reduce: (R, R) -> R): R | ||
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
@@ -152,6 +155,26 @@ internal sealed class AbstractTreapSet<@Treapable E, S : AbstractTreapSet<E, S>> | |
left === null && right === null -> shallowGetSingleElement() | ||
else -> null | ||
} | ||
|
||
override fun <R : Any> mapReduce(map: (E) -> R, reduce: (R, R) -> R): R = | ||
notForking(self) { mapReduceImpl(map, reduce) } | ||
|
||
override fun <R : Any> parallelMapReduce(map: (E) -> R, reduce: (R, R) -> R, parallelThresholdLog2: Int): R = | ||
maybeForking(self, threshold = { it.isApproximatelySmallerThanLog2(parallelThresholdLog2) }) { | ||
mapReduceImpl(map, reduce) | ||
} | ||
|
||
context(ThresholdForker<S>) | ||
private fun <R : Any> mapReduceImpl(map: (E) -> R, reduce: (R, R) -> R): R { | ||
val (left, middle, right) = fork( | ||
self, | ||
{ left?.mapReduceImpl(map, reduce) }, | ||
{ shallowMapReduce(map, reduce) }, | ||
{ right?.mapReduceImpl(map, reduce) } | ||
) | ||
val leftAndMiddle = left?.let { reduce(it, middle) } ?: middle | ||
return right?.let { reduce(leftAndMiddle, it) } ?: leftAndMiddle | ||
} | ||
Comment on lines
+159
to
+177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just so I'm not crazy: this is the same implementation as the map case right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think it's just the |
||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,10 +19,19 @@ internal class EmptyTreapMap<@Treapable K, V> private constructor() : TreapMap<K | |
override fun remove(key: K): TreapMap<K, V> = this | ||
override fun remove(key: K, value: V): TreapMap<K, V> = this | ||
|
||
override fun updateValues(transform: (K, V) -> V?): TreapMap<K, V> = this | ||
override fun parallelUpdateValues(parallelThresholdLog2: Int, transform: (K, V) -> V?): TreapMap<K, V> = this | ||
override fun <R : Any> updateValues( | ||
transform: (K, V) -> R? | ||
): TreapMap<K, R> = treapMapOf() | ||
|
||
override fun <U> updateEntry(key: K, value: U?, merger: (V?, U?) -> V?): TreapMap<K, V> = | ||
override fun <R : Any> parallelUpdateValues( | ||
parallelThresholdLog2: Int, | ||
transform: (K, V) -> R? | ||
): TreapMap<K, R> = treapMapOf() | ||
Comment on lines
+22
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't you return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is just a nicer-looking way of doing that. |
||
|
||
override fun <R : Any> mapReduce(map: (K, V) -> R, reduce: (R, R) -> R): R? = null | ||
override fun <R : Any> parallelMapReduce(map: (K, V) -> R, reduce: (R, R) -> R, parallelThresholdLog2: Int): R? = null | ||
|
||
override fun <U> updateEntry(key: K, value: U, merger: (V?, U) -> V?): TreapMap<K, V> = | ||
when (val v = merger(null, value)) { | ||
null -> this | ||
else -> put(key, v) | ||
|
@@ -53,7 +62,7 @@ internal class EmptyTreapMap<@Treapable K, V> private constructor() : TreapMap<K | |
@Suppress("Treapability", "UNCHECKED_CAST") | ||
override fun put(key: K, value: V): TreapMap<K, V> = when (key) { | ||
is PrefersHashTreap -> HashTreapMap(key, value) | ||
is Comparable<*> -> | ||
is Comparable<*> -> | ||
SortedTreapMap<Comparable<Comparable<*>>, V>(key as Comparable<Comparable<*>>, value) as TreapMap<K, V> | ||
else -> HashTreapMap(key, value) | ||
} | ||
|
@@ -71,4 +80,4 @@ internal class EmptyTreapMap<@Treapable K, V> private constructor() : TreapMap<K | |
@Suppress("UNCHECKED_CAST") | ||
operator fun <@Treapable K, V> invoke(): EmptyTreapMap<K, V> = instance as EmptyTreapMap<K, V> | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -165,7 +165,7 @@ internal class HashTreapMap<@Treapable K, V>( | |
} | ||
} | ||
|
||
override fun <U> shallowUpdate(entryKey: K, toUpdate: U, merger: (V?, U?) -> V?): HashTreapMap<K, V>? { | ||
override fun <U> shallowUpdate(entryKey: K, toUpdate: U, merger: (V?, U) -> V?): HashTreapMap<K, V>? { | ||
return when (this.key) { | ||
entryKey -> { | ||
val newValue = merger(this.value, toUpdate) | ||
|
@@ -280,6 +280,15 @@ internal class HashTreapMap<@Treapable K, V>( | |
forEachPair { (k, v) -> h += AbstractMapEntry.hashCode(k, v) } | ||
return h | ||
} | ||
|
||
override fun <R : Any> shallowMapReduce(map: (K, V) -> R, reduce: (R, R) -> R): R { | ||
var result: R? = null | ||
forEachPair { | ||
val mapped = map(it.key, it.value) | ||
result = result?.let { result -> reduce(result, mapped) } ?: mapped | ||
} | ||
return result!! | ||
} | ||
Comment on lines
+284
to
+291
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is the type bound on Probably not worth it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We depend on this constraint in |
||
} | ||
|
||
internal interface KeyValuePairList<K, V> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,24 +34,56 @@ public sealed interface TreapMap<K, V> : PersistentMap<K, V> { | |
merger: (K, V?, V?) -> V? | ||
): TreapMap<K, V> | ||
|
||
public fun updateValues( | ||
transform: (K, V) -> V? | ||
): TreapMap<K, V> | ||
/** | ||
Produces a new [TreapMap] with updated entries, by applying the supplied [transform]. Removes entries for which | ||
[transform] returns null. | ||
|
||
Note that even a seemingly non-mutating transform may result in a different map, if the map contains null values: | ||
|
||
``` | ||
val map = treapMapOf("a" to null).updateValues { _, v -> v } // yields an empty map | ||
``` | ||
*/ | ||
public fun <R : Any> updateValues( | ||
transform: (K, V) -> R? | ||
): TreapMap<K, R> | ||
Comment on lines
+47
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is a change in the public API right? If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'm favor of this change, as it makes explicit that null values are filtered out, where as previously you could write: treapMapOf("foo" to 3, "bar" to null).updateValues { _, v -> v } and have this return a map without "bar" (and no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we document this behavior a bit more explicitly though? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can definitely write this, before and after this change: var map = treapMapOf("foo" to 3, "bar" to null).updateValues { _, v -> v } In both cases the result is a map without the entries that had null values. With this change, the result will be typed as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will add some comments. |
||
|
||
/** | ||
Produces a new [TreapMap] with updated entries, by applying the supplied [transform]. Removes entries for which | ||
[transform] returns null. | ||
|
||
Operations are performed in parallel for maps larger than (approximately) 2^parallelThresholdLog2. | ||
|
||
public fun parallelUpdateValues( | ||
See additional nodes on [updateValues]. | ||
*/ | ||
public fun <R : Any> parallelUpdateValues( | ||
parallelThresholdLog2: Int = 5, | ||
transform: (K, V) -> V? | ||
): TreapMap<K, V> | ||
transform: (K, V) -> R? | ||
): TreapMap<K, R> | ||
|
||
public fun <U> updateEntry( | ||
key: K, | ||
value: U?, | ||
merger: (V?, U?) -> V? | ||
value: U, | ||
merger: (V?, U) -> V? | ||
): TreapMap<K, V> | ||
|
||
public fun zip( | ||
m: Map<out K, V> | ||
): Sequence<Map.Entry<K, Pair<V?, V?>>> | ||
|
||
/** | ||
Applies the [map] function to each entry, then applies [reduce] to the results, in a depth-first traversal of | ||
the underlying tree. Returns null if the map is empty. | ||
*/ | ||
public fun <R : Any> mapReduce(map: (K, V) -> R, reduce: (R, R) -> R): R? | ||
|
||
/** | ||
Applies the [map] function to each entry, then applies [reduce] to the results, in a depth-first traversal of | ||
the underlying tree. Returns null if the map is empty. | ||
|
||
Operations are performed in parallel for maps larger than (approximately) 2^parallelThresholdLog2. | ||
*/ | ||
public fun <R : Any> parallelMapReduce(map: (K, V) -> R, reduce: (R, R) -> R, parallelThresholdLog2: Int = 5): R? | ||
} | ||
|
||
public fun <@Treapable K, V> treapMapOf(): TreapMap<K, V> = EmptyTreapMap<K, V>() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a compelling reason to not just go ahead and make this an erased types implementation too? It seems like we always go back and do this for performance anyway so...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Static typing is good. :)