Reduce allocations in TableWriter (#6648)

* [Feature] Reduce allocations in TableWriter
* Revert iterator
ReadOnlySpan is not supported in iterators
* Use columnsThresHold with stackalloc
This commit is contained in:
Ilya
2018-04-18 19:15:36 +05:00
committed by GitHub
parent 80951777cf
commit c05f1ec3f3
4 changed files with 38 additions and 26 deletions
@@ -744,8 +744,8 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
}
/// <summary>
/// In cases like implicit remoting, there is no console so reading the console width results in an exception.
/// Instead of handling exception every time we cache this value to increase performance.
/// In cases like implicit remoting, there is no console so reading the console width results in an exception.
/// Instead of handling exception every time we cache this value to increase performance.
/// </summary>
static private bool _noConsole = false;
@@ -940,6 +940,8 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
{
TableFormattingHint tableHint = this.InnerCommand.RetrieveFormattingHint() as TableFormattingHint;
int[] columnWidthsHint = null;
// We expect that console width is less then 120.
const int columnsThresHold = 120;
if (tableHint != null)
{
@@ -955,10 +957,10 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
}
// create arrays for widths and alignment
int[] columnWidths = new int[columns];
int[] alignment = new int[columns];
int k = 0;
Span<int> columnWidths = columns < columnsThresHold ? stackalloc int[columns] : new int[columns];
Span<int> alignment = columns < columnsThresHold ? stackalloc int[columns] : new int[columns];
int k = 0;
foreach (TableColumnInfo tci in this.CurrentTableHeaderInfo.tableColumnInfoList)
{
columnWidths[k] = (columnWidthsHint != null) ? columnWidthsHint[k] : tci.width;
@@ -1006,7 +1008,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
// need to make sure we have matching counts: the header count will have to prevail
string[] values = new string[headerColumns];
int[] alignment = new int[headerColumns];
Span<int> alignment = stackalloc int[headerColumns];
int fieldCount = tre.formatPropertyFieldList.Count;
@@ -1168,8 +1170,8 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
_buffer = new StringValuesBuffer(itemsPerRow);
// initialize the writer
int[] columnWidths = new int[itemsPerRow];
int[] alignment = new int[itemsPerRow];
Span<int> columnWidths = stackalloc int[itemsPerRow];
Span<int> alignment = stackalloc int[itemsPerRow];
for (int k = 0; k < itemsPerRow; k++)
{
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace Microsoft.PowerShell.Commands.Internal.Format
{
/// <summary>
@@ -29,7 +31,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// with column elimination, starting from the right most column
/// </summary>
/// <param name="columnWidths">array of column widths to appropriately size</param>
internal void CalculateColumnWidths(int[] columnWidths)
internal void CalculateColumnWidths(Span<int> columnWidths)
{
if (AssignColumnWidths(columnWidths))
{
@@ -47,7 +49,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// </summary>
/// <param name="columnWidths">columns to process</param>
/// <returns>true if there was a fit, false if there is need for trimming</returns>
private bool AssignColumnWidths(int[] columnWidths)
private bool AssignColumnWidths(Span<int> columnWidths)
{
// run a quick check to see if all the columns have a specified width,
// if so, we are done
@@ -127,7 +129,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// trim columns if the total column width is too much for the screen.
/// </summary>
/// <param name="columnWidths">column widths to trim</param>
private void TrimToFit(int[] columnWidths)
private void TrimToFit(Span<int> columnWidths)
{
while (true)
{
@@ -166,7 +168,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// </summary>
/// <param name="columnWidths">column widths array</param>
/// <returns></returns>
private int CurrentTableWidth(int[] columnWidths)
private int CurrentTableWidth(Span<int> columnWidths)
{
int sum = 0;
int visibleColumns = 0;
@@ -188,7 +190,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// </summary>
/// <param name="columnWidths">column widths array</param>
/// <returns>index of the last visible column, -1 if none</returns>
private static int GetLastVisibleColumn(int[] columnWidths)
private static int GetLastVisibleColumn(Span<int> columnWidths)
{
for (int k = 0; k < columnWidths.Length; k++)
{
@@ -700,12 +700,16 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
internal static string TruncateAtNewLine(string s)
{
if (string.IsNullOrEmpty(s))
return s;
{
return "";
}
int lineBreak = s.IndexOfAny(s_lineBreakChars);
if (lineBreak < 0)
{
return s;
}
return s.Substring(0, lineBreak) + PSObjectHelper.ellipses;
}
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Management.Automation.Internal;
using System.Text;
@@ -75,7 +77,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
/// <param name="columnWidths">array of specified column widths</param>
/// <param name="alignment">array of alignment flags</param>
/// <param name="suppressHeader">if true, suppress header printing</param>
internal void Initialize(int leftMarginIndent, int screenColumns, int[] columnWidths, int[] alignment, bool suppressHeader)
internal void Initialize(int leftMarginIndent, int screenColumns, Span<int> columnWidths, ReadOnlySpan<int> alignment, bool suppressHeader)
{
//Console.WriteLine(" 1 2 3 4 5 6 7");
//Console.WriteLine("01234567890123456789012345678901234567890123456789012345678901234567890123456789");
@@ -161,7 +163,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
// generate an array of "--" as header markers below
// the column header labels
string[] breakLine = new string[values.Length];
for (int k = 0; k < _si.columnInfo.Length; k++)
for (int k = 0; k < breakLine.Length; k++)
{
// the column can be hidden
if (_si.columnInfo[k].width <= 0)
@@ -185,25 +187,25 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
GenerateRow(breakLine, lo, false, null, lo.DisplayCells);
}
internal void GenerateRow(string[] values, LineOutput lo, bool multiLine, int[] alignment, DisplayCells dc)
internal void GenerateRow(string[] values, LineOutput lo, bool multiLine, ReadOnlySpan<int> alignment, DisplayCells dc)
{
if (_disabled)
return;
// build the current row alignment settings
int cols = _si.columnInfo.Length;
int[] currentAlignment = new int[cols];
Span<int> currentAlignment = stackalloc int[cols];
if (alignment == null)
{
for (int i = 0; i < cols; i++)
for (int i = 0; i < currentAlignment.Length; i++)
{
currentAlignment[i] = _si.columnInfo[i].alignment;
}
}
else
{
for (int i = 0; i < cols; i++)
for (int i = 0; i < currentAlignment.Length; i++)
{
if (alignment[i] == TextAlignment.Undefined)
currentAlignment[i] = _si.columnInfo[i].alignment;
@@ -227,10 +229,10 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
}
}
private string[] GenerateTableRow(string[] values, int[] alignment, DisplayCells ds)
private string[] GenerateTableRow(string[] values, ReadOnlySpan<int> alignment, DisplayCells ds)
{
// select the active columns (skip hidden ones)
int[] validColumnArray = new int[_si.columnInfo.Length];
Span<int> validColumnArray = stackalloc int[_si.columnInfo.Length];
int validColumnCount = 0;
for (int k = 0; k < _si.columnInfo.Length; k++)
{
@@ -241,7 +243,9 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
}
if (validColumnCount == 0)
{
return null;
}
StringCollection[] scArray = new StringCollection[validColumnCount];
bool addPadding = true;
@@ -353,7 +357,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
// finally, build an array of strings
string[] rows = new string[screenRows];
for (int row = 0; row < rows.Length; row++)
for (int row = 0; row < screenRows; row++)
{
StringBuilder sb = new StringBuilder();
// for a given row, walk the columns
@@ -371,6 +375,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
}
rows[row] = sb.ToString();
}
return rows;
}
@@ -390,7 +395,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
return sc;
}
private string GenerateRow(string[] values, int[] alignment, DisplayCells dc)
private string GenerateRow(string[] values, ReadOnlySpan<int> alignment, DisplayCells dc)
{
StringBuilder sb = new StringBuilder();
@@ -408,7 +413,6 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
// skip columns that are not at least a single character wide
continue;
}
int newRowIndex = sb.Length;
// NOTE: the following padding operations assume that we
// pad with a blank (or any character that ALWAYS maps to a single screen cell
@@ -432,7 +436,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
private static string GenerateRowField(string val, int width, int alignment, DisplayCells dc, bool addPadding)
{
// make sure the string does not have any embedded <CR> in it
string s = StringManipulationHelper.TruncateAtNewLine(val) ?? "";
string s = StringManipulationHelper.TruncateAtNewLine(val);
string currentValue = s;
int currentValueDisplayLength = dc.Length(currentValue);