区别linq和plinq

ful*_*ack 8 linq plinq

这两者有什么区别?

比较最好的方法是什么?

plinq总是更好吗?

当我们使用plinq?

Jon*_*nna 10

Linq是一组技术,它们协同工作以解决类似的问题 - 在所有这些技术中,您都拥有数据源(xml文件或文件,数据库内容,内存中的对象集合),并且您想要检索部分或全部这些数据以某种方式对其采取行动.Linq致力于解决这一系列问题的共性:

var brithdays = from user in users where
  user.dob.Date == DateTime.Today && user.ReceiveMails
  select new{user.Firstname, user.Lastname, user.Email};
foreach(bdUser in birthdays)
  SendBirthdayMail(bdUser.Firstname, bdUser.Lastname, bdUser.Email);
Run Code Online (Sandbox Code Playgroud)

和等效(使用传统的C#语法明确使用Linq相关的类和方法):

var birthdays = users
  .Where(user => user.dob.Date == DateTime.Today)
  .Select(user => new{user.Firstname, user.Lastname, user.Email});
foreach(bdUser in birthdays)
  SendBirthdayMail(bdUser.Firstname, bdUser.Lastname, bdUser.Email);
Run Code Online (Sandbox Code Playgroud)

这两个代码的示例都可以工作,无论它是否会转变为数据库调用,解析xml文档或搜索对象数组.

唯一的区别是什么类型的对象users.如果它是一个列表,数组或其他可枚举的集合,它将是linq-to-objects,如果它是一个System.Data.Linq.Tablelinq to sql.前者将导致内存中操作,后者在SQL查询中,然后尽可能晚地反序列化为内存中对象.

如果它是ParallelQuery- 通过调用.AsParallel内存中的可枚举集合生成 - 那么查询将在mem-memroy,并行化(大多数时间)执行,以便由多个线程执行 - 理想情况下保持每个核心忙于推进工作.

显然,这里的想法是更快.当它运作良好时,确实如此.

但是有一些缺点.

首先,即使在最终无法进行并行化的情况下,并行化总是会有一些开销.如果没有对数据进行足够的工作,这种开销将超过任何潜在的收益.

其次,并行处理的好处取决于可用的内核.如果查询不会最终阻塞4核计算机上的资源,理论上你可以获得4倍的加速(4个超线程可能会给你更多甚至更少,但可能不会超过8倍 - 线程加倍CPU的某些部分并没有给出明显的两倍增加.在单核上使用相同的查询,或者具有处理器亲和性意味着只有一个核可用(例如,"web-garden"模式中的web服务器),那么就没有加速.如果资源受阻,仍然可以获得收益,但好处取决于机器.

第三,如果有任何共享资源(可能正在输出集合结果)以非线程安全的方式使用,那么错误的结果,崩溃等可能会出现严重错误.

第四,如果以线程安全的方式使用共享资源,并且线程安全来自锁定,则可能存在足够的争用成为瓶颈,从而解除并行化带来的所有好处.

第五,如果你有一台四核机器在四个不同的线程上运行或多或少相同的算法(可能是由于四个客户端在客户端 - 服务器情况下,或者在一个类似的任务中的一组类似任务的桌面情况过程),然后他们总是充分利用这些核心.将算法中的工作分开以便在所有四个核心中进行处理意味着您已经从四个线程(每个线程使用一个核心)移动到四个核心的16个线程.充其量只会是相同的,并且可能的开销会使情况稍微恶化.

可以仍然是在很多情况下的重大胜利,但上面应明确表示,它不会永远.


Jam*_*rth 5

我还想知道何时使用 PLINQ 而不是 LINQ,所以我进行了一些测试。

简介: 在决定是使用 LINQ 还是 PLINQ 运行查询时,有两个问题需要回答。

  1. 运行查询涉及多少次迭代(集合中有多少对象)?

  2. 一次迭代涉及多少工作?

除非 PLINQ 的性能更高,否则请使用 LINQ。如果查询集合涉及太多迭代和/或每次迭代涉及太多工作,PLINQ 的性能可能比 LINQ 更高。

但随之而来的是两个棘手的问题:

  1. 多少次迭代是太多迭代?
  2. 多少工作是太多工作?

我的建议是测试您的查询。使用 LINQ 测试一次,使用 PLINQ 测试一次,然后比较两个结果。

测试 1:通过增加集合中的对象数来增加查询中的迭代次数。

初始化 PLINQ 的开销大约需要 20 毫秒。如果不利用 PLINQ 的优势,这就是浪费时间,因为 LINQ 有 0 毫秒的开销。

每次迭代中涉及的工作对于每次测试总是相同的。工作保持最少。

工作定义:将 int(集合中的对象)乘以 10。

当迭代 100 万个对象时,每次迭代涉及的工作最少,PLINQ 比 LINQ 快。尽管在专业环境中,我从未查询甚至初始化内存中 1000 万个对象的集合,因此这可能是 PLINQ 恰好优于 LINQ 的不太可能的场景。

??????????????????????????????????????
? # Objects ? LINQ (ms) ? PLINQ (ms) ?
??????????????????????????????????????
? 1         ?         1 ?         20 ?
? 10        ?         0 ?         18 ?
? 100       ?         0 ?         20 ?
? 1k        ?         0 ?         23 ?
? 10k       ?         1 ?         17 ?
? 100k      ?         4 ?         37 ?
? 1m        ?        36 ?         76 ?
? 10m       ?       392 ?        285 ?
? 100m      ?      3834 ?       2596 ?
??????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

测试 2:增加迭代中涉及的工作

我将集合中的对象数设置为始终为 10,因此查询涉及的迭代次数较少。对于每次测试,我增加了处理每次迭代所涉及的工作。

工作定义:将 int(集合中的对象)乘以 10。

增加工作的定义:增加迭代次数以将 int 乘以 10。

PLINQ 查询集合的速度更快,因为当工作迭代中的迭代次数增加到 1000 万时,工作量显着增加,我得出的结论是,当单个迭代涉及如此多的工作量时,PLINQ 优于 LINQ。

此表中的“#Iterations”表示工作迭代中的迭代次数。请参阅下面的测试 2 代码。

?????????????????????????????????????????
? # Iterations ? LINQ (ms) ? PLINQ (ms) ?
?????????????????????????????????????????
? 1            ?         1 ?         22 ?
? 10           ?         1 ?         32 ?
? 100          ?         0 ?         25 ?
? 1k           ?         1 ?         18 ?
? 10k          ?         0 ?         21 ?
? 100k         ?         3 ?         30 ?
? 1m           ?        27 ?         52 ?
? 10m          ?       263 ?        107 ?
? 100m         ?      2624 ?        728 ?
? 1b           ?     26300 ?       6774 ?
?????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

测试1代码:

class Program
{
    private static IEnumerable<int> _numbers;

    static void Main(string[] args)
    {
        const int numberOfObjectsInCollection = 1000000000;

        _numbers = Enumerable.Range(0, numberOfObjectsInCollection);

        var watch = new Stopwatch();

        watch.Start();

        var parallelTask = Task.Run(() => ParallelTask());

        parallelTask.Wait();

        watch.Stop();

        Console.WriteLine($"Parallel: {watch.ElapsedMilliseconds}ms");

        watch.Reset();

        watch.Start();

        var sequentialTask = Task.Run(() => SequentialTask());

        sequentialTask.Wait();

        watch.Stop();

        Console.WriteLine($"Sequential: {watch.ElapsedMilliseconds}ms");

        Console.ReadKey();
    }

    private static void ParallelTask()
    {
        _numbers
            .AsParallel()
            .Select(x => DoWork(x))
            .ToArray();
    }

    private static void SequentialTask()
    {
        _numbers
            .Select(x => DoWork(x))
            .ToArray();
    }

    private static int DoWork(int @int)
    {
        return @int * 10;
    }
}
Run Code Online (Sandbox Code Playgroud)

测试2代码:

class Program
{
    private static IEnumerable<int> _numbers;

    static void Main(string[] args)
    {
        _numbers = Enumerable.Range(0, 10);

        var watch = new Stopwatch();

        watch.Start();

        var parallelTask = Task.Run(() => ParallelTask());

        parallelTask.Wait();

        watch.Stop();

        Console.WriteLine($"Parallel: {watch.ElapsedMilliseconds}ms");

        watch.Reset();

        watch.Start();

        var sequentialTask = Task.Run(() => SequentialTask());

        sequentialTask.Wait();

        watch.Stop();

        Console.WriteLine($"Sequential: {watch.ElapsedMilliseconds}ms");

        Console.ReadKey();
    }

    private static void ParallelTask()
    {
        _numbers
            .AsParallel()
            .Select(x => DoWork(x))
            .ToArray();
    }

    private static void SequentialTask()
    {
        _numbers
            .Select(x => DoWork(x))
            .ToArray();
    }

    private static int DoWork(int @int)
    {
        const int numberOfIterations = 1000000000;

        for (int i = 0; i < numberOfIterations; i++)
        {
            @int = @int * 10;
        }

        return @int;
    }
}
Run Code Online (Sandbox Code Playgroud)


Wou*_*ort 2

PLinq 是 Linq 的并行版本。有些查询可以在多个线程上执行,然后 PLinq 可以提高性能。

\n\n

然而,其他查询不能并行执行,否则会给出错误的结果。因此,您应该为每个查询决定何时使用 PLinq,并确保性能真正提高。

\n\n

MSDN有很多关于它的文档。

\n