OCaml 5.3 compatibility#38
Open
fantazio wants to merge 15 commits intoLexiFi:masterfrom
Open
Conversation
7df2f25 to
87f8070
Compare
Open
bcd17f9 to
b1d3dbb
Compare
ca1d7c1 to
f72000d
Compare
Naively fix compilation issues. In particular, the use of `cmt_declaration_dependecies` instead of `cmt_value_dependencies` is a simple translation from the latter to the former and is not exactly equivalent because `uid`s may not be found in the `cmt_uid_to_decl` table. This is certainly the root cause of the introduced FP and FN
This will be used in by the state component
If a compilation has a`.mli`, then it has a `.cmti` which is more complete than the `.cmi`. This extra information is useful to fill the state's `file_infos`. However, a `.ml` wihtout a corresponding `.mli` does not have a corresponding `.cmti`. Hence, introducing even more mistakes for now. Now that interfaces are read from `.cmti` instead of `.cmi`, the order in which the files are read is reversed to ensure interfaces (`.cmti`) are read before their corresponding implementation (`.cmt`)
If a `.ml` has a `.mli` then its interface is available as `cmi_infos` in the `.cmti`. Otherwise, there is no `.cmti` and it is available in the`.cmt`. All FN and FP introduced by the previous commit are fixed.
Using information available in the `.cmt`, store locations of uids coming from `include`s in a new state field : `state.signature.uid_to_loc`. Using `.cmt` and `.cmti`, store locations of values' (and patterns in value bindings) uids at the same location for easier lookup. This is done as soon as the files are loaded, before the analysis. The new table tries to be semantically equivalent to the usage of `cmt_value_dependencies` instead of `cmt_declaration_dependencies` before OCaml 5.3. This fixes the FP and FN introduced when moving to OCaml 5.3.
Rather than adding a `signature` field to the state to hold a `uid_to_loc` table to then use in `DeadCode.load_file` to convert uids into locations, to then convert `cmt_declaration_dependencies` into a list of pairs of locations, directly construct that list and store along with the `file_infos`. This move is more consistent with the nature, usage and provenance of the information : it is created using info in `cmt_infos` and `cmti_infos` to replace the `cmt_value_dependencies` field that existed prior to OCaml 5.3, and should only live as long the corresponding `cmt_infos` live.
Only keep useful information from the cm*_infos rather than the whole structure. This makes clearer what is actually used from those and reduces the error surface.
This fixes the remaining FP/FN
This significantly improves the performances (compared to the previous commit) measured on Frama-C : -84% time, same memory. This is still however noticeably less performant than the OCaml 5.2 -compatible version : +246% time, +39% memory.
This replaces the previous local cache in location dependencies computation. This improves the performance measured on Frama-C : -56% time, +10% memory. Compared to the OCaml 5.2-compatible version : +51% time, +52% memory.
This table drastically speeds up the lookup for the corresponding cmt/cmti from the compilation unit when recreating location_dependecies. This improves the performance measured on Frama-C : -33% time, +7% memory. Compared to the OCaml 5.2-compatible version : +1% time, +45% memory.
This will bcontrol the cache size used internally
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
To merge after release 1.1.1
Other than updating the OCaml version to 5.3 and fixing the compilation mistakes, this PR includes 2 important changes :
.cmtifiles are used instead of.cmifiles. Thecmi_infosare now read from either the.cmtiif it exists or the.cmtotherwise. This simplifies a lot the initialization ofState.File_infos.t. This type has evolved to only expose the information used from the variouscm*_infosread. Using.cmtiprovides information used in the next point.the
cmt_infos.cmt_value_dependenciesis recreated (as a list of pairs of locations, to only expose what is actually used). Indeed, it is replaced bycmt_declaration_dependencieswhich exposes a lot more dependencies than only values, and some uids in that list are missing in thecmt_infos.cmt_uid_to_decl. To recreate the value dependencies, the latter field of both the.cmtand.cmtifiles are used, and missing uids' locations are retrieved by opening their corresponding.cmti/.cmt(if provided).Introducing a cache for cmt files
Point 2 above may require reading multiple cmt/cmti files to retrieve their
cmt_uid_to_decltables in order to construct a single equivalent to thecmt_value_dependencies. This strongly and negatively impacts the performance, hindering its use on real life projects. E.g. on Frama-C, the execution would take >5min on my machine vs ~10s when using the 5.2-compatible version of the analyzer.Important
Having to read multiple cmt/cmti files to reconstruct the equivalent of the
cmt_value_dependenciesis expected as explained in the documentation of ShapesNote
Merlin relies on caching as well. In particular, it uses a cache in its shape reduction functions.
We cannot rely on shape reductions because shapes contain Uids and we rely on the locations dependencies at the end of the analysis of each compilation unit.
A possible future improvement would be to rely less on locations during the analysis and only resolve uids to their corresponding locations after the analysis (and before reporting of course). In this situation, the uid -> loc associations and uid -> uid dependencies would be stored to be used all at once during the final resolution.
Caching mechanism
The implemented caching mechanism is very naive :
--cmt_cache_size; default is 64);It uses a Hashtbl for speed, and hit/miss statistics are gathered during the execution. These statistics can be displayed by calling
Cmt.print_cache_stats ().Speed improvement
In order to scale the tool the large projects, a cache is introduced to keep cmt/cmti files in memory. Although the cache size is limited (to avoid moving the performance issue from speed to memory), it drastically reduces the IO operations and the result is comparable to the 5.2-compatible version.
The table below details time measurements (using time) of the OCaml 5.2-compatbile version and this PR's version of the dead_code_analyzer on Frama-C, and their relative difference (
5.3 / 5.2 - 1) :As we can see, the speed of the new version is of the same order of magnitude as the OCaml 5.2-compatible version. It sometimes appears faster but the methodology used is not precise enough to assert that. The differences are mostly within the error margin.
As a point of comparison, the new version without cache takes 308.82s with the
--nothingoption, and 333.08s with the--alloption, making it ~30 times slower.Impact on memory
Caching the cmt/cmti files has a cost. The table below details memory measurements (the maximum resident size returned by time) of the OCaml 5.2-compatbile version and this PR's version of the dead_code_analyzer on Frama-C, and their relative difference (
5.3 / 5.2 - 1) :As we can see, the memory consumption almost reaches a 60% increase in some cases. As before, the methodology is not precise enough to assert anything but the change in order of magnitude is clear.
Selecting the cache size
The default cache size is 64. It has been selected because it is the smallest power of 2 with similar time performance to the OCaml 5.2-compatible version.
A new command line option
--cmt_cache_sizegives the user control over the maximum amount of cmt/cmti files cached.The 2 tables below details the time and memory performance of different cache sizes compared to the previous cache size and compared to a cache size = 64, measured on Frama-C.
As we can see, the time performance improves as the cache size increases. The improvement cache size doubling reduces, displaying a logarithmic behavior.
More surprisingly, the maximum resident size decreases as the cache size increases at first, then increases at cache size = 32, stabilizes until cache size = 128, and finally increases again. Unlike for time performance, the impact on the memory is less predictable. I have not investigated this behavior in details (I suspect the measurement methodology and the GC to be mostly responsible).
Results change
The test-suite results are identical (module a few blank lines rightfully removed). The results on Frama-C change a little as detailed in the table below. This table describes the number of reports produced for a given section (or the combination of them all for
--all) and a version of the analyzer. It also provides the actual number of deletions and additions from the OCaml 5.2-compatible version to the OCaml 5.3-compatible. Finally, it shows the relative differences, compared to the OCaml 5.2-compatible version for deletions, the 5.3-compatible for additions, and the 5.2-compatible for overall (deletions + additions)As we can see, the update to OCaml 5.3 does not modify the results too much overall (<4%). However the changes in the optional arguments never used are important (>12%).
After triaging the changes, I was able to obtain the results described in the table below. Deletions that were false positives and additions that were false negatives are discarded from the difference.