aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/scala/firrtl/options/StageAnnotations.scala26
-rw-r--r--src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala25
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