Skip to content

String performance implementation improvements #2217

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

Merged
merged 2 commits into from
Aug 14, 2018
Merged

String performance implementation improvements #2217

merged 2 commits into from
Aug 14, 2018

Conversation

mitchelsellers
Copy link
Contributor

@mitchelsellers mitchelsellers commented Aug 8, 2018

Improvements to the management and processing of strings. This change was limited to the changes that can be made that would not change behavior, or impact the possibility of a behavior change.

As such, the changes here are not "perfect," but represent the best efforts to improve the operation with strings in the core. The goals here are to avoid calls that result in new strings being generated via calls such as ToLower(), ToUpper() etc and comparisons being made.

The following constructs were followed when making these changes.

  • Implementations that used ToUpperInvariant() or ToLowerInvariant() were moved to use StringComparison.InvariantCultureIgnoreCase
  • Comparisons that were done before with format specifiers but redundant calls to ToLower()/ToUpper() removed

I did not fix the following types of items that at some point in the future might be worth continuing to research.

  • ToLowerInvariant().Contains("") = These should be changed to IndexOf() if possible to avoid the string creation of ToLowerInvariant()
  • Situations where the same variable was called multiple times convertng the value
  • Situations where performance could be further improved by using OrdinalIgnoreCase rather than InvariantCultureIgnoreCase
  • Unique situation where both invariant & Ordnial was used. (DotNetNuke.Services.FileSystem.FolderManager)

In testing the updated installation with these changes I am seeing an exponential reduction in the number of string objects in memory snapshots compared to the baseline version, with indications of less garbage collector cycles. No adverse implementation details.

Fixes #2215

@mitchelsellers mitchelsellers added the Community Legacy label used to identify community contributions label Aug 8, 2018
@mitchelsellers mitchelsellers requested review from bdukes and ohine August 8, 2018 20:22
@david-poindexter
Copy link
Contributor

I believe this should reference #2215 instead. Excellent work @mitchelsellers!

Copy link
Contributor

@bdukes bdukes left a comment

Choose a reason for hiding this comment

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

Thanks, @mitchelsellers, looks solid to me

@@ -153,7 +153,7 @@ private static void WatcherOnChanged(object sender, FileSystemEventArgs e)
if (Logger.IsInfoEnabled && !e.FullPath.EndsWith(".log.resources"))
Logger.Info($"Watcher Activity: {e.ChangeType}. Path: {e.FullPath}");

if (_handleShutdowns && !_shutdownInprogress && (e.FullPath ?? "").ToLowerInvariant().StartsWith(_binFolder))
if (_handleShutdowns && !_shutdownInprogress && (e.FullPath ?? "").StartsWith(_binFolder, StringComparison.InvariantCultureIgnoreCase))
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this change mean we can remove the ToLowerInvariant call on _binFolder?

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 left that call as-is as I wasn't aware of the inherit risk of changing it on the file system watcher.

@@ -557,7 +557,7 @@ Namespace DotNetNuke.UI.Utilities
End Function

Public Shared Function IsInCallback(ByVal objPage As Page) As Boolean
Return Len(objPage.Request(SCRIPT_CALLBACKID)) > 0 AndAlso objPage.Request.HttpMethod.ToUpper() = "POST"
Return Len(objPage.Request(SCRIPT_CALLBACKID)) > 0 AndAlso objPage.Request.HttpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase)
Copy link
Contributor

Choose a reason for hiding this comment

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

the previous behavior of this call would have been culture specific, rather than Ordinal, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct so ordinal sorting seemed most appropriate, I could adjust if desired

@@ -3889,7 +3889,7 @@ public static string GetDBConnectionString()
strExtension = File.Substring(File.LastIndexOf(".") + 1);
}
string FileName = File.Substring(CurrentDirectory.FullName.Length);
if (strExtensions.ToUpper().IndexOf(strExtension.ToUpper()) != -1 || string.IsNullOrEmpty(strExtensions))
if (strExtensions.IndexOf(strExtension, StringComparison.InvariantCultureIgnoreCase) != -1 || string.IsNullOrEmpty(strExtensions))
Copy link
Contributor

Choose a reason for hiding this comment

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

