Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Introduce VBufferEditor and hide VBuffer.Count - #1580

Merged
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan
Nov 16, 2018
Merged

Introduce VBufferEditor and hide VBuffer.Count#1580
eerhardt merged 14 commits into
dotnet:masterfrom
eerhardt:MasterVBufferPlan

Conversation

@eerhardt

@eerhardteerhardt commented Nov 8, 2018

Copy link
Copy Markdown
Member

This PR executes on proposed changes (1) and (5) in #608 (comment).

  1. Keep Length, but hide/private Count
  1. The most drastic proposed change is how to actually mutate a VBuffer.

This introduces VBufferEditor, and uses it in almost all the places where VBuffers are mutated. There are a few stragglers that I will fix in a subsequent PR, but I thought this change was large enough, so I cut it off once I could make .Count private.

Working towards #608.

Comment threadsrc/Microsoft.ML.Data/DataView/Transposer.cs
Comment threadsrc/Microsoft.ML.Core/Data/MetadataUtils.cs
Comment threadsrc/Microsoft.ML.Core/Data/VBuffer.cs
Comment threadsrc/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
ValueGetter<VBuffer<ReadOnlyMemory<char>>> keyValueGetter =
(ref VBuffer<ReadOnlyMemory<char>> dst) =>
dst = new VBuffer<ReadOnlyMemory<char>>(keyNamesVBuffer.Length, keyNamesVBuffer.Count, keyNamesVBuffer.Values, keyNamesVBuffer.Indices);
keyNamesVBuffer.CopyTo(ref dst);

@TomFinleyTomFinleyNov 10, 2018

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.

keyNamesVBuffer [](start = 24, length = 15)

Wow. This was a really bad bug. We shared the underlying output buffers among everyone that could have asked for this!!

The more I read of your PR the more enthusiastic I become. I had thought only of the effort it would save me and others going forward of having to catch all these potential misuses of VBuffer, but now I become alarmed at the sheer volume of stuff I seem to have missed, that will now get caught. :P Or more precisely, made impossible. #Closed

Comment threadsrc/Microsoft.ML.Data/Depricated/Vector/VBufferMathUtils.cs
Float mean = Mean(src.Values, src.Count, src.Length);
Float divisor = StdDev(src.Values, src.Count, src.Length, mean);
var srcValues = src.GetValues();
Float mean = Mean(srcValues, src.Length);

@TomFinleyTomFinleyNov 10, 2018

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.

Float [](start = 32, length = 5)

Hi @eerhardt, I notice here and elsewhere you've been trying to maintain this Float stuff. This was an artifact of something roughly four years ago where we tried to maintain separate "double" and "single" builds (this of course before we had IDataView and could in principle accomodate either where appropriate), and we've been opportunistically removing them, since the desired final state is we have none of this at all.

By this I mean you should choose whichever is less effort for you. If you find it's causing you effort to keep doing this Float stuff then please don't bother, it is intended to be removed in due time anyway. But if it's more effort to remove it feel free to keep using it. Just whichever you find more helpful to do, is all. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Noted. I haven't been going out of my way to change Float => float, but I have made a slightly conscious effort to keep existing code the same. My reasoning was that I didn't want unnecessary changes in my PRs.


In reply to: 232436492 [](ancestors = 232436492)

@TomFinleyTomFinleyNov 13, 2018

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.

Makes sense, probably makes merges/rebases easier. #Resolved

for (int j = 0; j < inputOpsValues.Length; j++)
inputOpsResult[j] = inputOpsValues[j].ToString();

yield return (name, opType.ToString(), type, inputOpsResult);

@TomFinleyTomFinleyNov 10, 2018

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.

This code appears to have a bug in it which has been carried over, that is, it appears to assume that the output vector was dense. I'm not sure you want to fix it or not, but if not, maybe file an issue? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It appears that the place that populates this metadata always uses a dense vector. See

dst=newVBuffer<ReadOnlyMemory<char>>(op.NumInputs,inputOps);
.

I can add an assert here, if you'd like. If you want more of a "fix", I can log a bug. Up to you.


In reply to: 232436665 [](ancestors = 232436665)

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.

How about you just call DenseValues() in line 118 and fix the bug?..


In reply to: 232822331 [](ancestors = 232822331,232436665)

@TomFinleyTomFinley left a comment

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.

Thank you @eerhardt ! I'll take my extra ration of milk now... 😉

