Slow Scala assert
Asked Answered
H

4

12

We've been profiling our code recently and we've come across a few annoying hotspots. They're in the form

assert(a == b, a + " is not equal to " + b)

Because some of these asserts can be in code called a huge amount of times the string concat starts to add up. assert is defined as:

def assert(assumption : Boolean, message : Any) = ....

why isn't it defined as:

def assert(assumption : Boolean, message : => Any) = ....

That way it would evaluate lazily. Given that it's not defined that way is there an inline way of calling assert with a message param that is evaluated lazily?

Thanks

Harrod answered 11/3, 2010 at 13:10 Comment(1)
For those that don't know, this was fixed in 2.8.Harrod
M
17

Lazy evaluation has also some overhead for the function object created. If your message object is already fully constructed (a static message) this overhead is unnecessary.

The appropriate method for your use case would be sprintf-style:

assert(a == b,  "%s is not equal to %s", a, b)

As long as there is a speciaized function

assert(Boolean, String, Any, Any)

this implementation has no overhead or the cost of the var args array

assert(Boolean, String, Any*)

for the general case.

Implementing toString would be evaluated lazily, but is not readable:

assert(a == b, new { override def toString =  a + " is not equal to " + b })
Munger answered 11/3, 2010 at 13:44 Comment(1)
Thanks Thomas. I hadn't considered the overhead of lazy evaluation. From our code the overhead of the lazy evaluation is much much lower than the string concat so I think we'll go with writing our own assert method. It's not pretty but speeds things up a lot.Harrod
M
7

It is by-name, I changed it over a year ago.

http://www.scala-lang.org/node/825

Current Predef:

@elidable(ASSERTION)
def assert(assertion: Boolean, message: => Any) {
  if (!assertion)
    throw new java.lang.AssertionError("assertion failed: "+ message)
}
Mammy answered 13/3, 2010 at 6:33 Comment(1)
@extempore: Is that for 2.8 only? Or 2.7 as well?Documentation
C
1

Thomas' answer is great, but just in case you like the idea of the last answer but dislike the unreadability, you can get around it:

object LazyS {
  def apply(f: => String): AnyRef = new {
    override def toString = f
  }
}

Example:

object KnightSpeak {
  override def toString = { println("Turned into a string") ; "Ni" }
}

scala> assert(true != false , LazyS("I say " + KnightSpeak))

scala> println( LazyS("I say " + KnightSpeak) )
Turned into a string
I say Ni
Chufa answered 12/3, 2010 at 16:55 Comment(0)
A
-2

Try: assert( a==b, "%s is not equals to %s".format(a,b)) The format should only be called when the assert needs the string. Format is added to RichString via implicit.

Albuminate answered 11/3, 2010 at 14:32 Comment(3)
This is wrong for precisely the reason that the questioner has highlighted: that the assert method takes an Any and not a => AnyUtilitarianism
I'm pretty sure there's no magic in format so it will just get called as normal, whether a==b is true or not.Harrod
d'oh. I'm an id10t. It's obvious now.Albuminate

© 2022 - 2024 — McMap. All rights reserved.