我可以为列表中的所有条目多线程处理一个函数并返回在 O(n) 中给出最大值的列表条目吗?

问题描述

我有一个 3D X/Y/Z 点列表 foundPoints 和单个 3D X/Y/Z 点 rayOrigin。我希望从列表中返回离 rayOrigin 最远的点。该列表可能包含数千个点,因此我想对它进行多线程处理。

我有一个现有的单线程函数,它可以正常工作并且是 O(n)。我在多线程上所做的第一次尝试是 O(2n) 因为我要浏览两次列表,我完全承认它并不漂亮但我正在努力寻找一种方法来使用多线程但在 O (n)。我怀疑答案将与 PLINQ 有关,但我非常愿意接受建议。

这是我的单线程代码

static List<double> getFarthestPoint(List<List<double>> foundPoints,double[] rayOrigin)
{
    //No need to iterate if we have one point
    if (foundPoints.Count == 1)
    {
        return foundPoints[0];
    }

    //Run through a bunch of output points and compare to eyePoin
    List<double> farthestPoint = foundPoints[0];
    double maxdistance = getPointdistance(rayOrigin,farthestPoint);
    
    double pointdistance;

    foreach (List<double> foundPoint in foundPoints)
    {
        pointdistance = getPointdistance(rayOrigin,foundPoint);

        if (pointdistance > maxdistance)
        {
            maxdistance = pointdistance;
            farthestPoint = foundPoint;
        }
    }

    //Return max distance point
    return farthestPoint;
}

这是我平庸的多线程代码

static List<double> getFarthestPointMultiThread(List<List<double>> foundPoints,double[] rayOrigin)
{
    //No need to iterate if we have one point
    if (foundPoints.Count == 1)
    {
        return foundPoints[0];
    }

    //This is multi-threaded but still O(2n) is there a way to multi-thread an O(n)?
    Dictionary<Double,List<Double>> calculateddistances = new Dictionary<double,List<double>>();

    //Multi thread to create dictionary of all distances
    Parallel.For(0,foundPoints.Count,i =>
        calculateddistances.Add(getPointdistance(rayOrigin,foundPoints[i]),foundPoints[i]));

    //return point of max distance
    return calculateddistances[calculateddistances.Keys.AsParallel().Max()];
}

解决方法

我对 PLINQ 不是很熟悉,但是这个调用链应该允许你分配工作量:

return foundPoints
        .AsParallel()
        .Select(point => (point: point,distance: getPointDistance(rayOrigin,point)))
        .Aggregate((p1,p2) => p1.distance > p2.distance ? p1 : p2)
        .point;

感谢 @PeterDuniho 指出我之前的基准测试设置中的一个主要缺陷。
以下是结果(2000 万点,100 次迭代,在 i7-3820 @ 3.6 GHz 上运行):

      1 core                2 cores               4 cores               8 cores
-------------------   -------------------   -------------------   -------------------
 Average:  1673 ms     Average:  932 ms      Average:   581 ms     Average:   454 ms