for (int i = 0; i < source.Length; i++)
{
if (predicate(source[i]))
{

@Zruty0Zruty0Nov 14, 2018

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.

{ [](start = 16, length = 1)

remove curlies for 1-line clauses like this and below #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done.


In reply to: 233650638 [](ancestors = 233650638)

/// with a larger physical value count than was needed
/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>

@Zruty0Zruty0Nov 14, 2018

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.

[](start = 12, length = 19)

remove empty #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixing.


In reply to: 233651783 [](ancestors = 233651783)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

CreateBuffer [](start = 26, length = 12)

should we assert that this only called once? We don't want it to be called multiple times, right?

Also, if we don't, should we call it something like Commit? I can see you already had the name 'Complete' before, and you left it inside a comment. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

It's not possible to assert it is only called once because VBufferMutationContext<T> is a ref struct (and actually I need to mark it as readonly). Since it is a ref struct, it shouldn't change state because you can get in a lot of trouble passing it into methods as parameters, or making copies of it on the stack.

I can change the name of CreateBuffer, if you'd like. Do you have a suggestion? Do you like Commit more than CreateBuffer? I didn't like Complete, but I can be persuaded.


In reply to: 233652648 [](ancestors = 233652648)

/// because the final value count was not known at creation time.
/// </param>
/// <returns></returns>
public VBuffer<T> CreateBuffer(int? physicalValuesCount = null)

@Zruty0Zruty0Nov 14, 2018

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.

physicalValuesCount [](start = 44, length = 19)

how do we use this? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

There are some cases that don't know the final "count" until they are done iterating through a list of mutations. So they allocate "the max necessary" upfront, but may change this value to a smaller count at the end. It isn't often we need this, but there are a handful of places that NEED it.

Here's an example:


In reply to: 233667829 [](ancestors = 233667829)

Array.Clear(values, 0, src.Length);
dst = new VBuffer<Float>(src.Length, values, dst.Indices);
var mutation = VBufferMutationContext.Create(ref dst, src.Length);
if (!mutation.CreatedNewValues) // We need to clear it

@Zruty0Zruty0Nov 15, 2018

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.

t [](start = 73, length = 1)

you lost the period. #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

OOPS. Will fix.


In reply to: 233669776 [](ancestors = 233669776)

}

dst = new VBuffer<TDst>(newLength, iiDst, values, indices);
dst = mutation.CreateBuffer(iiDst);

@Zruty0Zruty0Nov 15, 2018

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.

iiDst [](start = 40, length = 5)

this looks like the only usage of this parameter.
Can we maybe have a separate version of CreateBuffer / Commit? Like, I don't know, CommitTruncated or something? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

This isn't the only usage of the parameter. I pointed out one above in the other comment. And there are more, like in EnsembleUtils.cs. #Resolved

/// An object capable of mutation a <see cref="VBuffer{T}"/> by filling out
/// <see cref="Values"/> (and <see cref="Indices"/> if the buffer is not dense).
/// </summary>
public ref struct VBufferMutationContext<T>

@Zruty0Zruty0Nov 15, 2018

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.

VBufferMutationContext [](start = 22, length = 22)

I'm not a big fan of this name. Is the discussion closed on naming?
Given the usage pattern, maybe we can call it VBufferBuilder or VBufferEditor? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not a big fan of the name either. I'm open to suggestions.

VBufferBuilder is already taken by BufferBuilder<T>. So I don't think we should go with that.

(BTW: some of these "design" discussions would have been great to have been had up front on #608, instead of in the PR 😝) #Resolved

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.

well, well. I didn't actually understand what you're planning to do until I saw it implemented.

BufferBuilder is internal anyway, right?


In reply to: 233672374 [](ancestors = 233672374)

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 would much rather rename BufferBuilder to something ridiculous like 'value accumulator', and have the good name for the more common (and public) use case.


In reply to: 233675246 [](ancestors = 233675246,233672374)

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

BufferBuilder is internal anyway, right?

It isn't right now.

publicsealedclassBufferBuilder<T>

I don't really view this thing as a "builder" anyway. For one there is no "Build" method on it. VBufferEditor would be more appropriate IMO. Do you like that?

  • VBufferMutation
  • VBufferMutationContext
  • VBufferEditor

Any others to consider? #Resolved

private int FindDistinctCounts(in VBuffer<Double> values, double[] valueBuffer, double[] distinctValues, int[] counts)
{
if (values.Count == 0)
var valueValues = values.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

valueValues [](start = 16, length = 11)

valueValues goes against feng shui.
Maybe explicitValues or something? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Changed to explicitValues and explicitValuesCount


In reply to: 233672186 [](ancestors = 233672186)

// Get histogram of values
Array.Sort(valArray, 0, values.Count);
Contracts.Assert(valueBuffer.Length >= valuesCount);
valueValues.CopyTo(valueBuffer);

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.

valueValues.CopyTo(valueBuffer); [](start = 12, length = 32)

so, we haven't done this before. And FastTree.cs line 1463 actually assumes that bin finder is destructive. Maybe we should reconcile one way or another?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I'm not following exactly what you want changed here.

I changed this for 2 reasons:

  1. You can no longer get an array from a VBuffer. So you can't call Array.Sort on it.
  2. Breaking the passed in VBuffer is pretty bad IMO. So I came up with a different way that doesn't destroy its usefulness. Upstream, we can get rid of the other copy, once this REVIEW comment is taken care of:

// REVIEW: Change this, as well as the bin finding code and bin upper bounds, to be Float instead of Double.

Once those are floats, then you no longer have to copy upstream. You can just pass in the values, and this method won't muck with the VBuffer.

ch.Check(FloatUtils.IsFinite(beta[i]), "Non-finite values detected in OLS solution");

var weights = VBufferUtils.CreateDense<float>(beta.Length - 1);
var weightsMutation = VBufferMutationContext.CreateFromBuffer(ref weights);

@Zruty0Zruty0Nov 15, 2018

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.

weights [](start = 78, length = 7)

why create a VBuffer and immediately mutate? #Resolved

@eerhardteerhardtNov 15, 2018

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

You mean, why not just create an array, mutate that, and then create the buffer from the array? I can change that. #Resolved

var vf = values as float[];
var vb = values as byte[];
Contracts.Assert(vf != null || vb != null);
Span<float> vf = typeof(TValue) == typeof(float) ? MemoryMarshal.Cast<TValue, float>(mutation.Values) : default;

@Zruty0Zruty0Nov 15, 2018

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.

vf [](start = 36, length = 2)

I feel that a more common pattern is to have 2 getters: one for float case and one for byte case.
Maybe add a // REVIEW: split the getter into 2 specialized getters, one for float case and one for byte case. ? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Done.


In reply to: 233673911 [](ancestors = 233673911)

{
dst = new VBuffer<TDst>(0, dst.Values, dst.Indices);
dst = VBufferMutationContext.Create(ref dst, 0)
.CreateBuffer();

@Zruty0Zruty0Nov 15, 2018

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.

Create followed immediately by CreateBuffer seems to be a common pattern too. Maybe add VBufferMutationContext.Resize for that? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

That method seems like it would belong in VBufferUtils. I will add it there.


In reply to: 233679182 [](ancestors = 233679182)

<TargetFramework>netstandard2.0</TargetFramework>
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
<DefineConstants>CORECLR</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

@Zruty0Zruty0Nov 15, 2018

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.

AllowUnsafeBlocks [](start = 5, length = 17)

just for one GEMV call... that's annoying. #Resolved

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.

actually nevermind, it's going into HalLearners anyway


In reply to: 233679433 [](ancestors = 233679433)

writer.WriteLine("# Number of Ngrams terms = {0}", ngramsNames.Count);
for (int j = 0; j < ngramsNames.Count; j++)
writer.WriteLine("{0}\t{1}", j, ngramsNames.Values[j]);
var ngramNameValues = ngramsNames.GetValues();

@Zruty0Zruty0Nov 15, 2018

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.

ngramNameValues [](start = 32, length = 15)

explicitNgramNames? #Resolved

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

fixed.


In reply to: 233680569 [](ancestors = 233680569)

if (terms.Count > 0)
var mutation = VBufferMutationContext.Create(ref dst, terms.Count);
for (int i = 0; i < terms.Count; i++)
{

@Zruty0Zruty0Nov 15, 2018

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.

{ [](start = 24, length = 1)

remove #Resolved

@Zruty0Zruty0 left a comment

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.

:shipit:

@eerhardt
eerhardt merged commit cb9effc into dotnet:masterNov 16, 2018
@eerhardteerhardt changed the title Introduce VBufferMutationContext and hide VBuffer.CountIntroduce VBufferEditor and hide VBuffer.CountNov 16, 2018
@eerhardt
eerhardt deleted the MasterVBufferPlan branch November 16, 2018 02:43
@ghostghost locked as resolved and limited conversation to collaborators Mar 26, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@eerhardt@TomFinley@Zruty0