Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina
, '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

Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina
, '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

Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina
, '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

Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina
, '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

Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina
, '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

Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina
, '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

Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina
, '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

Replace DV data type system with .NET standard type system. - #863

Merged
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem
Sep 19, 2018
Merged

Replace DV data type system with .NET standard type system.#863
codemzs merged 44 commits into
dotnet:masterfrom
codemzs:typesystem

Conversation

@codemzs

Copy link
Copy Markdown
Member

This change replaces DvType system with .NET standard data type system and fixes#673

Old TypeNew Type
DvInt1sbyte
DvInt2short
DvInt4int
DvInt8long
DvBoolbool
DvDateTimeDateTime
DvDateTimeZoneDateTimeOffset
DvTimeSpanTimeSpan
DvTextReadOnlyMemory<char>

…nto typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
…into typesystem
# Conflicts:
#	src/Microsoft.ML.Api/ApiUtils.cs
#	src/Microsoft.ML.Api/DataViewConstructionUtils.cs
#	src/Microsoft.ML.Api/TypedCursor.cs
#	src/Microsoft.ML.Core/Data/DataKind.cs
#	src/Microsoft.ML.Core/Data/DvInt1.cs
#	src/Microsoft.ML.Core/Data/DvInt2.cs
#	src/Microsoft.ML.Core/Data/DvInt4.cs
#	src/Microsoft.ML.Core/Data/DvInt8.cs
#	src/Microsoft.ML.Core/Data/DvText.cs
#	src/Microsoft.ML.Core/Data/TypeUtils.cs
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/Evaluators/BinaryClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/ClusteringEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MultiOutputRegressionEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/MulticlassClassifierEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RankerEvaluator.cs
#	src/Microsoft.ML.Data/Evaluators/RegressionEvaluatorBase.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt
#	test/Microsoft.ML.Core.Tests/UnitTests/DvTypes.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestCSharpApi.cs
#	test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
#	test/Microsoft.ML.StaticPipelineTesting/StaticPipeTests.cs
#	test/Microsoft.ML.TestFramework/TestSparseDataView.cs
#	test/Microsoft.ML.Tests/CollectionDataSourceTests.cs
 into typesystem
# Conflicts:
#	src/Microsoft.ML.Data/Data/Conversion.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/CodecFactory.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/Codecs.cs
#	src/Microsoft.ML.Data/DataLoadSave/Binary/UnsafeTypeOps.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
#	test/BaselineOutput/SingleDebug/Command/Datatypes-datatypes.txt
#	test/BaselineOutput/SingleRelease/Command/Datatypes-datatypes.txt

/// <summary>
/// Whether this type is the standard timespan type.
/// Whether this type is the standard <see cref="TimeSpan"/> type.

@TomFinleyTomFinleySep 8, 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.

TimeSpan [](start = 57, length = 8)

TimeSpanType, not TimeSpan right? Certainly any instance of a ColumnType could not be a System.TimeSpan. #Resolved

Contracts.Assert(this == TimeSpanType.Instance);
return true;
Contracts.Assert((this == TimeSpanType.Instance) == (this is TimeSpanType));
return this is TimeSpanType;

@TomFinleyTomFinleySep 8, 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 is a very nice simplification of some, frankly, previously ridiculous code. Thank you for adding it. #Resolved

</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.1" />

@TomFinleyTomFinleySep 8, 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.

Since we're referencing this package in many places, the version should be a variable SystemMemoryVersion in build/Dependencies.props. #Resolved


/// <summary>
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a DvText.
/// Metadata kind that indicates the prediction kind as a string. E.g. "BinaryClassification". The value is typically a ReadOnlyMemory.

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 132, length = 14)

Not just any, but specifically ReadOnlyMemory<char>. #Resolved

/// * is a vector of length N (including 0)
/// * has a SlotNames metadata
/// * metadata type is VBuffer&lt;DvText&gt; of length N
/// * metadata type is VBuffer&lt;ReadOnlyMemory&gt; of length N

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 43, length = 14)

If you wanted to read it out it would be not ReadOnlyMemory but ReadOnlyMemory&ltchar&gt, but this is why we have <see tags in the XML docs. #Resolved

