Можно ли ускорить (динамические) запросы LINQ с помощью графического процессора?

Я несколько дней искал достоверную информацию о возможности ускорения запросов LINQ с помощью графического процессора.

Технологии, которые я «исследовал» до сих пор:

  • Акселератор Майкрософт
  • Кудафи
  • Брахма

Короче говоря, возможно ли вообще выполнять фильтрацию объектов в памяти на графическом процессоре?

Допустим, у нас есть список некоторых объектов, и мы хотим отфильтровать что-то вроде:

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

Любые указатели на этот?

Заранее спасибо!

ОБНОВЛЕНИЕ

Я постараюсь быть более конкретным в отношении того, чего я пытаюсь достичь :)

Цель состоит в том, чтобы использовать любую технологию, способную отфильтровать список объектов (от ~ 50 000 до ~ 2 000 000) максимально быстрым способом.

Операции, которые я выполняю с данными при фильтрации (сумма, минимум, максимум и т. д.), выполняются с использованием встроенных методов LINQ и уже достаточно быстры для нашего приложения, так что это не проблема.

Узким местом является «просто» фильтрация данных.

ОБНОВЛЕНИЕ

Просто хотел добавить, что я протестировал около 15 баз данных, включая MySQL (проверка возможного кластерного подхода / решения memcached), H2, HSQLDB, VelocityDB (в настоящее время исследуется), SQLite, MongoDB и т. д., и NONE достаточно хорош, когда дело доходит до скорость фильтрации данных (конечно, решения NO-sql не предлагают этого, как решения sql, но вы поняли идею) и/или возврата фактических данных.

Просто подытожу, что мне/нам нужно:

База данных, способная сортировать данные в формате 200 столбцов и около 250 000 строк менее чем за 100 мс.

В настоящее время у меня есть решение с распараллеленным LINQ, которое способно (на определенной машине) тратить всего нано-секунды на каждую строку при фильтрации И обработки результата!

Итак, нам нужна менее нано-секундная фильтрация в каждой строке.

  1. Почему кажется, что это может обеспечить только LINQ в памяти?
  2. Почему это невозможно?

Некоторые цифры из лог-файла:

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 (не нуль), общее количество, Sum, Min, Max, Avg, Median. Итак, у нас есть 7 операций над этими отфильтрованными строками.

Таким образом, мы можем сказать, что на самом деле она в 7 раз быстрее, чем те базы данных, которые мы пробовали, поскольку в этих случаях мы НЕ выполняем какие-либо действия по агрегированию!

Итак, в заключение, почему базы данных так плохо фильтруют данные по сравнению с фильтрацией LINQ в памяти? Неужели Microsoft так хорошо поработала, что с ней невозможно конкурировать? :)

Есть смысл в том, что фильтрация в памяти должна быть быстрее, но мне не нужно ощущение, что она быстрее. Я хочу знать, что работает быстрее и, если возможно, почему.


person Johan    schedule 15.02.2012    source источник
comment
не то чтобы я знаю, но +1 за идею. Я предвосхищаю ответы.   -  person Jodrell    schedule 15.02.2012
comment
Посмотрите github.com/palladin/LinqOptimizer/tree/gpu.   -  person Mauricio Scheffer    schedule 13.01.2014


Ответы (5)


Я отвечу определенно о Брахме, поскольку это моя библиотека, но, вероятно, это применимо и к другим подходам. GPU ничего не знает об объектах. Его память также в основном полностью отделена от памяти процессора.

Если у вас действительно БОЛЬШОЙ набор объектов и вы хотите работать с ними, вы можете упаковать только те данные, с которыми хотите работать, в буфер, подходящий для используемого вами графического процессора/API, и отправить их для обработки.

Обратите внимание, что это совершит два круговых прохода через интерфейс памяти ЦП-ГП, поэтому, если вы не выполняете достаточно работы на ГП, чтобы сделать его стоящим, вы будете медленнее, чем если бы вы просто использовали ЦП в первую очередь ( как в образце выше).

Надеюсь это поможет.

