F# неизменяемые структуры данных для высокочастотных потоковых данных в реальном времени
мы находимся в начале проекта f# с участием реального времени и исторического анализа потоковых данных. Данные содержатся в объекте c# (см. ниже) и отправляются как часть стандартного события .net. В режиме реального времени количество событий, которые мы обычно получаем, может сильно варьироваться от менее 1/С до более 800 событий в секунду на инструмент и, таким образом, может быть очень бурным. Типичный день может накапливать 5 миллионов строк / элементов в insturment
универсальный вариант структура данных события C# выглядит следующим образом:
public enum MyType { type0 = 0, type1 = 1}
public class dataObj
{
public int myInt= 0;
public double myDouble;
public string myString;
public DateTime myDataTime;
public MyType type;
public object myObj = null;
}
мы планируем использовать эту структуру данных в f# двумя способами:
- исторический анализ с использованием контролируемого и неконтролируемого машинного обучения (CRFs, модели кластеризации и т. д.)
- классификация потоков данных в реальном времени с использованием вышеуказанных моделей
структура данных должна быть в состоянии расти, поскольку мы добавляем больше событий. Это исключает array<t>
потому что это не позволяет изменение размера, хотя его можно использовать для исторического анализа. Структура данных также должна иметь возможность быстрого доступа к последним данным и в идеале должна иметь возможность перехода к данным X точек назад. Это исключает Lists<T>
из-за линейного времени поиска и потому, что нет случайного доступа к элементам, просто "только вперед" обход.
по данным этот пост, Set<T>
может быть хорошим выбором...
EDIT:ответ Инь Чжу дал мне дополнительную ясность в том, что я спрашивал. Я отредактировал оставшуюся часть сообщения, чтобы отразить это. Кроме того, предыдущая версия этого вопроса была замутнена введением требований к исторический анализ. Я опустил их.
вот разбивка шагов процесса в реальном времени:
- событие в реальном времени получено
- это событие помещается в структуру данных. это структура данных, которую мы пытаемся определить. Должно ли это быть
Set<T>
, или какая-то другая структура? - подмножество элементов либо извлекается, либо каким-то образом повторяется с целью создания объектов. Этот будут либо последние n строк/элементов структуры данных (т. е. последние 1000 событий или 10 000 событий) или все элементы за последние X секунд / минут (i.e все события за последние 10 минут). В идеале нам нужна структура, которая позволяет нам делать это эффективно. В частности, имеет значение структура данных, которая позволяет произвольный доступ n-го элемента без итерации через все остальные элементы.
- функции для модели генерируются и отправляются на модели оценка.
- мы можем обрезать структуру данных старых данных для повышения производительности.
таким образом, вопрос в том, какая структура данных лучше всего использовать для хранения потоковых событий в реальном времени, которые мы будем использовать для сгенерированных функций.
4 ответов
следует считать FSharpx.Коллекции.Вектор. Vector
в своем ответе Джек Фокс предлагает использовать либо FSharpx.Сборники Vector<'T>
или сплошная Vector<'t>
Грега Розенбаума (https://github.com/GregRos/Solid). Я подумал, что мог бы дать немного назад сообществу, предоставив Инструкции о том, как встать и работать с каждым из них.
использование FSharpx.Коллекции.Вектор
процесс довольно прямолинейный:
- скачать FSharpx.Основной пакет NuGet использование либо консоли Project Manager, либо пакетов NuGet Manager для решения. Оба находятся в Visual Studio - > tools - > Library Manager.
- если вы используете его в файле скрипта F#, добавьте
#r "FSharpx.Core.dll"
. Возможно, вам придется использовать полный путь.
использование:
open FSharpx.Collections
let ListOfTuples = [(1,true,3.0);(2,false,1.5)]
let vector = ListOfTuples |> Vector.ofSeq
printfn "Last %A" vector.Last
printfn "Unconj %A" vector.Unconj
printfn "Item(0) %A" (vector.[0])
printfn "Item(1) %A" (vector.[1])
printfn "TryInitial %A" dataAsVector.TryInitial
printfn "TryUnconj %A" dataAsVector.Last
через сплошную.Вектор
получение настройки для использования Solid Vector<'t>
- это немного сложнее. Но твердая версия имеет гораздо более удобную функциональность и как Jack отмечено, имеет ряд преимуществ. Он также имеет много полезной документации.
- вам нужно будет загрузить решение visual studio изhttps://github.com/GregRos/Solid
- после того, как вы загрузили его, вам нужно будет построить его, так как нет готовых к использованию предварительно построенной dll.
- если вы похожи на меня, вы можете столкнуться с рядом отсутствующих зависимостей, которые мешают решения строится. В моем случае, они все они были связаны с фреймворками тестирования nuit (я использую другой). Просто работайте через загрузку / добавление каждой из зависимостей, пока решения не будут построены.
- как только это будет сделано, и решение будет построено, у вас будет блестящее новое твердое тело.dll в папке Solid/Solid / bin. Вот тут я ошибся. Это основная dll и достаточно только для использования C#. Если только включить ссылку на твердое тело.dll вы сможете создать вектор в f#, но фанки вещи будут с этого момента.
- чтобы использовать эту структуру данных в F#, вам необходимо указать как
Solid.dll
иSolid.FSharp.dll
который находится в . Вам понадобится только одно открытое заявление ->open Solid
вот некоторый код, показывающий использование в файле скрипта F#:
#r "Solid.dll"
#r "Solid.FSharp.dll" // don't forget this reference
open Solid
let ListOfTuples2 = [(1,true,3.0);(2,false,1.5)]
let SolidVector = ListOfTuples2 |> Vector.ofSeq
printfn "%A" SolidVector.Last
printfn "%A" SolidVector.First
printfn "%A" (SolidVector.[0])
printfn "%A" (SolidVector.[1])
printfn "Count %A" SolidVector.Count
let test2 = vector { for i in {0 .. 100} -> i }
Предположим ваш dataObj
содержит уникальное поле ID, тогда любая структура данных набора будет в порядке для вашей работы. Неизменяемые структуры данных в основном используются для кода функционального стиля или персистентности. Если вам не нужны эти два, вы можете использовать HashSet<T>
или SortedSet<T>
в библиотеке коллекции .Net.
некоторая оптимизация потока может быть полезна, например, сохранение фиксированного размера Queue<T>
для самых последних объектов данных в потоке и храните более старые объекты в более тяжелом весе набор. Я бы предложил бенчмаркинг перед переключением на такие гибридные решения структуры данных.
Edit:
после более тщательного чтения ваших требований я обнаружил, что вам нужна очередь с доступным для пользователя индексированием или обратным перечислителем. В этой структуре данных ваши операции извлечения объектов(например, среднее/сумма и т. д.) стоят O (n). Если вы хотите выполнить некоторые операции в O (log n), вы можете использовать более сложные структуры данных, например интервальные деревья или списки пропусков. Однако вам придется реализовать эти структуры данных самостоятельно, так как вам нужно хранить метаданные в узлах дерева, которые находятся за API сбора.
это событие помещается в структуру данных. Это структура данных, которую мы пытаемся определить. Должен ли это быть набор, очередь или какая-то другая структура?
трудно сказать без дополнительной информации.
если ваши данные поступают с метками времени в порядке возрастания (т. е. они никогда не выходят из строя), вы можете просто использовать какую-то очередь или расширяемый массив.
если ваши данные могут прийти в не в порядке, и вы нуждаетесь в них переупорядочено, тогда вам нужна приоритетная очередь или индексированная коллекция.
до более чем 800 событий в секунду
те весьма ручные требования производительности для тарифа ввода.
подмножество элементов либо извлекается, либо каким-то образом повторяется с целью создания объектов. Это будут либо последние n строк/элементов структуры данных (т. е. последние 1000 событий или 10 000 событий) или все элементы за последние X сек / мин (i.e все события за последние 10 минут). В идеале нам нужна структура, которая позволяет нам делать это эффективно. В частности, имеет значение структура данных, которая позволяет произвольный доступ n-го элемента без итерации через все остальные элементы.
если вы только когда-либо хотите элементы в начале, почему вы хотите случайный доступ? Вы действительно хотите случайный доступ по индексу или вы действительно хотите случайный доступ по какому-то другому ключу, например время?
из того, что вы сказали, я бы предложил использовать обычный F# Map
введено по индексу, поддерживаемому MailboxProcessor
это может добавить новое событие и получить объект, который позволяет индексировать все события, т. е. обернуть Map
в объекте, который предоставляет свою собственную Item
свойство и реализация IEnumerable<_>
. На моей машине, что простое решение занимает 50 строк кода и может обрабатывать около 500 000 событий в секунду.