Skip to content

Avoid caching indentation data that changes over time#61304

Merged
sharwell merged 6 commits intodotnet:mainfrom
sharwell:non-lazy
May 17, 2022
Merged

Avoid caching indentation data that changes over time#61304
sharwell merged 6 commits intodotnet:mainfrom
sharwell:non-lazy

Conversation

@sharwell
Copy link
Copy Markdown
Contributor

Suggested review strategy: commit-by-commit

Fixes #60437

@sharwell sharwell requested a review from a team as a code owner May 13, 2022 15:38
@ghost ghost added the Area-IDE label May 13, 2022
(baseIndentationData, totalAdjustment) = (indentationData, 0);
}

return indentationData.Indentation + indentationDelta;
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.

📝 indentationData.Indentation could be a computed property against values that change during formatting. The result of this computation is no longer directly cached.

Comment on lines -201 to -202
var baseIndentation = _tokenStream.GetCurrentColumn(operation.Option.IsOn(IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine) ?
_tokenStream.FirstTokenOfBaseTokenLine(operation.BaseToken) :
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.

📝 baseIndentation is computed from properties that can change during formatting. This value is no longer cached.

}

public IndentationData BaseIndentationData { get; }
public int Adjustment { get; }
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 wouldn't mind some docs on this. the relation of base-indentation, adjustment, and text span aren't super clear.

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.

I'm not sure what the text span is for, but I can add docs to explain the other two. 😄

{
private readonly Lazy<int> _indentationGetter;
public LazyIndentationData(TextSpan textSpan, Lazy<int> indentationGetter)
private readonly Lazy<int> _lazyIndentationDelta;
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.

so the idea is htat we wait until we actually need this, then comput it, then cache it at that point?

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.

That was the idea even before this PR. The problem stemmed from the fact that too much of the computed result was captured in that lazily-computed value. The part that changes is now a separate lambda which is called every time instead of cached.

protected abstract IndentationData WithTextSpanCore(TextSpan span);
}

private class RootIndentationData : SimpleIndentationData
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.

👍

if (existingIndentationDelta != UninitializedIndentationDelta)
return existingIndentationDelta;

return indentationDelta;
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.

confused by this code. very very confused by it. needs comments majorly. if this is just an int, it's unclear to me why we need volatile at all.

if this is to somehow allow multiple callers to initialize, but let first win, why not let any win?

if this is to allow multiple callers to initialize, and they'll get different results... then 😨

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.

This is the standard optimistic lazy initialization pattern, but I've extracted a helper method so it's reusable.

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 guess my point is: for integer values, where all callers will produce the same value, it seems liek with normal .net memory model this can be trivial. just:

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.

var result = _lazyIndentationDelta;
if (result == UninitializedIndentationDelta
    result = _lazyIndentationDelta = _indentationDeltaGetter(_formattingContext, Operation);

return result;

Copy link
Copy Markdown
Contributor Author

@sharwell sharwell May 16, 2022

Choose a reason for hiding this comment

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

... where all callers will produce the same value ...

The code does not assume or require this. It's not desirable for this to be violated, but if it is we have the most reasonable available outcome.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

Overall, def needs more doc-comments and explanation/example comments in code. And there is one piece of code that i really do not like in the current state :)

return existingValue;

return InterlockedStore(ref target, uninitializedValue, valueFactory(state));
}
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.

fun :)

@sharwell sharwell merged commit faf6e25 into dotnet:main May 17, 2022
@ghost ghost added this to the Next milestone May 17, 2022
@sharwell sharwell deleted the non-lazy branch May 17, 2022 18:32
@Cosifne Cosifne modified the milestones: Next, 17.3 P2 May 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Formatter requires two passes for comments in lambdas

3 participants