Neo-coder
Neo-coder

Reputation: 7840

Scala how to decrease execution time

I have one method which generate UUID and code as below :

def generate(number : Int): List[String] = {
List.fill(number)(Generators.randomBasedGenerator().generate().toString.replaceAll("-",""))
}

and I called this as below :

for(i <-0 to 100) {
  val a = generate(1000000)
  println(a)
}

But for running the above for loop it take almost 8-9 minutes for execution, is there any other way to minimised execution time ?

Note: Here for understanding I added for loop but in real situation the generate method will call thousand of times from other request at same time.

Upvotes: 0

Views: 316

Answers (3)

Artavazd Balayan
Artavazd Balayan

Reputation: 2413

Basically you used not the fastest implementation. You should use that one when you pass Random to the constructor Generators.randomBasedGenerator(new Random(System.currentTimeMillis())). I did next things:

  • Use Array instead of List (Array is faster)
  • Removed string replacing, let's measure pure performance of generation

Dependency: "com.fasterxml.uuid" % "java-uuid-generator" % "3.1.5"

Result:

Generators.randomBasedGenerator(). Per iteration: 1579.6 ms
Generators.randomBasedGenerator() with passing Random Per iteration: 59.2 ms

Code:

import java.util.{Random, UUID}
import com.fasterxml.uuid.impl.RandomBasedGenerator
import com.fasterxml.uuid.{Generators, NoArgGenerator}
import org.scalatest.{FunSuiteLike, Matchers}

import scala.concurrent.duration.Deadline

class GeneratorTest extends FunSuiteLike
  with Matchers {

  val nTimes = 10

  // Let use Array instead of List - Array is faster!
  // and use pure UUID generators
  def generate(uuidGen: NoArgGenerator, number: Int): Seq[UUID] = {
    Array.fill(number)(uuidGen.generate())
  }

  test("Generators.randomBasedGenerator() without passed Random (secure one)") {
    // Slow generator
    val uuidGen = Generators.randomBasedGenerator()
    // Warm up JVM
    benchGeneration(uuidGen, 3)

    val startTime = Deadline.now
    benchGeneration(uuidGen, nTimes)
    val endTime = Deadline.now
    val perItermTimeMs = (endTime - startTime).toMillis / nTimes.toDouble
    println(s"Generators.randomBasedGenerator(). Per iteration: $perItermTimeMs ms")
  }

  test("Generators.randomBasedGenerator() with passing Random (not secure)") {
    // Fast generator
    val uuidGen = Generators.randomBasedGenerator(new Random(System.currentTimeMillis()))
    // Warm up JVM
    benchGeneration(uuidGen, 3)    

    val startTime = Deadline.now
    benchGeneration(uuidGen, nTimes)
    val endTime = Deadline.now
    val perItermTimeMs = (endTime - startTime).toMillis / nTimes.toDouble
    println(s"Generators.randomBasedGenerator() with passing Random Per iteration: $perItermTimeMs ms")
  }

  private def benchGeneration(uuidGen: RandomBasedGenerator, nTimes: Int) = {
    var r: Long = 0
    for (i <- 1 to nTimes) {
      val a = generate(uuidGen, 1000000)
      r += a.length
    }
    println(r)
  }
}

Upvotes: 1

jwvh
jwvh

Reputation: 51271

The problem is the List. Filling a List with 1,000,000 generated and processed elements is going to take time (and memory) because every one of those elements has to be materialized.

You can generate an infinite number of processed UUID strings instantly if you don't have to materialize them until they are actually needed.

def genUUID :Stream[String] = Stream.continually {
  Generators.randomBasedGenerator().generate().toString.filterNot(_ == '-')
}

val next5 = genUUID.take(5) //only the 1st (head) is materialized
next5.length                //now all 5 are materialized

You can use Stream or Iterator for the infinite collection, whichever you find most conducive (or least annoying) to your work flow.

Upvotes: 2

Frederic A.
Frederic A.

Reputation: 3514

You could use scala's parallel collections to split the load on multiple cores/threads.

You could also avoid creating a new generator every time:

class Generator {
  val gen = Generators.randomBasedGenerator()
  def generate(number : Int): List[String] = {
    List.fill(number)(gen.generate().toString.replaceAll("-",""))
  }
}

Upvotes: 1

Related Questions