-------------------   -------------------   -------------------   -------------------
 Batch 0:  1698 ms     Batch 0:  1004 ms     Batch 0:   595 ms     Batch 0:   401 ms
 Batch 1:  1670 ms     Batch 1:   966 ms     Batch 1:   652 ms     Batch 1:   381 ms
 Batch 2:  1624 ms     Batch 2:   872 ms     Batch 2:   624 ms     Batch 2:   424 ms
 Batch 3:  1639 ms     Batch 3:   887 ms     Batch 3:   688 ms     Batch 3:   446 ms
 Batch 4:  1662 ms     Batch 4:   870 ms     Batch 4:   596 ms     Batch 4:   372 ms
 Batch 5:  1650 ms     Batch 5:   891 ms     Batch 5:   638 ms     Batch 5:   460 ms
 Batch 6:  1679 ms     Batch 6:   861 ms     Batch 6:   594 ms     Batch 6:   450 ms
 Batch 7:  1699 ms     Batch 7:   922 ms     Batch 7:   619 ms     Batch 7:   440 ms
 Batch 8:  1663 ms     Batch 8:   906 ms     Batch 8:   576 ms     Batch 8:   361 ms
 Batch 9:  1662 ms     Batch 9:   848 ms     Batch 9:   599 ms     Batch 9:   460 ms
 Batch 10: 1622 ms     Batch 10:  905 ms     Batch 10:  605 ms     Batch 10:  362 ms
 Batch 11: 1646 ms     Batch 11:  969 ms     Batch 11:  586 ms     Batch 11:  459 ms
 Batch 12: 1675 ms     Batch 12:  940 ms     Batch 12:  573 ms     Batch 12:  405 ms
 Batch 13: 1598 ms     Batch 13:  887 ms     Batch 13:  620 ms     Batch 13:  462 ms
 Batch 14: 1618 ms     Batch 14:  869 ms     Batch 14:  572 ms     Batch 14:  418 ms
 Batch 15: 1597 ms     Batch 15:  851 ms     Batch 15:  567 ms     Batch 15:  515 ms
 Batch 16: 1611 ms     Batch 16:  858 ms     Batch 16:  587 ms     Batch 16:  529 ms
 Batch 17: 1602 ms     Batch 17:  877 ms     Batch 17:  590 ms     Batch 17:  445 ms
 Batch 18: 1644 ms     Batch 18:  869 ms     Batch 18:  607 ms     Batch 18:  359 ms
 Batch 19: 1615 ms     Batch 19: 1021 ms     Batch 19:  648 ms     Batch 19:  490 ms
 Batch 20: 1700 ms     Batch 20:  957 ms     Batch 20:  530 ms     Batch 20:  454 ms
 Batch 21: 1642 ms     Batch 21:  902 ms     Batch 21:  583 ms     Batch 21:  433 ms
 Batch 22: 1680 ms     Batch 22:  857 ms     Batch 22:  567 ms     Batch 22:  513 ms
 Batch 23: 1615 ms     Batch 23:  894 ms     Batch 23:  562 ms     Batch 23:  436 ms
 Batch 24: 1680 ms     Batch 24:  907 ms     Batch 24:  584 ms     Batch 24:  371 ms
 Batch 25: 1611 ms     Batch 25:  851 ms     Batch 25:  569 ms     Batch 25:  519 ms
 Batch 26: 1590 ms     Batch 26:  865 ms     Batch 26:  514 ms     Batch 26:  385 ms
 Batch 27: 1686 ms     Batch 27:  934 ms     Batch 27:  634 ms     Batch 27:  521 ms
 Batch 28: 1729 ms     Batch 28:  931 ms     Batch 28:  582 ms     Batch 28:  415 ms
 Batch 29: 1655 ms     Batch 29:  863 ms     Batch 29:  560 ms     Batch 29:  442 ms
 Batch 30: 1700 ms     Batch 30:  939 ms     Batch 30:  553 ms     Batch 30:  420 ms
 Batch 31: 1651 ms     Batch 31:  892 ms     Batch 31:  565 ms     Batch 31:  401 ms
 Batch 32: 1613 ms     Batch 32:  861 ms     Batch 32:  562 ms     Batch 32:  511 ms
 Batch 33: 1629 ms     Batch 33:  841 ms     Batch 33:  543 ms     Batch 33:  549 ms
 Batch 34: 1652 ms     Batch 34:  918 ms     Batch 34:  545 ms     Batch 34:  551 ms
 Batch 35: 1714 ms     Batch 35:  954 ms     Batch 35:  554 ms     Batch 35:  375 ms
 Batch 36: 1709 ms     Batch 36:  906 ms     Batch 36:  566 ms     Batch 36:  415 ms
 Batch 37: 1665 ms     Batch 37:  898 ms     Batch 37:  588 ms     Batch 37:  366 ms
 Batch 38: 1659 ms     Batch 38:  886 ms     Batch 38:  585 ms     Batch 38:  500 ms
 Batch 39: 1620 ms     Batch 39:  903 ms     Batch 39:  536 ms     Batch 39:  421 ms
 Batch 40: 1627 ms     Batch 40:  851 ms     Batch 40:  572 ms     Batch 40:  542 ms
 Batch 41: 1607 ms     Batch 41:  864 ms     Batch 41:  560 ms     Batch 41:  605 ms
 Batch 42: 1678 ms     Batch 42:  926 ms     Batch 42:  575 ms     Batch 42:  561 ms
 Batch 43: 1679 ms     Batch 43:  890 ms     Batch 43:  560 ms     Batch 43:  612 ms
 Batch 44: 1707 ms     Batch 44:  941 ms     Batch 44:  556 ms     Batch 44:  531 ms
 Batch 45: 1863 ms     Batch 45: 1001 ms     Batch 45:  567 ms     Batch 45:  483 ms
 Batch 46: 1667 ms     Batch 46:  968 ms     Batch 46:  634 ms     Batch 46:  576 ms
 Batch 47: 1640 ms     Batch 47:  968 ms     Batch 47:  567 ms     Batch 47:  398 ms
 Batch 48: 1666 ms     Batch 48:  952 ms     Batch 48:  551 ms     Batch 48:  556 ms
 Batch 49: 1636 ms     Batch 49:  969 ms     Batch 49:  566 ms     Batch 49:  442 ms
 Batch 50: 1675 ms     Batch 50:  984 ms     Batch 50:  558 ms     Batch 50:  421 ms
 Batch 51: 1806 ms     Batch 51:  951 ms     Batch 51:  571 ms     Batch 51:  365 ms
 Batch 52: 1768 ms     Batch 52:  881 ms     Batch 52:  575 ms     Batch 52:  478 ms
 Batch 53: 1828 ms     Batch 53:  859 ms     Batch 53:  550 ms     Batch 53:  460 ms
 Batch 54: 1743 ms     Batch 54:  915 ms     Batch 54:  540 ms     Batch 54:  508 ms
 Batch 55: 1637 ms     Batch 55: 1066 ms     Batch 55:  563 ms     Batch 55:  488 ms
 Batch 56: 1668 ms     Batch 56: 1059 ms     Batch 56:  570 ms     Batch 56:  478 ms
 Batch 57: 1760 ms     Batch 57: 1110 ms     Batch 57:  505 ms     Batch 57:  437 ms
 Batch 58: 1814 ms     Batch 58:  874 ms     Batch 58:  626 ms     Batch 58:  426 ms
 Batch 59: 1855 ms     Batch 59:  893 ms     Batch 59:  555 ms     Batch 59:  440 ms
 Batch 60: 1818 ms     Batch 60:  927 ms     Batch 60:  570 ms     Batch 60:  432 ms
 Batch 61: 1701 ms     Batch 61:  932 ms     Batch 61:  551 ms     Batch 61:  452 ms
 Batch 62: 1691 ms     Batch 62:  875 ms     Batch 62:  537 ms     Batch 62:  389 ms
 Batch 63: 1743 ms     Batch 63:  966 ms     Batch 63:  592 ms     Batch 63:  521 ms
 Batch 64: 1698 ms     Batch 64: 1010 ms     Batch 64:  611 ms     Batch 64:  430 ms
 Batch 65: 1744 ms     Batch 65:  964 ms     Batch 65:  563 ms     Batch 65:  505 ms
 Batch 66: 1687 ms     Batch 66:  918 ms     Batch 66:  553 ms     Batch 66:  382 ms
 Batch 67: 1656 ms     Batch 67: 1021 ms     Batch 67:  567 ms     Batch 67:  457 ms
 Batch 68: 1684 ms     Batch 68:  965 ms     Batch 68:  548 ms     Batch 68:  480 ms
 Batch 69: 1675 ms     Batch 69:  891 ms     Batch 69:  548 ms     Batch 69:  516 ms
 Batch 70: 1700 ms     Batch 70:  893 ms     Batch 70:  560 ms     Batch 70:  429 ms
 Batch 71: 1687 ms     Batch 71:  971 ms     Batch 71:  531 ms     Batch 71:  486 ms
 Batch 72: 1654 ms     Batch 72: 1145 ms     Batch 72:  555 ms     Batch 72:  394 ms
 Batch 73: 1669 ms     Batch 73:  924 ms     Batch 73:  554 ms     Batch 73:  477 ms
 Batch 74: 1637 ms     Batch 74: 1023 ms     Batch 74:  591 ms     Batch 74:  481 ms
 Batch 75: 1653 ms     Batch 75: 1108 ms     Batch 75:  639 ms     Batch 75:  404 ms
 Batch 76: 1618 ms     Batch 76:  978 ms     Batch 76:  627 ms     Batch 76:  464 ms
 Batch 77: 1711 ms     Batch 77:  969 ms     Batch 77:  620 ms     Batch 77:  392 ms
 Batch 78: 1647 ms     Batch 78:  953 ms     Batch 78:  593 ms     Batch 78:  501 ms
 Batch 79: 1724 ms     Batch 79:  898 ms     Batch 79:  574 ms     Batch 79:  385 ms
 Batch 80: 1680 ms     Batch 80:  945 ms     Batch 80:  622 ms     Batch 80:  381 ms
 Batch 81: 1630 ms     Batch 81:  973 ms     Batch 81:  555 ms     Batch 81:  448 ms
 Batch 82: 1618 ms     Batch 82:  906 ms     Batch 82:  828 ms     Batch 82:  441 ms
 Batch 83: 1703 ms     Batch 83:  958 ms     Batch 83:  580 ms     Batch 83:  436 ms
 Batch 84: 1683 ms     Batch 84:  920 ms     Batch 84:  636 ms     Batch 84:  467 ms
 Batch 85: 1618 ms     Batch 85: 1032 ms     Batch 85:  588 ms     Batch 85:  427 ms
 Batch 86: 1695 ms     Batch 86:  947 ms     Batch 86:  547 ms     Batch 86:  410 ms
 Batch 87: 1691 ms     Batch 87: 1008 ms     Batch 87:  561 ms     Batch 87:  393 ms
 Batch 88: 1764 ms     Batch 88:  953 ms     Batch 88:  587 ms     Batch 88:  510 ms
 Batch 89: 1704 ms     Batch 89:  935 ms     Batch 89:  568 ms     Batch 89:  464 ms
 Batch 90: 1631 ms     Batch 90:  962 ms     Batch 90:  570 ms     Batch 90:  435 ms
 Batch 91: 1613 ms     Batch 91:  983 ms     Batch 91:  569 ms     Batch 91:  439 ms
 Batch 92: 1604 ms     Batch 92:  930 ms     Batch 92:  582 ms     Batch 92:  428 ms
 Batch 93: 1592 ms     Batch 93:  926 ms     Batch 93:  507 ms     Batch 93:  518 ms
 Batch 94: 1596 ms     Batch 94:  946 ms     Batch 94:  613 ms     Batch 94:  444 ms
 Batch 95: 1688 ms     Batch 95:  894 ms     Batch 95:  572 ms     Batch 95:  492 ms
 Batch 96: 1613 ms     Batch 96:  939 ms     Batch 96:  586 ms     Batch 96:  490 ms
 Batch 97: 1651 ms     Batch 97:  915 ms     Batch 97:  606 ms     Batch 97:  489 ms
 Batch 98: 1689 ms     Batch 98:  946 ms     Batch 98:  639 ms     Batch 98:  482 ms
 Batch 99: 1680 ms     Batch 99:  953 ms     Batch 99:  614 ms     Batch 99:  400 ms
-------------------   -------------------   -------------------   -------------------

测试代码:

double CalculateDistance(List<double> p1,List<double> p2) {
    double dx = p2[0] - p1[0];
    double dy = p2[1] - p1[1];
    double dz = p2[2] - p1[2];
    return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}

var initTime = Stopwatch.StartNew();
var origin = new List<double> { 20,40,60 };
var points = Enumerable
    .Range(0,20000000)
    .Select(i => new List<double> { i,i,i })
    .ToList();
WriteLine($"Initialization: {initTime.ElapsedMilliseconds} ms");

var times = new List<long>();

for(int i = 0; i < 100; i++) {
    var sw = Stopwatch.StartNew();
    points
        .AsParallel()
        .WithDegreeOfParallelism(8)
        .Select(point => (point: point,distance: CalculateDistance(point,origin)))
        .Aggregate((p1,p2) => p1.distance > p2.distance ? p1 : p2);
    
    long time = sw.ElapsedMilliseconds;
    WriteLine($"Batch {i}: {time} ms");
    times.Add(time);
}

WriteLine($"Average: {times.Average()} ms");