Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

When writing in Scala, it usually feels natrual to treat Arrays Array[T] as a sequence of T's and Strings as a sequence of characters. For example

...

While this is convenient and feel's like "correct" Scala, in such cases Scala will implicity box the string String with a StringOps to provide that extra functionality, requiring which requires an allocation. Similarly, using Seq-like functions on an Array will also box he underlying array with an ArrayOps, again requiring allocation. Note that even simple things like the String apply()  function, e.g. str(4) , will cause such boxing. Instead, you should use the equivalent str.charAt(4). This is also a key difference between calling .size  on an array vs .length. The size method requires allocating an ArrayOps, while length will diretly acess directly access the length from the java array primitive.

Note that in most cases, these allocations are so efficient that it likely won't affect performaneperformance. However, it's possible it could have an affect in a tight inner loop. At , and at the very least, it avoids noise when profiling.

...

In some cases, it can be relatively expensive to create a new instance of an object. In such cases, it might be worth considering if clone()ing an existing instance and mutating it is faster.

Once One case where this appears to be beneficial is with ICU4J Calendar objects. Creating a new Calendar object via Calendar.getInstance(...)  is a fairly expensive process with lots of different object allocations. Instead, it is reccommend that one consider something like the following, which minimizes allocations and initialization computations:

Code Block
scala
scala
object SomeClass {
  val emptyCalendar = {
    val c = Calendar.getInstance(TimeZone.UNKNOWN_ZONE)
    c.clear()
    c
  }
}

def functionThatNeedsANewCalendar = {
  val cal = SomeClass.emptyCalendar.clone.asInstanceOf[Calendar]
  ...
  cal.set(...)
  ...
  cal
}

Examining Bytecode

As is apparent from many of the above suggestions, minimizing allocations is often key to improving Daffodil performance and making profiling less noisy. Often times an allocation will occur but it isn't clear based on the source why such an allocation might be happening. In these cases, it is often necessary to inspect the bytecode. To do so, the use of the javap  function can be invaluable. The following will convert a class to bytecode, including some helpful bytecode interpretations in comments:

Code Block
bash
bash
java -p -c path/to/class/file.class

It can also be useful to search the entire code base for certain allocations by looking through the disassemble code. A useful script to determine decompile all class files is the following:

Code Block
bash
bash
find daffodil.git -name '*.class' -exec javap -p -c '{}' \; > disassembled.txt

From there, you can grep this file and determine where unexpected allocations may be taking place. For example, to find allocationallocations oa java.math.BigInteger:

Code Block
bash
bash
grep -a "new" -n disassembled.txt | grep "java/math/BigInteger"

...