Skip to content
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

NODE-2612 RIDE replaceByIndex() #3881

Merged
merged 7 commits into from
Sep 28, 2023
Merged
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
@@ -0,0 +1,75 @@
package com.wavesplatform.lang.v1

import com.wavesplatform.common.utils.EitherExt2
import com.wavesplatform.lang.Common
import com.wavesplatform.lang.directives.values.StdLibVersion
import com.wavesplatform.lang.v1.FunctionHeader.Native
import com.wavesplatform.lang.v1.compiler.Terms.*
import com.wavesplatform.lang.v1.evaluator.FunctionIds.REPLACE_BY_INDEX_OF_LIST
import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext
import com.wavesplatform.lang.v1.traits.Environment
import org.openjdk.jmh.annotations.*
import org.openjdk.jmh.infra.Blackhole

import scala.concurrent.duration.{MICROSECONDS, SECONDS}

@OutputTimeUnit(MICROSECONDS)
@BenchmarkMode(Array(Mode.AverageTime))
@Threads(1)
@Fork(1)
@Warmup(iterations = 10, time = 1, timeUnit = SECONDS)
@Measurement(iterations = 10, time = 1, timeUnit = SECONDS)
class ListReplaceByIndexBenchmark {
@Benchmark
def listReplaceFirstByIndex(st: ListReplaceByIndexSt, bh: Blackhole): Unit =
bh.consume(eval(st.ctx, st.replaceFirst))

@Benchmark
def listReplaceMiddleByIndex(st: ListReplaceByIndexSt, bh: Blackhole): Unit =
bh.consume(eval(st.ctx, st.replaceMiddle))

@Benchmark
def listReplaceLastByIndex(st: ListReplaceByIndexSt, bh: Blackhole): Unit =
bh.consume(eval(st.ctx, st.replaceLast))
}

@State(Scope.Benchmark)
class ListReplaceByIndexSt {
val ctx =
PureContext
.build(StdLibVersion.VersionDic.all.max, useNewPowPrecision = true)
.withEnvironment[Environment]
.evaluationContext(Common.emptyBlockchainEnvironment())

val list = ARR(Vector.fill(1000)(CONST_LONG(Long.MaxValue)), limited = true).explicitGet()

val replaceFirst =
FUNCTION_CALL(
Native(REPLACE_BY_INDEX_OF_LIST),
List(
list,
CONST_LONG(0),
CONST_LONG(777)
)
)

val replaceMiddle =
FUNCTION_CALL(
Native(REPLACE_BY_INDEX_OF_LIST),
List(
list,
CONST_LONG(500),
CONST_LONG(777)
)
)

val replaceLast =
FUNCTION_CALL(
Native(REPLACE_BY_INDEX_OF_LIST),
List(
list,
CONST_LONG(999),
CONST_LONG(777)
)
)
}
Original file line number Diff line number Diff line change
@@ -1,100 +1,97 @@
package com.wavesplatform.lang.v1

import java.util.concurrent.TimeUnit

import cats.Id
import cats.kernel.Monoid
import cats.implicits.catsSyntaxSemigroup
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.{Base58, EitherExt2}
import com.wavesplatform.crypto.Curve25519
import com.wavesplatform.lang.Global
import com.wavesplatform.lang.directives.values.{V1, V4}
import com.wavesplatform.lang.directives.values.StdLibVersion
import com.wavesplatform.lang.v1.EnvironmentFunctionsBenchmark.{curve25519, randomBytes}
import com.wavesplatform.lang.v1.FunctionHeader.Native
import com.wavesplatform.lang.v1.ScriptEvaluatorBenchmark.*
import com.wavesplatform.lang.v1.compiler.Terms.*
import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext
import com.wavesplatform.lang.v1.evaluator.EvaluatorV1.*
import com.wavesplatform.lang.v1.evaluator.FunctionIds
import com.wavesplatform.lang.v1.evaluator.FunctionIds.{FROMBASE58, SIGVERIFY, TOBASE58}
import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext
import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext}
import com.wavesplatform.lang.v1.evaluator.{EvaluatorV1, FunctionIds}
import com.wavesplatform.lang.v1.traits.Environment
import com.wavesplatform.lang.{Common, Global}
import org.openjdk.jmh.annotations.*
import org.openjdk.jmh.infra.Blackhole

