是否可以使用GPU加速(动态)LINQ查询?

时间:2012-02-15 17:43:09

标签: c# linq gpu dynamic-linq

我一直在寻找有关使用GPU加速LINQ查询的可能性的可靠信息。

到目前为止我已“调查过”的技术:

  • Microsoft Accelerator
  • Cudafy

简而言之,甚至可以在GPU上对对象进行内存中过滤吗?

假设我们有一些对象列表,我们希望过滤类似的内容:

var result = myList.Where(x => x.SomeProperty == SomeValue);

关于这个的任何指针?

提前致谢!

更新

我会尝试更具体地说明我想要实现的目标:)

目标是使用能够以绝对最快的方式过滤对象列表(范围从~50 000到~2 000 000)的任何技术。

我在使用内置的LINQ方法完成过滤完成后对数据执行的操作(sum,min,max等),对于我们的应用程序已经足够快了,所以这不是问题。 / p>

瓶颈是“简单地”过滤数据。

更新

只是想补充说我已经测试了大约15个数据库,包括MySQL(检查可能的集群方法/ memcached解决方案),H2,HSQLDB,VelocityDB(目前正在进一步调查),SQLite,MongoDB等,而NONE就足够了过滤数据的速度(当然,NO-sql解决方案不提供像sql那样,但你得到了想法)和/或返回实际数据。

总结一下我/我们需要的东西:

一个数据库,能够在不到100毫秒的时间内以200列和约250 000行的格式对数据进行排序。

我目前有一个带有并行化LINQ的解决方案,当过滤 AND 处理结果时,它能够(在特定的机器上)在每一行上只花费 nano 秒。

因此,我们需要对每一行进行亚纳米 - 第二次过滤。

  1. 为什么只有内存中的LINQ能够提供这个?
  2. 为什么这不可能?
  3. 日志文件中的一些数字:

    Total tid för 1164 frågor: 2579
    

    这是瑞典语并翻译:

    Total time for 1164 queries: 2579
    

    在这种情况下,查询的查询类似于:

    WHERE SomeProperty = SomeValue
    

    这些查询都是在225639行上并行完成的。

    因此,225639行在内存中被过滤了1164次,大约2.5秒。

    这是9,5185952917007032597107300413827e-9秒/行,但是还包括数字的实际处理!我们做Count(非null),总计数,Sum,Min,Max,Avg,Median。因此,我们对这些过滤的行进行了7次操作。

    所以,我们可以说它实际上比我们尝试的数据库快<7倍,因为我们 NOT 在那些情况下执行任何聚合!

    因此,总之,与内存LINQ过滤相比,为什么数据库在过滤数据方面如此差劲?微软真的做得这么好,以至于无法与它竞争吗? :)

    虽然内存中过滤应该更快,但我不希望感觉它更快。我想知道什么更快,如果可能为什么

5 个答案:

答案 0 :(得分:9)

我将最终回答有关梵天的问题,因为它是我的图书馆,但它也可能适用于其他方法。 GPU不了解对象。它的内存也大部分与CPU内存完全分开。

如果您确实拥有大量对象并希望对其进行操作,则只能将要操作的数据打包到适合您正在使用的GPU / API的缓冲区中并将其发送以进行处理。

请注意,这将在CPU-GPU内存接口上进行两次往返,所以如果你在GPU上没有做足够的工作以使其值得,那么你将比在简单地使用CPU中的速度慢。第一名(如上面的样本)。

希望这有帮助。

答案 1 :(得分:4)

GPU实际上不是用于所有通用计算目的,特别是对于像这样的面向对象设计,过滤任意数据集合这样做真的不合适。

GPU计算非常适合在大型数据集上执行相同操作的事情 - 这就是矩阵运算和变换等事情非常好的原因。在那里,GPU上令人难以置信的快速计算能力可以超越数据复制......

在这种情况下,您必须将所有数据复制到GPU中以使其工作,并将其重组为GPU将理解的某种形式,这可能比仅在软件中执行过滤器更昂贵第一名。

相反,我建议使用PLINQ来加速这种性质的查询。如果您的过滤器是线程安全的(它必须与任何GPU相关的工作......)这可能是通用查询优化的更好选择,因为它不需要内存复制您的数据。 PLINQ可以通过将您的查询重写为:

var result = myList.AsParallel().Where(x => x.SomeProperty == SomeValue);

如果谓词是一项昂贵的操作,或者集合非常大(并且易于分区),与标准的LINQ to Objects相比,这可以显着提高整体性能。

答案 2 :(得分:3)

GpuLinq

GpuLinq的主要任务是通过LINQ实现GPGPU编程的民主化。主要思想是我们将查询表示为表达式树,并在各种转换优化之后将其编译为快速OpenCL内核代码。此外,我们提供了一个非常容易工作的API,而无需弄乱OpenCL API的细节。

https://github.com/nessos/GpuLinq

答案 3 :(得分:1)

select *
from table1  -- contains 100k rows
left join table2 -- contains 1M rows
on table1.id1=table2.id2 -- this would run for ~100G times 
                         -- unless they are cached on sql side
where table1.id between 1 and 100000 -- but this optimizes things (depends)

可以变成

select id1 from table1 -- 400k bytes if id1 is 32 bit 
-- no need to order

存储在内存中

select id2 from table2 -- 4Mbytes if id2 is 32 bit
-- no need to order

存储在内存中,两个数组都使用内核(cuda,opencl)发送到gpu,如下所示

int i=get_global_id(0); // to select an id2, we need a thread id
int selectedID2=id2[i];
summary__=-1;
for(int j=0;j<id1Length;j++)
{
      int selectedID1=id1[j];
      summary__=(selectedID2==selectedID1?j:summary__); // no branching
}
summary[i]=j; // accumulates target indexings of 
"on table1.id1=table2.id2" part.

在主机方面,您可以

 select * from table1 --- query3

 select * from table2 --- query4

然后使用gpu中的id列表来选择数据

 // x is table1 ' s data
 myList.AsParallel().ForEach(x=>query3.leftjoindata=query4[summary[index]]);

对于具有恒定内存,全局广播能力和数千个核心的gpu,gpu代码不应低于50ms。

如果使用任何三角函数进行过滤,性能会快速下降。此外,当左表连接表行数使得它的O(m * n)复杂度因此数百万而不是数百万会慢得多。 GPU内存带宽非常重要。

修改 我的hd7870(1280核)和R7-240(320核)上的gpu.findIdToJoin(table1,table2,“id1”,“id2”)的单个操作,带有“products table(64k rows)”和“categories table( 64k行)“(左连接过滤器)使用未优化的内核需要48毫秒。

Ado.Net的“nosql”风格linq-join花费超过2000毫秒,只有44k产品和4k类别表。

修改-2:

当字符串增长到1000行,每行至少有数百个字符时,带有字符串搜索条件的左连接在gpu上快50到200倍。

答案 4 :(得分:0)

您的用例的简单答案是否定的。

1)即使在原始的linq to object中,也没有针对这种工作负载的解决方案,更不用说会替换你的数据库了。

2)即使您一次性加载整个数据集(这需要时间),它仍然会慢得多,因为GPU具有高吞吐量但是它们的访问时间很长,所以如果您正在寻找at&#34;非常&#34;快速解决方案GPGPU通常不是答案,因为只是准备/发送工作负载并且返回结果会很慢,在您的情况下可能也需要以块的形式完成。