Skip to content

Conversation

@powercode
Copy link
Collaborator

@powercode powercode commented Aug 1, 2018

PR Summary

TryConvertTo is implemented in terms of Convert, but with wrapping exception handling.
This is costly performance wise, so by inlining the implementation of ConvertTo, and getting access to the FigureConversion output. Using that we can determine upfront on at least some occations that a conversion is not available.

Fixes #7420

PR Checklist

@powercode powercode force-pushed the LanguagePrimTryCompare branch 2 times, most recently from d403c9b to 250a9bf Compare August 1, 2018 00:26
Check the conversion rank on the supplied conversion to exit early
when we know up front  that the conversion will not succeed.

Also adding TryCompare, that uses TryConvert in it's implementation.
@powercode powercode force-pushed the LanguagePrimTryCompare branch from 250a9bf to 6b56c72 Compare August 1, 2018 00:28
@powercode powercode changed the title WIP: Improving performance of LanguagePrimitives.TryConvertTo. Improving performance of LanguagePrimitives.TryConvertTo. Aug 1, 2018
Copy link
Collaborator

@iSazonov iSazonov left a comment

Choose a reason for hiding this comment

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

It would be nice to see examples that we really get a win.

if (first is string firstString)
{
string secondString = second as string;
if (secondString == null)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why you skip this? I think we should follow one pattern or roll back the change in previous line 804.

Copy link
Member

Choose a reason for hiding this comment

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

@iSazonov I think we should be careful about following one pattern too strictly. I think as string is better here because the if condition is secondString == null. Changing it to if (!(second is string secondString)) would make it less readable, which goes against our goal.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Using different patterns (especially next to each other) also reduces readability - I just stumbled over it.
My suggestion was just to roll back the change at all to old pattern or use one pattern.

try
{
secondConverted = LanguagePrimitives.ConvertTo(second, firstType, culture);

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove the extra line.

{
result = (T)valueToConvert;
result = variable;
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd replace variable with value or valueToReturn.

result = (T)ConvertTo(valueToConvert, typeof(T), formatProvider);
if (TryConvertTo(valueToConvert, typeof(T), formatProvider, out var res))
{
result = (T) res;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove extra space after (T). Or maybe use as T as more fast?

}
result = conversion.Invoke(debase ? PSObject.Base(valueToConvert) : valueToConvert,
resultType, true, debase ? (PSObject)valueToConvert : null,
formatProvider, null);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please fix formatting:

result = conversion.Invoke(
             debase ? PSObject.Base(valueToConvert) : valueToConvert,
             resultType,
             recurse: true,
...

Copy link
Collaborator

@iSazonov iSazonov Aug 2, 2018

Choose a reason for hiding this comment

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

Please address the comment and re-format..

}

var conversion = FigureConversion(valueToConvert, resultType, out var debase);
if (conversion.Rank== ConversionRank.None)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add space before ==.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please address the comment and remove extra space
conversion.Rank== ConversionRank.None -> conversion.Rank == ConversionRank.None

return (T)valueToConvert;
return variable;
}
return (T)ConvertTo(valueToConvert, typeof(T), true, CultureInfo.InvariantCulture, null);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Make sense to use return ConvertTo( ... ) as T as more fast?

Copy link
Member

@daxian-dbw daxian-dbw Aug 1, 2018

Choose a reason for hiding this comment

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

I think the perf difference between cast and as is neglectable. And (T)ConvertTo() ensures an exception to be thrown when ConverTo() doesn't really return a T object, which is the expected behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think all exceptions occur in ConvertTo(valueToConvert, typeof(T), true, CultureInfo.InvariantCulture, null); and if the method returns a result then the result is always of type T. So formally we can use as T. I see there are performance comparisons of cast and as operators - last is faster - but I don't know whether it is true for .Net Core 2.1.

public static bool TryConvertTo<T>(object valueToConvert, IFormatProvider formatProvider, out T result)
{
result = default(T);
result = default;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd keep old code as more readable for the generic method.

@iSazonov iSazonov self-assigned this Aug 1, 2018
{
result = default(T);
result = default;
try
Copy link
Member

Choose a reason for hiding this comment

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

The try/catch can be removed, as the exceptions are taken care of in TryConvertTo(valueToConvert, typeof(T), formatProvider, out var res).

public static bool TryConvertTo(object valueToConvert, Type resultType, IFormatProvider formatProvider, out object result)
{
result = null;
result = default;
Copy link
Member

Choose a reason for hiding this comment

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

I think result = null is more explicit here, as the type of result is object here.

Copy link
Collaborator

@iSazonov iSazonov left a comment

Choose a reason for hiding this comment

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

Please look CodeFactor issues.

if (valueToConvert is T variable)
{
return (T)valueToConvert;
return variable;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use value name as we do below.

if (TryConvertTo(valueToConvert, typeof(T), formatProvider, out var res))
{
return false;
result = (T) res;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please remove extra space (T) res -> (T)res.

}

var conversion = FigureConversion(valueToConvert, resultType, out var debase);
if (conversion.Rank== ConversionRank.None)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please address the comment and remove extra space
conversion.Rank== ConversionRank.None -> conversion.Rank == ConversionRank.None

}
result = conversion.Invoke(debase ? PSObject.Base(valueToConvert) : valueToConvert,
resultType, true, debase ? (PSObject)valueToConvert : null,
formatProvider, null);
Copy link
Collaborator

@iSazonov iSazonov Aug 2, 2018

Choose a reason for hiding this comment

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

Please address the comment and re-format..

Copy link
Member

@daxian-dbw daxian-dbw left a comment

Choose a reason for hiding this comment

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

LGTM

@daxian-dbw
Copy link
Member

@powercode I made a minor update to a few style issues. I hope you don't mind. They are as follows:

  • Address @iSazonov's comment about the rename variable to value to be consistent with other instances.
  • Change out var to out object and out bool to make the type of the out parameter a little more explicit.
  • Change the indentation of the parameters for the call to conversion.Invoke.

Thank you again for the great contribution!

@iSazonov iSazonov merged commit 8131374 into PowerShell:master Aug 3, 2018
@iSazonov
Copy link
Collaborator

iSazonov commented Aug 3, 2018

@powercode Thanks for your contribution!

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.

Improve performance of LanguagePrimitives.TryConvertTo

3 participants