this original behavior would have been culture-specific, is there any real scenario where that matters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not from what I could tell, and being invariant seems to lesson any risk

@@ -199,16 +199,18 @@ private string GetConnectionStringUserID()
string DBUser = "public";

//If connection string does not use integrated security, then get user id.
if (ConnectionString.ToUpper().Contains("USER ID") || ConnectionString.ToUpper().Contains("UID") || ConnectionString.ToUpper().Contains("USER"))
//Normalize to uppervase before all of the comparisons
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: uppervase for uppercase

Copy link
Contributor

Choose a reason for hiding this comment

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

fixed

@@ -2338,8 +2338,9 @@ private static bool IgnoreRequestForInstall(string physicalPath, string refererP

private static bool IgnoreRequestForWebServer(string requestedPath)
{
if (requestedPath.ToLowerInvariant().IndexOf("synchronizecache.aspx", StringComparison.Ordinal) > 1
|| requestedPath.EndsWith("keepalive.aspx", true, CultureInfo.InvariantCulture))
//Should standardize comparison methods
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand this comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

One uses EndsWith, the other looks for it anywhere. I think both could be safe in the future with ends with

Copy link
Contributor

Choose a reason for hiding this comment

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

I was assuming that synchronizecache might have parameters, whereas keepalive is not expected to

Copy link
Contributor

Choose a reason for hiding this comment

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

Correct, synchronizecache always has params. Keepalive shouldn't, but it might since some services add a random number param on requests to avoid caching. I think changing it to indexof would be best.

|| requestedPath.EndsWith("keepalive.aspx", true, CultureInfo.InvariantCulture))
//Should standardize comparison methods
if (requestedPath.IndexOf("synchronizecache.aspx", StringComparison.OrdinalIgnoreCase) > 1
|| requestedPath.EndsWith("keepalive.aspx", StringComparison.OrdinalIgnoreCase))
Copy link
Contributor

Choose a reason for hiding this comment

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

should this one have switched from InvariantCulture to Ordinal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

THe thought here was standardization of the comparisons between the two calls

Copy link
Contributor

@ohine ohine left a comment

Choose a reason for hiding this comment

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

Amazing work Mitch! I can't wait to see the final numbers on performance improvements with these changes. I'll leave it up to you if you agree to change the one function I called out. I totally understand if we want to do that in a future release to reduce any risks of a change in behavior.

folderPath.Equals("skins", StringComparison.InvariantCultureIgnoreCase) ||
folderPath.Equals("containers", StringComparison.InvariantCultureIgnoreCase) ||
folderPath.StartsWith("skins/", StringComparison.InvariantCultureIgnoreCase) ||
folderPath.StartsWith("containers/", StringComparison.InvariantCultureIgnoreCase);
Copy link
Contributor

Choose a reason for hiding this comment

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

This just seems odd. Wouldn't it make more sense to switch to just two checks for contains instead of doing an equals or startswith?

@@ -199,16 +199,18 @@ private string GetConnectionStringUserID()
string DBUser = "public";

//If connection string does not use integrated security, then get user id.
if (ConnectionString.ToUpper().Contains("USER ID") || ConnectionString.ToUpper().Contains("UID") || ConnectionString.ToUpper().Contains("USER"))
//Normalize to uppervase before all of the comparisons
Copy link
Contributor

Choose a reason for hiding this comment

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

fixed

@@ -2338,8 +2338,9 @@ private static bool IgnoreRequestForInstall(string physicalPath, string refererP

private static bool IgnoreRequestForWebServer(string requestedPath)
{
if (requestedPath.ToLowerInvariant().IndexOf("synchronizecache.aspx", StringComparison.Ordinal) > 1
|| requestedPath.EndsWith("keepalive.aspx", true, CultureInfo.InvariantCulture))
//Should standardize comparison methods
Copy link
Contributor

Choose a reason for hiding this comment

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

Correct, synchronizecache always has params. Keepalive shouldn't, but it might since some services add a random number param on requests to avoid caching. I think changing it to indexof would be best.

@ohine ohine added this to the 9.2.2 milestone Aug 9, 2018
@ohine ohine merged commit 2049d2c into dnnsoftware:development Aug 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community Legacy label used to identify community contributions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve String Comparison Performance & Consistency
4 participants