{

/// <summary>
/// This implements IEquatable's Equals method.

@TomFinleyTomFinleySep 8, 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 implements IEquatable's Equals method. [](start = 12, length = 43)

It couldn't possibly be implementing an interface method since this is a static method on a static class, and even if it wasn't you couldn't make ReadOnlyMemory<char> implement any interface anyway, since it is not a type under your control.

I think you meant to say it was a utility function out of which one could make an IEquatityComparer<ReadOnlyMemory<char>>, but it's difficult to be certain. #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.

Also please try to use <see tags.


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

/// <summary>
/// Compare equality with the given system string value.
/// </summary>
public static bool EqualsStr(string s, ReadOnlyMemory<char> memory)

@TomFinleyTomFinleySep 8, 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.

EqualsStr [](start = 27, length = 9)

Is this different method necessary due to perf reasons of s.AsMemory? #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.

You can say that.


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

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.

Would you say that?


In reply to: 216189037 [](ancestors = 216189037,216126482)


for (int i = 0; i < memory.Length; i++)
{
if (memory.Span[i] != b.Span[i])

@TomFinleyTomFinleySep 8, 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.

Span [](start = 27, length = 4)

Please store the spans as a variable upfront, then operate on them. These calls to .Span property are not free. Here and everywhere.

Consider this code.

classProgram{privatestaticvoidTimeTest(stringstr){longtemp=0;varsw=newStopwatch();constinttrials=1_000_000_000;sw.Restart();for(inti=0;i<trials;++i)temp+=str[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on string");temp=0;sw.Restart();varmem=str.AsMemory();for(inti=0;i<trials;++i)temp+=mem.Span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on memory.Span");temp=0;sw.Restart();varspan=mem.Span;for(inti=0;i<trials;++i)temp+=span[0];sw.Stop();Console.WriteLine($"{sw.Elapsed} on a stored span");}privatestaticvoidMain(string[]args){TimeTest("hello");}}

When compiled and run in .NET Core 2.1, in a release build, the timings are this:

00:00:00.4667924 on string
00:00:01.8801733 on memory.Span
00:00:00.2327934 on a stored span
``` #Resolved

yield break;
}

int ichMin = 0;

@TomFinleyTomFinleySep 8, 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.

ichMin [](start = 16, length = 6)

As far as I see both ichMin and ichLim could, here and most methods in this file, be replaced with direct usage of the 0 literal and memory.Length (or, more precisely, span.Length since we should be storing the span upfront, see above) literal. This would both simplify the code and make it more clear.

Look at it this way. If we had code over an array a that looked like this:

for(inti=0;i<a.Length;++i)DoSomething(a[i]);

we certainly would not welcome any movement to change the code to this.

intmin=0;intlim=a.Length;for(inti=min;i<lim;++i)DoSomething(a[i]);

That's less clear and needlessly verbose. #Resolved

@codemzscodemzsSep 10, 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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times. There are places where ichMin is incremented and even there we should keep it.


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

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.

ichLim might be worth keeping since we don't want to call memory.Length multiple times.

Why? Are you under the impression that calling this on a ReadOnlySpan<char> is a perf hit? Have you checked?

Just reactivating, since it reads like you aren't going to do this.


In reply to: 216189476 [](ancestors = 216189476,216126883)

@codemzscodemzsSep 10, 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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.


In reply to: 216192013 [](ancestors = 216192013,216189476,216126883)

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.

No, I did not check but based on your comment for Span(and other places in the codebase I have seen such fields cached locally) I assumed it might also be the case for Length but may be I'm wrong. I will replace ichLim as well.

Why would you? Are you under the impression that an array .Length involves some heavy computation as well?

The point of caching is to avoid computation or memory allocations. In this case .Length is an immutable property of an immutable struct. By caching its value you're storing more on the stack.

The point is that a call to Span involves some actual work whereas Length does not.


In reply to: 216192140 [](ancestors = 216192140,216192013,216189476,216126883)

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.

Span doing additional computation is also concerning to me. We need to take this up with the .NET team and provide this feedback.


In reply to: 216194473 [](ancestors = 216194473,216192140,216192013,216189476,216126883)

}

// Note that we don't use any fields of "this" here in case one
// of the out parameters is the same as "this".

@TomFinleyTomFinleySep 8, 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.

Copy pasta detected. #Resolved

}

/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and

@TomFinleyTomFinleySep 8, 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 instance [](start = 19, length = 13)

This is not an instance method, so saying "this instance" is unclear. A <paramref however would not go amiss. #Resolved


/// <summary>
/// Splits this instance on the left-most occurrence of an element of separators character array and
/// produces the left and right ReadOnlyMemory values. If this instance does not contain any of the

@TomFinleyTomFinleySep 8, 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.

ReadOnlyMemory [](start = 40, length = 14)

Another textual reference to a non-generic ReadOnlyMemory structure, which does not exist. I think these can be replaced more or less well with <see cref="ReadOnlyMemory{char}"/>. #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.

gives an error but apparently it needs to be of


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

// REVIEW: Can this be faster?
private static bool ContainsChar(char ch, char[] rgch)
{
Contracts.CheckNonEmpty(rgch, nameof(rgch));

@TomFinleyTomFinleySep 8, 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.

Contracts.CheckNonEmpty(rgch, nameof(rgch)); [](start = 12, length = 44)

Since there's that review comment (though I suspect it was just copy/pasted over), yes, this can be faster.

First this is a private method that you're calling repeatedly in tight loops in the split code. An assert on the non-emptyness of the input is appropriate for sure, and you ought to check that the code is unreachable in any situation where the separators fed in should not be null, but other than that it seems fine. A check is code run all the time even in a release build, which is not needed here. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor
public class TextLoaderTestPipe : TestDataPipeBase

I have added test for this in DataTypesTest


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


Refers to: test/Microsoft.ML.Tests/TextLoaderTests.cs:19 in 0511d74. [](commit_id = 0511d74, deletion_comment = False)

…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/Text/WordEmbeddingsTransform.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Estimators/Visibility.cs
#	test/Microsoft.ML.Tests/Scenarios/Api/Visibility.cs
…to typesystem
# Conflicts:
#	src/Microsoft.ML.Transforms/NAReplaceTransform.cs
#	src/Microsoft.ML.Transforms/NAReplaceUtils.cs
@eerhardt

Copy link
Copy Markdown
Member
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

That's not really the same thing. When you test for exceptions you should verify the exception type and optionally you can verify its message.

The code could start throwing MyCrazyException("Nullable object must have a value.") and this test would still pass. However, that could break user code that was catching a specific exception type, but is no longer thrown.


In reply to: 422599427 [](ancestors = 422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

@codemzs

Copy link
Copy Markdown
MemberAuthor
 var ex = Assert.ThrowsAny<Exception>(() => TestCore(pathData, false, new[] { "loader=Parquet{bigIntDates=+}" }, forceDense: true));

By that logic the code could also throw another exception of the same type and even with the same message..... :)


In reply to: 422847966 [](ancestors = 422847966,422599427,420677248)


Refers to: test/Microsoft.ML.TestFramework/DataPipe/Parquet.cs:36 in d45bc2c. [](commit_id = d45bc2c, deletion_comment = False)

/// <summary>
/// Returns a <see cref="ReadOnlyMemory{T}"/> of <see cref="char"/> with trailing whitespace trimmed.
/// </summary>
public static ReadOnlyMemory<char> TrimEndWhiteSpace(ReadOnlyMemory<char> memory, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this method is a little awkward and should be removed. It is assuming that the memory and span are pointing to the same location. I see it is only called in 1 spot, which can just be moved to the above overload that only takes a ROM<char>. #Resolved

int ichLim = scan.IchLimBuf;
int ichCur = scan.IchMinNext;

//var span = text.Span;

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nit) can be removed. #Resolved

return pool.Get(memory);
}

public static void AddToStringBuilder(ReadOnlyMemory<char> memory, StringBuilder sb)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can get rid of this method and just have all the callers call sb.Append(memory) themselves. That way we don't have yet another public util method that does the same thing. #Resolved

}

for (; ; ichMin++)
public static Result Parse(out Single value, ReadOnlySpan<char> span)

@eerhardteerhardtSep 19, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While we are in here fixing this, can we switch the parameters around so the out variable is at the end? #Resolved

@eerhardt

eerhardt commented Sep 19, 2018

Copy link
Copy Markdown
Member

I think this is looking really good. Just a couple last comments to address and I think this will be ready to merge. #Resolved

@codemzs

Copy link
Copy Markdown
MemberAuthor

Thanks for reviewing!


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

@eerhardteerhardt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:shipit:

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.

.NET data type system instead of DvTypes

5 participants

@codemzs@TomFinley@eerhardt@ahsonkhan@KrzysztofCwalina