Closed8

scala.js の IRCache

tanishikingtanishiking

IRをJARとかファイルからインメモリに読んでくるIO操作をキャッシュしている。
これどれくらい早くなんの???

sbt plugin の場合キャッシュの読み書きは ScalaJSPluginInternal.scalascalaJSIR task の中から。

  • なんかコメントも Update IR Cache とか行って cache を保存しますみたいな顔してるけど 、実際は IRContiner -> IRFile への変換。
  • 多分だけど IRContainer は File (path) や JAR への参照で、IRFile はその中身
  • IRContainer を受け取って、その中身をインメモリに展開するところをキャッシュしてる

cache を instantiate して

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPluginInternal.scala#L365-L366

コメントに
cache.cachedIRContainer から IRFile に変換、

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/sbt-plugin/src/main/scala/org/scalajs/sbtplugin/ScalaJSPluginInternal.scala#L392-L401

tanishikingtanishiking

IRContainer ってなんですか?

"A virtual file containing Scala.js IR." container 自体は path (JAR or SJSIR の file か...) と、version (hash とか、mtimeとか) を持つ。この version を使って invalidate するんかな。
sjsirFiles メソッドが生えていて、これでsjsirの中身を取得できるらしい

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/IRContainer.scala#L19-L21

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/unstable/IRContainerImpl.scala#L22-L54

tanishikingtanishiking

いかれた IRContainer の実装を紹介するぜ

NodeJARIR

sjsirを含む JAR ファイルへの参照

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/js/src/main/scala/org/scalajs/linker/NodeIRContainer.scala#L81-L103

def sjsirFiles は JAR から file 読んで MemIRFileImpl 返す。

別の実装

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker-interface/shared/src/main/scala/org/scalajs/linker/interface/IRContainer.scala#L24-L29

うーん?なんか IRFile から IRContainer 作ってるだけ、生の sjsir ファイルから作るんやろうな。

tanishikingtanishiking

IRFile の実装

NodeIRFileImpl

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/js/src/main/scala/org/scalajs/linker/NodeIRFile.scala#L46-L47

treeの実装がこんな感じでfile読み込みをしていることから、ファイルシステム上のファイルを指す IRFile なんだろうな

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/js/src/main/scala/org/scalajs/linker/NodeIRFile.scala#L88-L93

MemClassDefIRFileImpl

単一 classdef を持つ IR のインメモリ表現

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/shared/src/main/scala/org/scalajs/linker/standard/MemClassDefIRFileImpl.scala#L23-L35

PersistentIRFile

これ実は cache から帰ってくる IRFile の実装はこれになってる。
引数に IRFileImpl 受け取って、loadTree で underlying +irFile を読み込んでるだけ

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala#L213-L247

tanishikingtanishiking

IRContainer -> IRFile with cache のおさらい

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala#L62-L68

こんな感じでしたね PersistedFile って何?

PersistedFile

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala#L107-L115

はいはい ._filesSeq[PersistedIRFile] を返すのね。この _files はどこで更新されるのかというと update というメソッド

https://github.com/scala-js/scala-js/blob/3fe1c302329ca8da7d19fd95809bd669f63b3737/linker/shared/src/main/scala/org/scalajs/linker/standard/StandardIRFileCache.scala#L180-L206

なんかこういうのやってるね

           _files = {
              performIO(file.sjsirFiles).map { files =>
                files.map { file =>
                  new PersistentIRFile(IRFileImpl.fromIRFile(file))
                }
              }

version.sameVesrion がどうのこうのみたいなのやってるしここで cache invalidate してそう

まず最初に _version (前回読み込んだ IRContainer の version) を比較、同じバージョンなら " yeepeeh, nothing to do" 確かに。statsReused を更新する、なんで?

      if (_version.sameVersion(file.version)) {
        // yeepeeh, nothing to do
        statsReused.incrementAndGet()

なんか synchronized ブロックの中でもやってるけどなんで? マルチスレッドでやってるからだと思うけど、直後に synchronized ブロック内で同じ操作やる意味ある?

        synchronized {
          if (_version.sameVersion(file.version)) {
            // someone else had the same idea and did our work
            statsReused.incrementAndGet()
このスクラップは2023/11/28にクローズされました