Можно ли ускорить (динамический) запрос LINQ с помощью GPU?
Я искал несколько дней для получения достоверной информации о возможности ускорения запросов LINQ с использованием графического процессора.
Технологии, которые я исследовал до сих пор:
- Microsoft Accelerator
- Cudafy
- Брахма
Короче говоря, возможно ли вообще вообще сделать фильтрацию объектов на GPU в памяти?
Предположим, у нас есть список некоторых объектов, и мы хотим отфильтровать что-то вроде:
var result = myList.Where(x => x.SomeProperty == SomeValue);
Любые указатели на этом?
Спасибо заранее!
UPDATE
Я постараюсь более конкретно узнать, чего я пытаюсь достичь:)
Цель состоит в том, чтобы использовать любую технологию, которая может фильтровать список объектов (от ~ 50 000 до ~ 2 000 000) самым быстрым способом.
Операции, которые я выполняю на данных при завершении фильтрации (sum, min, max и т.д.), выполняются с использованием встроенных методов LINQ и уже достаточно быстро для нашего приложения, так что это не проблема.
Узким местом является "просто" фильтрация данных.
UPDATE
Просто захотелось добавить, что я тестировал около 15 баз данных, включая MySQL (проверка возможного подхода к кластеру/решение memcached), H2, HSQLDB, VelocityDB (в настоящее время исследует далее), SQLite, MongoDB и т.д., и NONE достаточно хорош, когда приходит к скорости фильтрации данных (конечно, NO-sql-решения не предлагают такого, как sql-те, но вы получаете идею) и/или возврат фактических данных.
Просто подытожим то, что мне нужно:
База данных, способная сортировать данные в формате 200 столбцов и около 250 000 строк менее чем за 100 мс.
В настоящее время у меня есть решение с параллелизированным LINQ, которое может (на определенной машине) тратить только nano -секунды на каждую строку при фильтрации И обработки результата!
Итак, нам нужна суб- nano -секундная фильтрация для каждой строки.
- Почему кажется, что только в памяти LINQ может это предоставить?
- Почему это невозможно?
Некоторые цифры из файла журнала:
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 раз быстрее, чем в базах данных, которые мы пробовали, поскольку мы делаем НЕ любые агрегирующие материалы в этих случаях
Итак, в заключение, почему базы данных настолько плохи при фильтрации данных по сравнению с фильтрацией LINQ в памяти? Действительно ли Microsoft сделала такую хорошую работу, что с ней невозможно конкурировать?:)
Это имеет смысл, хотя фильтрация в памяти должна быть быстрее, но мне не нужен смысл, что он быстрее. Я хочу знать, что быстрее, и если возможно , почему.
Ответы
Ответ 1
Я окончательно отвечу о Брахме, так как это моя библиотека, но она, вероятно, относится и к другим подходам. Графический процессор не знает объектов. Эта память также в основном полностью отделена от памяти ЦП.
Если у вас есть БОЛЬШОЙ набор объектов и вы хотите работать с ними, вы можете упаковать только те данные, которые хотите работать, в буфер, подходящий для используемого вами графического процессора /API, и отправить его для обработки,
Обратите внимание, что это сделало бы две круглые поездки по интерфейсу памяти CPU-GPU, поэтому, если вы не выполняете достаточную работу на графическом процессоре, чтобы сделать это стоящим, вы будете медленнее, чем если бы вы просто использовали CPU в первое место (например, образец выше).
Надеюсь, что это поможет.
Ответ 2
Графический процессор действительно не предназначен для всех целей общего назначения, особенно с объектно-ориентированными проектами, подобными этому, и фильтрация произвольного набора данных, подобных этому, действительно не подходит.
Вычисления GPU отлично подходят для вещей, где вы выполняете одну и ту же операцию в большом наборе данных - вот почему такие вещи, как операции с матрицами и преобразования, могут быть очень приятными. Там копирование данных может быть перевешивается невероятно быстрыми вычислительными возможностями на GPU....
В этом случае вам нужно будет скопировать все данные в графический процессор, чтобы выполнить эту работу, и перестроить его в той или иной форме, какой будет понимать GPU, что, вероятно, будет более дорогостоящим, чем просто выполнение фильтра в программном обеспечении в первое место.
Вместо этого я бы рекомендовал использовать PLINQ для ускорения запросов такого рода. Если ваш фильтр является потокобезопасным (что должно быть для любой работы с графическим процессором...), это, скорее всего, лучший вариант для оптимизации запросов общего назначения, поскольку для копирования данных ваши данные не потребуются. PLINQ работал бы, переписывая ваш запрос как:
var result = myList.AsParallel().Where(x => x.SomeProperty == SomeValue);
Если предикат является дорогостоящей операцией или сбор очень велик (и легко разбивается на разделы), это может значительно улучшить общую производительность по сравнению со стандартными LINQ to Objects.
Ответ 3
GpuLinq
Основная задача GpuLinq - демократизировать программирование GPGPU через LINQ. Основная идея заключается в том, что мы представляем запрос как дерево выражений, а после различных преобразований-преобразований мы скомпилируем его в быстрый код ядра OpenCL. Кроме того, мы предоставляем очень простой в работе API без необходимости возиться с деталями API OpenCL.
https://github.com/nessos/GpuLinq
Ответ 4
Простой ответ для вашего случая использования - нет.
1) Нет решения для такого рода рабочей нагрузки, даже в необработанном linq для объекта, а тем более в чем-то, что заменит вашу базу данных.
2) Даже если вы были в порядке с загрузкой всего набора данных одновременно (это требует времени), все равно будет намного медленнее, так как у GPU высокая пропускная способность, но их доступ имеет высокую задержку, поэтому, если вы смотрите на "очень" быстрые решения" GPGPU часто не являются ответом, так как просто подготовка/отправка рабочей нагрузки и возвращение результатов будут медленными, и в вашем случае, вероятно, также необходимо сделать куски.
Ответ 5
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
затем используйте список id из gpu, чтобы выбрать данные
// x is table1 ' s data
myList.AsParallel().ForEach(x=>query3.leftjoindata=query4[summary[index]]);
Код gpu не должен быть медленнее, чем 50 мс для gpu с постоянной памятью, возможностью глобального вещания и несколькими тысячами ядер.
Если для фильтрации используется любая тригонометрическая функция, производительность будет быстро снижаться. Кроме того, когда слева объединенные таблицы подсчет строк делает сложностью O (m * n), то миллионы против миллионов будут намного медленнее. Здесь важна пропускная способность памяти GPU.
Edit:
Единственная операция gpu.findIdToJoin(таблица1, таблица2, "id1", "id2" ) на моем hd7870 (1280 ядер) и R7-240 (320 ядер) с "таблицей продуктов (строки 64k)" и "таблица категорий ( 64k строк)" (фильтр левого соединения) занял 48 миллисекунд с неоптимизированным ядром.
Ado.Net "nosql" стиль linq-join занял более 2000 мс только с 44 тыс. продуктов и 4 тыс. категорий категорий.
Edit-2:
левое соединение с условием поиска строки получает от 50 до 200 x быстрее на gpu, когда таблицы вырастают до 1000 строк, каждый из которых имеет по меньшей мере сотни символов.