person Ani    schedule 15.02.2012
comment
Спасибо! Мне нравится ваша библиотека, и я использовал ее для других вещей, кроме того, о чем я спрашиваю выше! Все, что я действительно хочу сделать, это очень быстро фильтровать объекты :) Продолжайте в том же духе с Brahma! - person Johan; 15.02.2012
comment
@ Ани, где я могу найти информацию о Брахме? Любое руководство и / или примеры в любом месте? - person Alex Sanséau; 03.05.2018
comment
@AlexSanséau Извините, я уже несколько лет активно разрабатываю этот проект. Вы можете ознакомиться с версией F# здесь (github.com/gsvgit/Brahma.FSharp) - person Ani; 05.05.2018

Графический процессор действительно не предназначен для всех вычислительных целей общего назначения, особенно с объектно-ориентированными проектами, подобными этому, и фильтрация произвольного набора данных, подобного этому, действительно была бы неуместной.

Вычисления на графическом процессоре отлично подходят для тех случаев, когда вы выполняете одну и ту же операцию с большим набором данных, поэтому такие операции, как матричные операции и преобразования, могут быть очень удобными. Там копирование данных может быть перевешено невероятно быстрыми вычислительными возможностями GPU....

В этом случае вам придется скопировать все данные в графический процессор, чтобы заставить это работать, и реструктурировать их в форме, понятной графическому процессору, что, вероятно, будет дороже, чем просто выполнение фильтра в программном обеспечении в первую очередь. .

Вместо этого я бы рекомендовал использовать PLINQ для ускорения запросов такого рода. При условии, что ваш фильтр является потокобезопасным (что должно быть для любой работы, связанной с графическим процессором...), это, вероятно, лучший вариант для оптимизации запросов общего назначения, поскольку он не требует копирования памяти ваших данных. PLINQ будет работать, переписав ваш запрос как:

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

Если предикат является дорогостоящей операцией или коллекция очень большая (и легко разделяемая), это может значительно улучшить общую производительность по сравнению со стандартным LINQ to Objects.

