-
Notifications
You must be signed in to change notification settings - Fork 326
Precompute the normalized type names of DataTypes and use string.Create on NS2.1 for faster normalization. #364
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
Precompute the normalized type names of DataTypes and use string.Create on NS2.1 for faster normalization. #364
Conversation
…te on NS2.1 for faster normalization.
@@ -1,7 +1,7 @@ | |||
<Project Sdk="Microsoft.NET.Sdk"> | |||
|
|||
<PropertyGroup> | |||
<TargetFramework>netstandard2.0</TargetFramework> | |||
<TargetFrameworks>netstandard2.0; netstandard2.1</TargetFrameworks> |
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.
Do we also want netcoreapp2.1
, so we get the perf improvements there? Or is getting them only on .NET Core 3.0+ ok?
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 don't mind adding the ncapp2.1 TFM if there are no concerns with doing so. If there are other perf benefits of adding netcoreapp2.1 specifically, then let's do it. Otherwise, this change alone might not warrant doing so.
Debug.Assert(s_complexTypeNormalizedNames == null); | ||
BuildNormalizedStringMapping(); | ||
} | ||
return s_simpleTypeNormalizedNames.AsSpan().IndexOf(typeName); |
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 AsSpan
here? Why not use Array.IndexOf
?
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.
Habit/personal preference to use spans :)
Array.IndexOf
works fine (and is effectively equivalent). Shall I change it?
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.
Your call. It was just surprising to get a span just for IndexOf.
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.
Looks like GitHub is asking for another approval :) |
Thanks @ahsonkhan for the PR! This code is not in a critical path, but I will evaluate if it's worthwhile for extra complexity. @suhsteve can you also take a look? Thanks! |
private static string[] s_simpleTypeNormalizedNames = null; | ||
private static string[] s_complexTypeNormalizedNames = null; |
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.
Can we replace this with something like
private static Lazy<string[]> s_simpleTypeNormalizedNames =
new Lazy<string[]>(
() => s_simpleTypes.Select(t => NormalizeTypeName(t.Name)).ToArray());
private static Lazy<string[]> s_complexTypeNormalizedNames =
new Lazy<string[]>(
() => s_complexTypes.Select(t => NormalizeTypeName(t.Name)).ToArray());
With this you should be able to get rid of
private static int SimpleTypeIndex(string typeName)
private static int ComplexTypeIndex(string typeName)
private static void BuildNormalizedStringMapping()
as well as remove the if check in
private static string NormalizeTypeName(Type type)
{
if (s_simpleTypeNormalizedNames == null)
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.
With this you should be able to get rid of
Even with lazy initialization, how can we get rid of SimpleTypeIndex
and ComplexTypeIndex
? We still need to figure out what index the incoming type maps to in the normalized names array, no?
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.
Can you call
s_simpleTypeNormalizedNames.Value.IndexOf(typeName)
at the callsite instead ?
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.
Sure. I was thinking of keeping it a method to add doc comments.
/// <summary> | ||
/// Normalized type name. | ||
/// </summary> | ||
public string TypeName => NormalizeTypeName(GetType().Name); | ||
public string TypeName => NormalizeTypeName(GetType()); |
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.
Instead of having a new method for this, it may be simpler to normalize the name and cache the result. Something like
private string _typeName;
/// <summary>
/// Normalized type name.
/// </summary>
public string TypeName
{
get
{
return _typeName ?? (_typeName = NormalizeTypeName(GetType().Name));
}
}
Fyi, the
I suspect |
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.
LGTM, thanks @ahsonkhan
@eerhardt, before I merge this PR, I have a naive question. Do you see any cons of targeting multiple frameworks going forward? I don't see any, but just wanted to confirm since you are an expert on this. Thanks! |
@ahsonkhan Just a general comment, but if you really wanted, we can hardcode and create a mapping between the typename => type and use this instead of doing a linear search of the Array for the index of the type. |
The only real con is that you need to build multiple times, but since this repo is small, it won't matter. This change will only help future code that wants to take advantage of new APIs (typically for perf reasons, like this change). |
I noticed some areas where the implementation could be made more performant by avoiding using Linq and caching the normalized type names for the simple and complex DataTypes. That way, they don't have to be computed every time. I am not sure if the code paths are perf critical, in which case, I would understand if this PR isn't useful. If that's the case, please let me know.
Also, added a call to use
string.Create
to make normalizing the type names a bit faster (and allocate less). If adding a netstandard2.1 TFM isn't feasible, please let me know and I can revert that part of the change.The behavior isn't being changed at all, hence existing tests should be sufficient.
Accessing the
TypeName
property for the known simple/complex types got much faster:Normalizing a new type name (using string.Create) reduces allocations:
cc @eerhardt