import java.util.concurrent.TimeUnit
import scala.concurrent.duration.SECONDS
import scala.util.Random

object ScriptEvaluatorBenchmark {
val version = V1
val pureEvalContext: EvaluationContext[NoContext, Id] =
PureContext.build(V1, useNewPowPrecision = true).evaluationContext
val evaluatorV1: EvaluatorV1[Id, NoContext] = new EvaluatorV1[Id, NoContext]()
val lastVersion = StdLibVersion.VersionDic.all.max
val context =
(PureContext.build(lastVersion, useNewPowPrecision = true) |+| CryptoContext.build(Global, lastVersion))
.withEnvironment[Environment]
.evaluationContext(Common.emptyBlockchainEnvironment())
}

@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Array(Mode.AverageTime))
@Threads(1)
@Fork(1)
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@Warmup(iterations = 10, time = 1, timeUnit = SECONDS)
@Measurement(iterations = 10, time = 1, timeUnit = SECONDS)
class ScriptEvaluatorBenchmark {
@Benchmark
def bigSum(st: BigSum, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](pureEvalContext, st.expr))
def bigSum(st: BigSum, bh: Blackhole): Unit = bh.consume(eval(context, st.expr))

@Benchmark
def nestedBlocks(st: NestedBlocks, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.expr))
def nestedBlocks(st: NestedBlocks, bh: Blackhole): Unit = bh.consume(eval(context, st.expr))

@Benchmark
def signatures(st: Signatures, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.expr))
def signatures(st: Signatures, bh: Blackhole): Unit = bh.consume(eval(context, st.expr))

@Benchmark
def base58encode(st: Base58Perf, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.encode))
def base58encode(st: Base58Perf, bh: Blackhole): Unit = bh.consume(eval(context, st.encode))

@Benchmark
def base58decode(st: Base58Perf, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.decode))
def base58decode(st: Base58Perf, bh: Blackhole): Unit = bh.consume(eval(context, st.decode))

@Benchmark
def stringConcat(st: Concat, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.strings))
def stringConcat(st: Concat, bh: Blackhole): Unit = bh.consume(eval(context, st.strings))

@Benchmark
def bytesConcat(st: Concat, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.bytes))
def bytesConcat(st: Concat, bh: Blackhole): Unit = bh.consume(eval(context, st.bytes))

@Benchmark
def listMedianRandomElements(st: Median, bh: Blackhole): Unit =
bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.randomElements(Random.nextInt(10000))))
bh.consume(eval(context, st.randomElements(Random.nextInt(10000))))

@Benchmark
def listMedianSortedElements(st: Median, bh: Blackhole): Unit =
bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.sortedElements))
bh.consume(eval(context, st.sortedElements))

@Benchmark
def listMedianSortedReverseElements(st: Median, bh: Blackhole): Unit =
bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.sortedReverseElements))
bh.consume(eval(context, st.sortedReverseElements))

@Benchmark
def listMedianEqualElements(st: Median, bh: Blackhole): Unit =
bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.equalElements))
bh.consume(eval(context, st.equalElements))

@Benchmark
def listRemoveFirstByIndex(st: ListRemoveByIndex, bh: Blackhole): Unit =
bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.removeFirst))
bh.consume(eval(context, st.removeFirst))

@Benchmark
def listRemoveMiddleByIndex(st: ListRemoveByIndex, bh: Blackhole): Unit =
bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.removeMiddle))
bh.consume(eval(context, st.removeMiddle))

@Benchmark
def listRemoveLastByIndex(st: ListRemoveByIndex, bh: Blackhole): Unit =
bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.removeLast))
bh.consume(eval(context, st.removeLast))

