I tried to use the "Find all references" feature of F# power tools on a big solution. In certain cases, the analysis would was very slow (I did not measure how slow, I killed devenv.exe after one hour). A quick profiling showed that the FSharp.Compiler.Service spent all its time running IncrementalBuilder.GetLogicalTimeStampForProject. I then tried to reproduce the degenerative behaviour on a simple example and came up with this.
- A simple solution with 4 projects A, B, C and D.
- B references A
- C references A and B
- D references A, B, and C
Using F# powertools, finding all references on a type defined in A results in :
- 53 calls of GetLogicalTimeStampForProject on the project A
- 21 calls of GetLogicalTimeStampForProject on the project B
- 7 calls of GetLogicalTimeStampForProject on the project C
I understand that the logical timestamp of a project is computed from the last modified timestamp on all of its files, and recursively from the logical timestamp of all the projects it references. So I believe we're faced with an algorithm that is doing IO with exponential complexity.
In my opinion, simply memoizing the logical timestamps of projects during the computation should solve the problem. I may get enough time in the next weeks to try to do it myself but I thought I would share my analysis in the mean time.