Skip to content

fixing filtered update performance - #3233

Merged
forki merged 3 commits into
fsprojects:masterfrom
viktor-svub:bugfix/update-filter
Jun 5, 2018
Merged

fixing filtered update performance#3233
forki merged 3 commits into
fsprojects:masterfrom
viktor-svub:bugfix/update-filter

Conversation

@viktor-svub

Copy link
Copy Markdown
Contributor
  • update with regex filter, matching 83 packages of 210, resulted in runtime of about 30 minutes, instead of the expected cca 30 seconds of full update
  • most of the time was spent in the filter function, constructing and compiling the regex for each nuget package dependency
  • issue was completely fixed by extracting the regex object creation to be done only single time

Comment thread src/Paket.Core/Common/Domain.fs Outdated
QualifiedPackageName (groupName, packageName)

type PackageMatch(ex:String) =
member this.Expression = Regex("^" + ex + "$", RegexOptions.CultureInvariant ||| RegexOptions.IgnoreCase)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are you sure this no longer creates regex objects on every access?

@viktor-svub viktor-svub May 31, 2018

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

pretty much :)
as far I understand F# this should be constructor syntax, filling the Expression member with result of the expression after equals
and I'm seeing the described improvement on my machine, getting filtered update in approximately same time as full one
edit: including Debug build, so it's not just a result of compiler optimization, and it's indeed most likely called only on the filter instance creation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Decompiled into C#:

		[CompilationMapping(SourceConstructFlags.ObjectType)]
		[Serializable]
		public class PackageMatch
		{
			public PackageMatch(string ex) : this()
			{
				this.ex = ex;
			}

			public Regex Expression
			{
				get
				{
					return new Regex("^" + this.ex + "$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
				}
			}

			internal string ex;
		}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I know there is a syntax to initialize it only once, I think it is something with val. Tbh, in that regard F# is really confusing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And would it maybe make sense to add ``RegexOptions.Compiled`? https://stackoverflow.com/questions/513412/how-does-regexoptions-compiled-work#7707369

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here's a sharplab.io example: https://sharplab.io/#v2:DYLgZgzgPg9gDgUwHYAIDKBPCAXBBbAWAChjsNEUBhACgQA8QUcAnASyQHMBKFAXmJQo8+AEYJmKAG4BDYCgCidOMwQQIrGKl4p6KANQoARCOnNDAwYNLkEVAPoAlBAGMYeOAFdctBk2xtOHn4iQWE8MQlsAAtWCAA6RWVVdU0+HTp9IxMzIA===

You can see here that thanks to member val the property value is computed once on construction and then just reused, whereas for C_Recompute the property is recomputed each access.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note that we don't have access to System.Text.RegularExpressions in sharplab so that's why I didn't repro your scenario exactly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh my... :) Ok, I'll revise it to create the instance only once for sure.
At this moments it seems possible that all the slowdown was caused just by RegexOptions.Compiled while creating instance per comparison in either case.

@matthid

matthid commented May 31, 2018

Copy link
Copy Markdown
Member

Thanks for looking into this, always wondered why this is slower then full update

@viktor-svub

Copy link
Copy Markdown
Contributor Author

OK, I have zero idea what happened in the Travis CI ...
Error in '/usr/bin/mono': realloc(): invalid next size: 0x00000000025e0a70

QualifiedPackageName (groupName, packageName)

type PackageMatch(ex:String) =
member val Expression = Regex("^" + ex + "$", RegexOptions.CultureInvariant ||| RegexOptions.IgnoreCase)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm ok with this. Personally I would probably prefer:

type PackageMatch(ex:String) =
    let regex = Regex("^" + ex + "$", RegexOptions.CultureInvariant ||| RegexOptions.IgnoreCase) 
    member _.Expression = regex

or even

type PackageMatch =
   { Expression : Regex }
module PackageMatch =
   let ofString ex =
     { Expression = Regex("^" + ex + "$", RegexOptions.CultureInvariant ||| RegexOptions.IgnoreCase) }

@forki
forki merged commit e49b28a into fsprojects:master Jun 5, 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.

5 participants