Skip to content

Conversation

@pergardebrink
Copy link
Contributor

@pergardebrink pergardebrink commented Mar 5, 2021

This fixes #36165, #36025, and #48873.

I tossed my first fix I did that checked the array reference on every iteration in the loop and used the snippet @stephentoub proposed in the following discussion that was more elegant: #38734

Not sure the FormatQuick (naming is hard!) is really needed (a few more bytes allocated according to my tests), but the LoggerMessage is supposed to be used for high-performance logging scenarios, so maybe worth it anyways.

I created a pull-request in benchmarks for related microbenchmarks: dotnet/performance#1708

@ghost
Copy link

ghost commented Mar 5, 2021

Tagging subscribers to this area: @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details

This fixes #36165, #36025, and #48873.

I tossed my first fix I did that checked the reference on every iteration in the loop and used the snippet @stephentoub proposed in the following discussion that was more elegant: #38734

Not sure the FormatQuick (naming is hard!) is really needed (a few more bytes allocated according to my tests), but the LoggerMessage is supposed to be used for high-performance logging scenarios, so maybe worth it anyways.

I created a pull-request in benchmarks for related microbenchmarks: dotnet/performance#1708

Author: pergardebrink
Assignees: -
Labels:

area-Extensions-Logging

Milestone: -

@pergardebrink
Copy link
Contributor Author

