对于List<T>
,我需要实现多列排序,其中列名和排序方向在运行时是已知的。我使用System.Linq.Dynamic OrderBy
API,它可以将列名和排序方向作为连接字符串,因此代码可以工作:
List<T> data = DataCollection; // Stored in Cache
var sortedData = data.OrderBy("Col1 asc, Col2 desc, Col3 asc,Col4 asc");
挑战是当数据大小增加到1 million+
个记录时,相同的排序操作会大大减慢,因为没有魔杖。
现在我想了解在Parallel
模式下是否有相同操作的方法。以下是我正在考虑的选项:
选项1:
选项2
当我正在寻找选项时,遇到了List<int>
的以下并行模式,其中递归Parallel Sort也在内部调用递归序列排序:
public class CustomSort
{
// Fetch Partition
public static int Partition(List<int> list, int left, int right)
{
int start = left;
int pivot = list[start];
left++;
right--;
while (true)
{
while (left <= right && list[left] <= pivot)
left++;
while (left <= right && list[right] > pivot)
right--;
if (left > right)
{
list[start] = list[left - 1];
list[left - 1] = pivot;
return left;
}
int temp = list[left];
list[left] = list[right];
list[right] = temp;
}
}
// Quick Sort serial
public static void QuickSort(List<int> list, int left, int right)
{
if (list == null || list.Count <= 1)
return;
if (left < right)
{
int pivotIdx = Partition(list, left, right);
QuickSort(list, left, pivotIdx - 1);
QuickSort(list, pivotIdx, right);
}
}
// Quick Sort Parallel
public static void QuickSortParallel(List<int> list, int left, int right)
{
if (list == null || list.Count <= 1)
return;
if (left < right)
{
int pivotIdx = Partition(list, left, right);
Task leftTask = Task.Run(() => QuickSort(list, left, pivotIdx - 1));
Task rightTask = Task.Run(() => QuickSort(list, pivotIdx, right));
Task.WaitAll(new[] { leftTask, rightTask });
}
}
}
问题:
multi column sort
版本,因为选择分区将是一件复杂的事情任何指针,可以让我设置正确的路径
答案 0 :(得分:2)
最合乎逻辑的选择是从LINQ切换到Parallel LINQ (PLINQ)。
不幸的是,尽管System.Linq.Dynamic
OrderBy
方法有效,但它实际上会遇到Enumerable
方法重载,因此对ParallelQuery<T>
无效,这需要绑定到相应的ParallelEnumerable
重载。动态LINQ OrderBy
实现也使用内部类,因此无法在外部扩展它(没有源代码)。
仍然存在解决方案。您可以使用以下自定义扩展方法。它的作用是使用动态LINQ构建虚假查询,然后使用相对简单的ExpressionVisitor
替换相应的Queryable
调用相应的ParallelEnumerable
调用:
public static class DynamicPLINQ
{
public static OrderedParallelQuery<T> OrderBy<T>(this ParallelQuery<T> source, string ordering, params object[] values)
{
var query = Enumerable.Empty<T>().AsQueryable();
var orderedQuery = query.OrderBy(ordering, values);
var binder = new ParallelQueryBinder();
binder.source = query;
binder.target = source;
var queryExpr = binder.Visit(orderedQuery.Expression);
return (OrderedParallelQuery<T>)query.Provider.Execute(queryExpr);
}
class ParallelQueryBinder : ExpressionVisitor
{
public object source;
public object target;
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Value == source)
return Expression.Constant(target);
return base.VisitConstant(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType == ExpressionType.Quote)
return Visit(node.Operand);
return base.VisitUnary(node);
}
static readonly string[] Methods = { "OrderBy", "OrderByDescending", "ThenBy", "ThenByDescending" };
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.IsStatic && node.Method.DeclaringType == typeof(Queryable) && Methods.Contains(node.Method.Name))
{
var arguments = node.Arguments.Select(Visit).ToArray();
var result = Expression.Call(typeof(ParallelEnumerable), node.Method.Name, node.Method.GetGenericArguments(), arguments);
return result;
}
return base.VisitMethodCall(node);
}
}
}
现在您可以使用这样的PLINQ服务:
var sortedData = data.AsParallel()
.OrderBy("Col1 asc, Col2 desc, Col3 asc,Col4 asc")
.ToList();
并比较表现。