Skip to content
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

Avoid attributes recomputation #109

Merged
merged 4 commits into from
Mar 12, 2018

Conversation

vbfox
Copy link
Contributor

@vbfox vbfox commented Mar 12, 2018

Here is another performance PR.

The main performance gain is the removal of repeated calls to GetAttributes and instead doing a single GetAttributes and reusing the values.

I also removed some usage of printf as building the printers take time and it's not very necessary here.

Performance looks like that:

Method Args BypassDependencyGraphChecks Mean Error StdDev
Parse add nuget Foo.Bar False 323.0 ms 2.000 ms 4.041 ms
Parse add nuget Foo.Bar True 205.3 ms 3.239 ms 6.543 ms
Parse install False 314.0 ms 3.090 ms 6.243 ms
Parse install True 179.4 ms 1.554 ms 3.139 ms
Parse restore False 317.9 ms 7.365 ms 14.877 ms
Parse restore True 179.4 ms 2.066 ms 4.173 ms

It can be compared to #105 (comment) so the gain is of ~35% with structure check and ~10% without.

And from that point we're fast enough that dotnet/fsharp#4465 will gain 40% for checked and 50% for no checks, bringing the no check version of add nuget Foo.Bar around 150ms :

k flamegraph1

[<AutoOpen>]
module private FastAttributes =
let inline hasAttribute<'T when 'T :> Attribute> (attributes: obj[]) =
attributes |> Array.tryFindIndex (fun x -> x :? 'T) <> None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't Array.exists suffice here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I need to find a way to remember the name of this method, each time I stay on contains but it's not what I want and I end up rewriting it by other means)

attributes |> Array.tryFindIndex (fun x -> x :? 'T) <> None

let inline hasAttribute2<'T when 'T :> Attribute> (attributes1: obj[]) (attributes2: Attribute[]) =
(hasAttribute<'T> attributes1) || (attributes2 |> Array.tryFindIndex (fun x -> x :? 'T) <> None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

(hasAttribute<'T> attributes1) || (attributes2 |> Array.tryFindIndex (fun x -> x :? 'T) <> None)

let inline tryGetAttribute<'T when 'T :> Attribute> (attributes: obj[]) =
attributes |> Array.tryFind (fun x -> x :? 'T) |> Option.map (fun x -> x :?> 'T)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with Array.tryPick

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array.tryPick (function :? 'T as t -> Some t | _ -> None)

let inline tryGetAttribute<'T when 'T :> Attribute> (attributes: obj[]) =
attributes |> Array.tryFind (fun x -> x :? 'T) |> Option.map (fun x -> x :?> 'T)

let inline tryGetAttribute2<'T when 'T :> Attribute> (attributes: obj[]) (attributes2: Attribute[]) =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use System.Attribute as the type for the first input array. I'm assuming the two arguments are type-level vs. case level attributes? Naming of arguments should convey this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll rename them. But I can't change the type of the first argument, it's what UnionCaseInfo.GetCustomAttributes returns.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure you could map the array though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, everything in it is an Attribute, but why reallocate a second array with the exact same data to end up casting it out anyway ? There is no consumer that uses the fact that they are instances of Attribute.

In fact if it's ok with you i'll find what makes the second array contains Attribute and use obj instead. That's what https://msdn.microsoft.com/en-us/library/kff8s254(v=vs.110).aspx returns and any conversion is unneeded

| _ when Option.isSome appSettingsName.Value -> appSettingsName.Value.Value
| _ -> arguExn "parameter '%O' needs to have at least one parse source." uci)
| _ -> raise <| new ArguException("parameter '" + (uci.ToString()) + "' needs to have at least one parse source."))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of this change, premature optimization where raised exception would be the dominating factor. Please revert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes I need to measure, I changed all the printf initialization but I don't think this one is hit.

uci.GetAttributes<AltCommandLineAttribute>()
|> Seq.collect (fun attr -> attr.Names)
attributes.Value
|> Array.filter (fun x -> x :? AltCommandLineAttribute)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be inlined into a single Array.collect

@eiriktsarpalis eiriktsarpalis merged commit b156991 into fsprojects:master Mar 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants