-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Targetted optimisations #870
Conversation
1c49fe8
to
a76015d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is huge!
@@ -1,128 +1,187 @@ | |||
open Names | |||
(** {1 Paths} *) | |||
|
|||
type 'a id = { iv : 'a; ihash : int; ikey : string } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why store the hash instead of letting it be recomputed from the key ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just so we only compute it once.
@@ -16,6 +16,8 @@ | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these diffs make the Paths
module even more annoying. What do you think of getting rid of it ?
It is not abstracting anything from Paths_types
, it's just adding wrapper modules, which are not used much and could be written inline every time we want a Map
or a Set
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm, we could do this. It is certainly a bit annoying as it is. I'd like to hold off just a bit though.
@@ -60,95 +60,96 @@ module General_paths = struct | |||
|
|||
let rec identifier : Paths.Identifier.t t = | |||
Variant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an opportunity to have a shorter output, this could return the key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, though I wasn't thinking about printing the keys when I made them so they might not be particularly legible. In particular they're backward, so Foo.bar
is v_bar.m_Foo
IIRC (I thought many identifiers would have the same prefix, so reversing them might make it quicker to reject when comparing them)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would have no effect for hash
and equal
, only compare
, which is used less.
src/xref2/component.ml
Outdated
let compare a b = Ident.compare (a :> Ident.any) (b :> Ident.any) | ||
let compare a b = | ||
let i1 = match a with `LRoot (_, i) | `LModule (_, i) -> i in | ||
let i2 = match b with `LRoot (_, i) | `LModule (_, i) -> i in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this really have an effect ? I'm fine with specializing cases where the type has only one constructor (TypeMap
) but I would be surprised if this leads to a speed up compared to the overhead of the Map
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, quite a big effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my testing, Ident.int_of_any
compiles to just an argument access (doing just two loads, ignoring the constructor) and is inlined inside Ident.compare
, which then inline the integer comparison (which is not optimized, it does two cmp
).
The new versions seems to compiles to the same thing but then calls the polymorphic compare.
Do you have an input to benchmark this function ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I was doing was turning off all the caches and doing dune build @docgen
with ODOC_BENCHMARK=true
(and with core_kernel changed to core if you've got v0.15)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After your comment I was curious and went back to do some more testing. Looks like the performance benefit I initially saw was just a fluke I think. Averaged over 10 runs, before and after this change I got:
Before: 258.052 ±0.318
After: 257.672 ±0.427
I also ran the same job on all the subsequent commits and got:
Add smart constructors to identifiers and avoid polymorphic compare: 203.102 ±1.15
Simplify paths module: 206.368 ±1.016
Turn off caching in Of_Lang: 167.676 ±0.653
4.07/8 compatibility: 171.707 ±0.532 (this one is a bit annoying!)
Correct Identifier compare function: 175.155 ±0.685 (doh!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have amended the commit so all that is left is the removal of a couple of constructors that weren't used.
@@ -186,16 +186,17 @@ and is_resolved_module_hidden : | |||
fun ~weak_canonical_test -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR but weak_canonical_test
is very rarely passed through, almost always set to a constant. Would it make sense to remove this argument but do extra check at the call site ? eg. change is_resolved_module_hidden ~weak_canonical_test:true p
into is_weak_canonical p || is_resolved_module_hidden p
,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I prefer it as one function - though we could make the weak_canonical_test
an optional parameter.
src/model/paths.ml
Outdated
|
||
let compare = compare | ||
let compare x y = compare x.ihash y.ihash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't correspond to equal
, should compare the key when the hashes matches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good spot thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed now
a76015d
to
df5ac5b
Compare
Use map adds instead - this is much more efficient
Use Odoc_model.Paths.Path.*.t as the RHS of the cpath canonical constructors. These should always be absolute paths, so there should be no need to translate to/from cpaths
The problem here is that if the canonical path for a type contains a canonical module then we need to resolve that to ensure the identifiers are correct, which is required by the logic to select the 'minimal' canonical path. We didn't hit this in practise until the subsequent commit.
Explicitly unresolve the 'source' path, on the basis that we're way more likely to use the destination.
When paths don't use local idents, we keep them as Odoc_model.Paths.Path* to avoid unnecessary round trips.
This adds a string representation of the identifier that can be reliably compared in place of the polymorphic compare that was previously being used.
df5ac5b
to
2418960
Compare
Spotted in PR review by @Julow
2418960
to
ca1d61c
Compare
For completeness, the timing stats for most of the commits: master: 620s first few were one run only so no stats. |
Annoyingly 4.02 doesn't allow doc comments on type constructors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty huge improvements ! Let's merge
dune
Outdated
@@ -4,7 +4,7 @@ | |||
(env | |||
(dev | |||
(flags | |||
(:standard -g -w -18-53))) | |||
(:standard -g -w -18-53-50))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be disabled only in the module it's needed. It's a 4.02 compat thing so I'd prefer if it was less permanent.
CHANGES: Additions - New unstable option `--as-json` for the HTML renderer that emits HTML fragments (preamble, content) together with metadata (table of contents, breadcrumbs, whether katex is used) in JSON format. (@sabine, ocaml/odoc#908) - New maths support via `{m ... }` and `{math ... }` tags. (@giltho, @gpetiot, ocaml/odoc#886) - Various optimisations (@jonludlam, ocaml/odoc#870, ocaml/odoc#883) - Better handling of alerts and deprecation notices. (@panglesd, ocaml/odoc#828) - Handle language tags on code blocks (@Julow, ocaml/odoc#848) Bugfixes - Shadowing issues (@jonludlam, ocaml/odoc#853) - Layout fixes and improvements (@panglesd, ocaml/odoc#832, ocaml/odoc#839, ocaml/odoc#847) - Handle comments on class constraints and inherit (@Julow, ocaml/odoc#844) - Disable the missing root warning (@jonludlam, ocaml/odoc#881)
Building on the other PRs, this one contains a set of optimisations.