person Reed Copsey    schedule 15.02.2012
comment
Спасибо за ответы! Тем не менее, именно этот подход мы используем сегодня. Этого почти достаточно, но скоро у нас будет намного больше строк, и что ж... Даже 16-ядерной машины, которая у нас есть, уже недостаточно :( - person Johan; 15.02.2012
comment
@Johan В какой-то момент использование базы данных и методов, которые вызывают ускорение фильтрации на стороне запроса (например, EF), приведет к тому, что этот тип операции будет НАМНОГО быстрее с большими наборами данных, особенно когда фильтр представляет собой простую проверку свойств. ... Стоимость копирования на GPU была бы чрезмерно высокой для такого типа операций. Что вы на самом деле делаете? Что это за данные? - person Reed Copsey; 15.02.2012
comment
ну, это список объектов, которые поступают из таблицы (я использую Dapper для получения данных). В таблице много столбцов (не нормализованных по соображениям производительности), но не так много строк (около 250 000). Однако все базы данных, которые я пробовал до сих пор, НАМНОГО медленнее по сравнению с фильтрацией в памяти. Знаете ли вы какой-нибудь чрезвычайно быстрый метод получения данных, оптимизированный для определения местоположения? :) - person Johan; 15.02.2012
comment
@ Йохан, даже если у тебя есть индекс SomeProperty? - person Jodrell; 15.02.2012
comment
@Johan Если в вашем запросе используется индексированный столбец, БД должна быть НАМНОГО быстрее, чем любой запрос в памяти. Какую базу данных вы используете? Индексируются ли столбцы вашего запроса? - person Reed Copsey; 15.02.2012
comment
@ReedCopsey, мы протестировали: MySQL H2 HSQLDB (MongoDB) (RedisCache) eXtremeDB (использует отражение и поэтому медленнее) SQLite и некоторые другие альтернативы. Возможно, они быстро фильтруют, но в какой-то момент вы должны получить результаты. - person Johan; 15.02.2012
comment
@Johan Как правило, самый быстрый способ справиться с этим типом сценария - выполнить весь запрос, включая сумму, в БД. При правильном индексировании это должно на много порядков превосходить любую фильтрацию памяти... - person Reed Copsey; 15.02.2012
comment
@ Джодрелл, да, фильтрация может быть достаточно быстрой. Но не передача результатов. Что ж, думаю, мне просто нужно больше стараться :) Немного информации: сегодня мы используем цикл Parallel.For. Мы ожидаем, что будет выполнено около 1000 одновременных запросов. Не лучше ли сделать обычный последовательный цикл и использовать метод AsParallel-extension вместо Parallel.For без AsParallel? Заранее спасибо! - person Johan; 15.02.2012
comment
@Johan Это действительно во многом зависит от того, что делают ваши запросы. Для больших наборов данных хранение обычно является ключевым... но правильное хранение зависит от того, как вы будете получать доступ к данным и что вы с ними делаете. - person Reed Copsey; 15.02.2012
comment
@ReedCopsey, спасибо за ответ! Хорошо, я еще раз взгляну на использование подхода к базе данных. Есть ли у вас какие-либо рекомендации (относительно высокой производительности)? - person Johan; 16.02.2012
comment
@Johan Конечно, зависит от данных, о которых идет речь, но MySQL обычно довольно быстр для запросов, а SQL Server очень настраиваемый ... Почти любое реальное решение для БД должно справляться с этим лучше, чем подход в памяти, для большинства вещей, хотя . - person Reed Copsey; 16.02.2012
comment
@ReedCopsey, хорошо, спасибо. Я посмотрю еще немного на это. Тем не менее, я не очень в этом уверен, так как к настоящему моменту я перепробовал МНОЖЕСТВО баз данных, включая VelocityDB, BerkeleyDB и т. д. Я бы сказал, почти все известные базы данных. Все они слишком медленные, либо фильтруют строки, либо возвращают их для дальнейшей обработки. :( - person Johan; 17.02.2012
comment
@ReedCopsey, пожалуйста, посмотрите мой обновленный вопрос, если у вас есть время. - person Johan; 17.02.2012
comment
@Johan Я подозреваю, что проблема в том, что вы неправильно настраиваете БД. Вы должны выполнять агрегацию В БД, а не извлекать результаты и фильтровать в памяти, и я подозреваю, что вы будете превосходить это каждый раз ... Однако ключ - это правильное индексирование. - person Reed Copsey; 17.02.2012

GpuLinq

Основная миссия GpuLinq — демократизировать программирование GPGPU с помощью LINQ. Основная идея заключается в том, что мы представляем запрос в виде дерева выражений и после различных преобразований-оптимизаций компилируем его в быстрый код ядра OpenCL. Кроме того, мы предоставляем очень простой в работе API без необходимости вникать в детали OpenCL API.

https://github.com/nessos/GpuLinq

person user1760527    schedule 03.09.2014

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

хранятся в памяти, оба массива отправляются на gpu с использованием ядра (cuda, opencl), как показано ниже.

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, чтобы выбрать данные

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

Код GPU не должен быть медленнее 50 мс для GPU с постоянной памятью, возможностью глобальной трансляции и несколькими тысячами ядер.

Если для фильтрации использовать какую-либо тригонометрическую функцию, производительность быстро упадет. Кроме того, когда количество строк в левых соединенных таблицах делает его сложность O (m * n), поэтому миллионы против миллионов будут намного медленнее. Здесь важна пропускная способность памяти графического процессора.

Изменить: Одна операция gpu.findIdToJoin(table1,table2,"id1","id2") на моем hd7870(1280 ядер) и R7-240(320 ядер) с таблицей продуктов(64k rows)» и «таблица категорий (64k строк)» (левый фильтр соединения) заняли 48 миллисекунд с неоптимизированным ядром.

linq-join Ado.Net в стиле «nosql» заняло более 2000 мс только с 44 тыс. продуктов и 4 тыс. таблиц категорий.

Правка-2:

левое соединение с условием поиска строки становится в 50-200 раз быстрее на GPU, когда таблицы вырастают до 1000 строк, каждая из которых содержит не менее сотни символов.

person huseyin tugrul buyukisik    schedule 24.10.2015

Простой ответ для вашего варианта использования - нет.

1) Нет решения для такой рабочей нагрузки даже в необработанном linq to object, тем более в чем-то, что заменит вашу базу данных.

2) Даже если у вас все в порядке с загрузкой всего набора данных сразу (это требует времени), это все равно будет намного медленнее, поскольку GPU имеет высокую пропускную способность, но их доступ с высокой задержкой, поэтому, если вы смотрите на «очень» быстро решения GPGPU часто не является ответом, так как просто подготовка/отправка рабочей нагрузки и получение результатов будут медленными, и в вашем случае, вероятно, их также нужно выполнять по частям.

person Ronan Thibaudau    schedule 03.09.2014