The JsonConsoleFormatter (and any logging provider using the state) will have a breaking change now that the state is passed to the log provider exactly as provided, without being mutated (which, in my opinion, was a bug before and was the reason I filed #48873).

Does this need to be added to any documentation/release notes if this pull-request is accepted?


// Assert
Assert.Single(testSink.Writes);
var write = testSink.Writes.First();
Copy link
Member

Choose a reason for hiding this comment

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

var [](start = 12, length = 3)

please specify the type instead of var in the lines that is not clear what type the RHS will return

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 was a bit confused on if I should use var or not as I've read the coding guidelines for the dotnet repos that states that the type should be specified, but also to keep the existing style of a file, even if it breaks the guidelines.

In this case I copied the structure of the test from another test to keep the style consistent with the others, so that's why I kept using var here. I'll fix my added test to use explicit types, but leave the existing ones in the file as they are then?

var param3 = new[] { 1, 2, 3, 4 };
var param4 = "string";

var logValues = new FormattedLogValues(format, param1, param2, param3, param4);
Copy link
Member

Choose a reason for hiding this comment

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

var [](start = 12, length = 3)

same, please use the type instead of var.

@tarekgh
Copy link
Member

tarekgh commented Mar 22, 2021

@pergardebrink the change looks good. Thanks for adding the benchmark for logging formatting too. could you please paste here some perf numbers before and after your changes showing the memory allocations?

CC @stephentoub @davidfowl

{
object?[]? formattedValues = values;

if (values != null)
Copy link
Member

Choose a reason for hiding this comment

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

IMO there should be a comment here explaining why this is happening

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Explaining why it does an allocation of a new array in case the value would be changed? Yes, I'll add a comment about that!

return string.Format(CultureInfo.InvariantCulture, _format, formattedValues ?? Array.Empty<object>());
}

internal string FormatQuick(object?[]? values)
Copy link
Member

Choose a reason for hiding this comment

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

Should be a comment explaining when you can use FormatQuick vs Format

Copy link
Contributor Author

@pergardebrink pergardebrink Mar 27, 2021

Choose a reason for hiding this comment

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

Makes sense, I'll add a note to the FormatQuick/FormatWithOverwrite explaining that it mutates the array! Do you think it's necessary to have a note/hint about this method on the Format method as well?

@tarekgh
Copy link
Member

tarekgh commented Mar 26, 2021

@pergardebrink could you please address the remining feedback so we can move forward merging this PR?

@pergardebrink
Copy link
Contributor Author

pergardebrink commented Mar 27, 2021

@pergardebrink the change looks good. Thanks for adding the benchmark for logging formatting too. could you please paste here some perf numbers before and after your changes showing the memory allocations?

Is there any special scenarios you'd like to see? The benchmark I added to the performance repo for the case with enumerable parameter, along with existing ones look like the following, but I can run some extra variations on number of arguments. How many arguments to a log message would be interesting to see. In our codebase at our company, I'd say most log messages have on average 2-3, arguments up to a maximum of 10?:

Before my changes:

Method Mean Error StdDev Median Min Max Gen 0 Gen 1 Gen 2 Allocated
TwoArguments_DefineMessage 161.13 ns 1.679 ns 1.571 ns 161.15 ns 158.29 ns 164.28 ns 0.0090 - - 80 B
FourArguments_DefineMessage 268.59 ns 2.042 ns 1.910 ns 269.19 ns 264.81 ns 271.03 ns 0.0207 - - 176 B
NoArguments 26.89 ns 0.238 ns 0.222 ns 26.84 ns 26.51 ns 27.35 ns - - - -
TwoArguments 201.28 ns 2.284 ns 2.243 ns 201.52 ns 197.89 ns 206.11 ns 0.0139 - - 120 B
FourArguments_EnumerableArgument 875.65 ns 10.929 ns 10.223 ns 879.34 ns 850.23 ns 885.08 ns 0.0702 - - 600 B

After my changes:

Method Mean Error StdDev Median Min Max Gen 0 Gen 1 Gen 2 Allocated
TwoArguments_DefineMessage 160.32 ns 1.423 ns 1.331 ns 160.58 ns 157.56 ns 162.81 ns 0.0091 - - 80 B
FourArguments_DefineMessage 268.60 ns 2.429 ns 2.272 ns 268.77 ns 263.66 ns 272.80 ns 0.0208 - - 176 B
NoArguments 27.28 ns 0.549 ns 0.564 ns 27.36 ns 26.49 ns 28.17 ns - - - -
TwoArguments 196.46 ns 2.386 ns 2.232 ns 196.56 ns 193.16 ns 200.26 ns 0.0142 - - 120 B
FourArguments_EnumerableArgument 895.14 ns 6.801 ns 6.362 ns 895.47 ns 886.72 ns 908.54 ns 0.0783 - - 656 B

@pergardebrink
Copy link
Contributor Author

pergardebrink commented Mar 27, 2021

Added local benchmark for 10 arguments with/without an enumerable also (the enumerable item, which was an int[9] array, came in position 5 in the array)

Before my changes:

Method Mean Error StdDev Median Min Max Gen 0 Gen 1 Gen 2 Allocated
TenArguments_EnumerableArgument 1,187.31 ns 25.175 ns 28.992 ns 1,195.83 ns 1,130.22 ns 1,235.98 ns 0.0888 - - 768 B
TenArguments_NoEnumerableArgument 624.23 ns 4.705 ns 4.401 ns 622.92 ns 618.17 ns 632.00 ns 0.0388 - - 344 B

After my changes:

Method Mean Error StdDev Median Min Max Gen 0 Gen 1 Gen 2 Allocated
TenArguments_EnumerableArgument 1,201.41 ns 4.526 ns 4.013 ns 1,201.59 ns 1,194.70 ns 1,208.46 ns 0.1001 - - 872 B
TenArguments_NoEnumerableArgument 620.78 ns 18.114 ns 20.860 ns 616.15 ns 598.21 ns 669.17 ns 0.0409 - - 344 B

@tarekgh
Copy link
Member

tarekgh commented Mar 28, 2021

@pergardebrink thanks a lot for updating the code and getting the perf numbers.

@davidfowl @stephentoub there is some extra allocations happening but this to fix the original concern. I am seeing the result is reasonable. do you have any feedback before we go ahead and merge this PR?

@davidfowl
Copy link
Member

As long as the extra allocations are when there's an enumerable then that's fine

@tarekgh tarekgh merged commit 8fc5789 into dotnet:main Mar 29, 2021
@tarekgh
Copy link
Member

tarekgh commented Mar 29, 2021

@pergardebrink thanks a lot for your help with this issue!

@ghost ghost locked as resolved and limited conversation to collaborators Apr 28, 2021
@karelz karelz added this to the 6.0.0 milestone May 20, 2021
@pergardebrink pergardebrink deleted the pergardebrink/issue-36025 branch June 10, 2024 23:11
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ILogger mutates the array of objects to format

6 participants