diff options
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/scala/firrtl/options/StageAnnotations.scala | 26 | ||||
| -rw-r--r-- | src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala | 25 |
2 files changed, 47 insertions, 4 deletions
diff --git a/src/main/scala/firrtl/options/StageAnnotations.scala b/src/main/scala/firrtl/options/StageAnnotations.scala index 2b12dd4e..584f2842 100644 --- a/src/main/scala/firrtl/options/StageAnnotations.scala +++ b/src/main/scala/firrtl/options/StageAnnotations.scala @@ -71,7 +71,33 @@ trait CustomFileEmission { this: Annotation => val name = view[StageOptions](annotations).getBuildFileName(baseFileName(annotations), suffix) new File(name) } +} + +/** A buffered version of [[CustomFileEmission]] + * + * This is especially useful for serializing large data structures. When emitting Strings, it makes + * it much easier to avoid materializing the entire serialized String in memory. It also helps + * avoid materializing intermediate datastructures in memory. Finally, it reduces iteration + * overhead and helps optimize I/O performance. + * + * It may seem strange to use `Array[Byte]` in an otherwise immutable API, but for maximal + * performance it is best to use the JVM primitive that file I/O uses. These Arrays should only + * used immutably even though the Java API technically does allow mutating them. + */ +trait BufferedCustomFileEmission extends CustomFileEmission { this: Annotation => + + /** A buffered version of [[getBytes]] for more efficient serialization + * + * If you only need to serialize an `Iterable[String]`, you can use the `String.getBytes` method. + * It's also helpful to create a `view` which will do the `.map` lazily instead of eagerly, + * improving GC performance. + * {{{ + * def getBytesBuffered: Iterable[Array[Byte]] = myListString.view.map(_.getBytes) + * }}} + */ + def getBytesBuffered: Iterable[Array[Byte]] + final def getBytes: Iterable[Byte] = getBytesBuffered.flatten } /** Holds the name of the target directory diff --git a/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala b/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala index 0e26a5f7..2cf4c92f 100644 --- a/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala +++ b/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala @@ -4,7 +4,16 @@ package firrtl.options.phases import firrtl.AnnotationSeq import firrtl.annotations.{Annotation, DeletedAnnotation, JsonProtocol} -import firrtl.options.{CustomFileEmission, Dependency, Phase, PhaseException, StageOptions, Unserializable, Viewer} +import firrtl.options.{ + BufferedCustomFileEmission, + CustomFileEmission, + Dependency, + Phase, + PhaseException, + StageOptions, + Unserializable, + Viewer +} import java.io.{BufferedOutputStream, File, FileOutputStream, PrintWriter} @@ -38,9 +47,17 @@ class WriteOutputAnnotations extends Phase { filesWritten.get(canonical) match { case None => val w = new BufferedOutputStream(new FileOutputStream(filename)) - a.getBytes match { - case arr: mutable.WrappedArray[Byte] => w.write(arr.array.asInstanceOf[Array[Byte]]) - case other => other.foreach(w.write(_)) + a match { + // Further optimized emission + case buf: BufferedCustomFileEmission => + val it = buf.getBytesBuffered + it.foreach(bytearr => w.write(bytearr)) + // Regular emission + case _ => + a.getBytes match { + case arr: mutable.WrappedArray[Byte] => w.write(arr.array.asInstanceOf[Array[Byte]]) + case other => other.foreach(w.write(_)) + } } w.close() filesWritten(canonical) = a |