@Benchmark
def sigVerify32Kb(st: SigVerify32Kb, bh: Blackhole): Unit = bh.consume(evaluatorV1.apply[EVALUATED](st.context, st.expr))
def sigVerify32Kb(st: SigVerify32Kb, bh: Blackhole): Unit =
bh.consume(eval(context, st.expr))
}

@State(Scope.Benchmark)
class NestedBlocks {
val context: EvaluationContext[NoContext, Id] = pureEvalContext

val expr: EXPR = {
val blockCount = 300
val cond = FUNCTION_CALL(PureContext.eq, List(REF(s"v$blockCount"), CONST_LONG(0)))
Expand All @@ -107,9 +104,6 @@ class NestedBlocks {

@State(Scope.Benchmark)
class Base58Perf {
val context: EvaluationContext[NoContext, Id] =
Monoid.combine(pureEvalContext, CryptoContext.build(Global, version).evaluationContext)

val encode: EXPR = {
val base58Count = 120
val sum = (1 to base58Count).foldRight[EXPR](CONST_LONG(0)) { case (i, e) =>
Expand Down Expand Up @@ -150,9 +144,6 @@ class Base58Perf {

@State(Scope.Benchmark)
class Signatures {
val context: EvaluationContext[NoContext, Id] =
Monoid.combine(pureEvalContext, CryptoContext.build(Global, version).evaluationContext)

val expr: EXPR = {
val sigCount = 20
val sum = (1 to sigCount).foldRight[EXPR](CONST_LONG(0)) { case (i, e) =>
Expand Down Expand Up @@ -191,8 +182,6 @@ class Signatures {

@State(Scope.Benchmark)
class Concat {
val context: EvaluationContext[NoContext, Id] = pureEvalContext

private val Steps = 180

private def expr(init: EXPR, func: FunctionHeader, operand: EXPR, count: Int) =
Expand All @@ -218,8 +207,6 @@ class Concat {

@State(Scope.Benchmark)
class Median {
val context: EvaluationContext[NoContext, Id] = PureContext.build(V4, useNewPowPrecision = true).evaluationContext

val randomElements: Array[EXPR] =
(1 to 10000).map { _ =>
val listOfLong = (1 to 1000).map(_ => CONST_LONG(Random.nextLong()))
Expand Down Expand Up @@ -260,9 +247,6 @@ class Median {

@State(Scope.Benchmark)
class SigVerify32Kb {
val context: EvaluationContext[NoContext, Id] =
Monoid.combine(PureContext.build(V4, useNewPowPrecision = true).evaluationContext, CryptoContext.build(Global, V4).evaluationContext)

val expr: EXPR = {
val (privateKey, publicKey) = curve25519.generateKeypair
val message = randomBytes(32 * 1024 - 1)
Expand All @@ -281,12 +265,6 @@ class SigVerify32Kb {

@State(Scope.Benchmark)
class ListRemoveByIndex {
val context: EvaluationContext[NoContext, Id] =
Monoid.combine(
PureContext.build(V4, useNewPowPrecision = true).evaluationContext,
CryptoContext.build(Global, V4).evaluationContext
)

val list: ARR = ARR(Vector.fill(1000)(CONST_LONG(Long.MaxValue)), limited = true).explicitGet()

val removeFirst: EXPR =
Expand All @@ -312,7 +290,7 @@ class ListRemoveByIndex {
Native(FunctionIds.REMOVE_BY_INDEX_OF_LIST),
List(
list,
CONST_LONG(1000)
CONST_LONG(999)
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ package object v1 {
def eval(
ctx: EvaluationContext[Environment, Id],
expr: EXPR,
stdLibVersion: StdLibVersion
stdLibVersion: StdLibVersion = StdLibVersion.VersionDic.all.max
): (Log[Id], Int, Either[ExecutionError, Terms.EVALUATED]) =
EvaluatorV2.applyCompleted(ctx, expr, LogExtraInfo(), stdLibVersion, newMode = true, correctFunctionCallScope = true, enableExecutionLog = false)
}
11 changes: 9 additions & 2 deletions lang/doc/v8/funcs/list-functions.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,15 @@
name: "removeByIndex"
params: [ "List[T]", "Int" ]
doc: "Removes the element at given index from the list and returns new list."
paramsDoc: [ "The list.", "The element." ]
complexity: 7
paramsDoc: [ "The list.", "The index." ]
complexity: 4
}
{
name: "replaceByIndex"
params: [ "List[T]", "Int", "T" ]
doc: "Replaces the element at given index in the list and returns new list."
paramsDoc: [ "The list.", "The index.", "New element." ]
complexity: 4
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@ object FunctionIds {
val STRING_TO_BIGINTOPT: Short = 424
val MEDIAN_LISTBIGINT: Short = 425

val CREATE_LIST: Short = 1100
val APPEND_LIST: Short = 1101
val CONCAT_LIST: Short = 1102
val INDEX_OF_LIST: Short = 1103
val LAST_INDEX_OF_LIST: Short = 1104
val REMOVE_BY_INDEX_OF_LIST: Short = 1105
val CREATE_LIST: Short = 1100
val APPEND_LIST: Short = 1101
val CONCAT_LIST: Short = 1102
val INDEX_OF_LIST: Short = 1103
val LAST_INDEX_OF_LIST: Short = 1104
val REMOVE_BY_INDEX_OF_LIST: Short = 1105
val REPLACE_BY_INDEX_OF_LIST: Short = 1106

val UTF8STRING: Short = 1200
val BININT: Short = 1201
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1452,7 +1452,7 @@ object PureContext {
lazy val listRemoveByIndex: BaseFunction[NoContext] =
NativeFunction(
"removeByIndex",
7,
Map(V4 -> 7L, V5 -> 7L, V6 -> 7L, V7 -> 7L, V8 -> 4L),
REMOVE_BY_INDEX_OF_LIST,
PARAMETERIZEDLIST(TYPEPARAM('T')),
("list", PARAMETERIZEDLIST(TYPEPARAM('T'))),
Expand All @@ -1471,6 +1471,29 @@ object PureContext {
notImplemented[Id, EVALUATED]("removeByIndex(list: List[T], index: Int)", xs)
}

private val listReplaceByIndex: BaseFunction[NoContext] =
NativeFunction(
"replaceByIndex",
4,
REPLACE_BY_INDEX_OF_LIST,
PARAMETERIZEDLIST(TYPEPARAM('T')),
("list", PARAMETERIZEDLIST(TYPEPARAM('T'))),
("index", LONG),
("element", TYPEPARAM('T'))
) {
case ARR(list) :: CONST_LONG(index) :: element :: Nil =>
if (list.isEmpty)
Left("Can't replace an element in empty list")
else if (index < 0)
Left(s"Index of the replacing element should be positive, but $index was passed")
else if (index >= list.size)
Left(s"Index of the replacing element should be lower than list size = ${list.length}, but $index was passed")
else
ARR(list.updated(index.toInt, element), limited = true)
case xs =>
notImplemented[Id, EVALUATED]("replaceByIndex(list: List[T], index: Int, element: T)", xs)
}

@VisibleForTesting
private[v1] def genericListIndexOf(
element: EVALUATED,
Expand Down Expand Up @@ -2041,12 +2064,20 @@ object PureContext {
v6Functions
)

private[this] val v8Ctx =
CTX[NoContext](
v5Types,
v5Vars,
v6Functions :+ listReplaceByIndex
)

def build(version: StdLibVersion, useNewPowPrecision: Boolean): CTX[NoContext] =
version match {
case V1 | V2 => v1V2Ctx(useNewPowPrecision)
case V3 => v3Ctx(useNewPowPrecision)
case V4 => v4Ctx(useNewPowPrecision)
case V5 => v5Ctx(useNewPowPrecision)
case _ => v6Ctx
case V6 | V7 => v6Ctx
case V8 => v8Ctx
}
}
Loading