Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}
, '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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}
, '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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}
, '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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}
, '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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}
, '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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}
, '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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}
, '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
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/Microsoft.ML.Parquet/ParquetLoader.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -390,9 +390,21 @@ public Cursor(ParquetLoader parent, Func<int, bool> predicate, IRandom rand)
Columns = _loader._columnsLoaded.Select(i => i.Name).ToArray()
};

int numBlocks = (int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count));
int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);
_blockEnumerator = blockOrder.GetEnumerator();
try
{
int numBlocks = checked((int)Math.Ceiling(((decimal)parent.GetRowCount() / _readerOptions.Count)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int [](start = 20, length = 3)

why not just allocate numBlocks as long or decimal? Then just see if it is >some reasonable value such as int.Maxvalue/1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Elsewhere, our checks check whether something would result in Utils.ArrayMaxLength. Of course those checks also assume that the user is running with gcAllowVeryLargeObjects in the app config, which may not be the case in API usage... :P


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Similar to another comment, we wanted the {block size} and {number of blocks} to both be under 300M to fit in an int array so that it can be shuffled. I changed the default block size to be 1M, but the user could change that value, and if either exceeds the upper limit the exception will be thrown :P

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

_readerOptions [](start = 95, length = 14)

Why not default to an AUTO block size? Can we figure out the right block size ourselves, so that it is not too big to fit in memory and not too small too create too many blocks? Also, if there are two many blocks, can we allocate only a few at a time? In the function below, please just instead create an random Ienumerable of blockorder without having to materialize the actual array.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

The main reason to put the total number of blocks into an array is to be able to shuffle the blocks (in addition to shuffling the rows in a block.) In order to achieve this, we not only need both the {blocks} and {rows in blocks} to be < int.MaxValue, but also < the number of elements that can be allocated in an int array. In my own experiments this number turns out to be ~300M.

I've set the default block size to be 1M rows. This will cover parquet files that contain 300 trillion rows, including reading and shuffling effectively without throwing the exception, and the user can adjust the blocks size if that's necessary.

int[] blockOrder = _rand == null ? Utils.GetIdentityPermutation(numBlocks) : Utils.GetRandomPermutation(rand, numBlocks);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Utils.GetIdentityPermutation(numBlocks) [](start = 55, length = 39)

Make blockOrder an IEnumerable<int>, and replace this specifiy "IdentityPermutation" with Enumerable.Range(0, numBlocks). This avoids materializing the trivial case. I don't see a way to avoid materializing the less trivial case, I'm sorry to say. :(

_blockEnumerator = blockOrder.GetEnumerator();
}
catch (Exception e)
{
if (e is OutOfMemoryException || e is OverflowException)

@glebukglebukMay 7, 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.

OutOfMemoryException [](start = 29, length = 20)

I would treat them as different exceptions. Don't they have exactly oppoisite causes?

@TomFinleyTomFinleyMay 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.

Maybe I'd say one is a refinement of the other. The size can be so large that either (1) it can't fit in an int or (2) it does fit in an int but that's moot since we can't allocate an int[] array that large in .NET anyway. So I'd like the error message to be the same, that is, I'm not sure they're opposites.

That said I still agree with your suggestion of separating the two out... we know exactly which line would throw which exception, and I'd prefer the slightly move verbose two try statements because we have that expectation, since it clarifies to the reader where we expect the two distinct failure cases of the code above to happen, possibly.

Also FYI, just a quick style note for the future: even if we let this code stand as is, we could convert this to catch (Exception e) when (e is OutOfMemoryException || e is OverflowException). This simplifies things enormously. (This very useful feature was added in, I think, C# 6 but I wouldn't stake my life on it.)


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

{
throw new InvalidDataException("Error due to too many blocks. Try increasing block size.", e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

throw [](start = 24, length = 5)

use _host.Except when throwing exceptions within our componints

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To reiterate this: this sort of exception marking is how we distinguish exceptions thrown by .NET or underlying exceptions itself, or exceptions thrown by this library. Marked exceptions are intended to be, in some sense, actionable by a user, so we treat them differently from "unmarked" exceptions, so we consider it important to distinguish them. (In some contexts they're treated differently -- it's useful to know when a problem is due to misuse vs. us just having a bug) Plus of course you get marked with the throwing component; this is useful information.


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

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Duly noted. This should definitely be a marked exception.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

, e [](start = 113, length = 3)

Given that we're interpreting the exception pretty exactly, I'm not certain that there is much utility in including that inner exception. My reason is: usually in this API and many APIs I've seen, innermost exceptions are usually the "real" problem and practically the only thing people wind up looking at. :) So it would be nice if your nice friendly error message you have written was that innermost exception, and I think you have a very good excuse to make it the innermost exception. :)

}

throw;
}

_dataSetEnumerator = new int[0].GetEnumerator(); // Initialize an empty enumerator to get started
_columnValues = new IList[_actives.Length];
Expand DownExpand Up@@ -477,7 +489,7 @@ protected override bool MoveNextCore()
}
else if (_blockEnumerator.MoveNext())
{
_readerOptions.Offset = (int)_blockEnumerator.Current * _readerOptions.Count;
_readerOptions.Offset = (long)_blockEnumerator.Current * _readerOptions.Count;

// When current dataset runs out, read the next portion of the parquet file.
DataSet ds;
Expand All@@ -486,9 +498,21 @@ protected override bool MoveNextCore()
ds = ParquetReader.Read(_loader._parquetStream, _loader._parquetOptions, _readerOptions);
}

int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
try
{
int[] dataSetOrder = _rand == null ? Utils.GetIdentityPermutation(ds.RowCount) : Utils.GetRandomPermutation(_rand, ds.RowCount);
_dataSetEnumerator = dataSetOrder.GetEnumerator();
_curDataSetRow = dataSetOrder[0];
}
catch (Exception e)

@danmoseleydanmoseleyMay 4, 2018

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can write this more simply as

catch (Exception e) when (e Is OutOfMemoryException)
{
throw new ... }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

why not just
catch (InvalidDataException e)?

{
if (e is OutOfMemoryException)
{
throw new InvalidDataException("Error caused because block size too big. Try decreasing block size.", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit "is too big"

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

fixed, thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

InvalidDataException [](start = 38, length = 20)

Just an FYI: InvalidDataException's 'documentation says "The exception that is thrown when a data stream is in an invalid format." This does not describe the scenario here: your data is not in an invalid format, rather, something is wrong with our implementation to interpret that format.

Copy 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 as @glebuk says, might be best to use _host.Except here... or _host.ExceptParam if you can isolate it to a specific user-provided parameter that was somehow "wrong."

}

throw;
}

// Cache list for each active column
for (int i = 0; i < _actives.Length; i++)
Expand DownExpand Up@@ -671,4 +695,4 @@ private string ConvertListToString(IList list)
}
}
}
}
}