Какие проблемы, если у дискриминируемого союза есть много вариантов?

Да, тривиальный вопрос, но экспертного мнения по нему я не нашел.

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

  1. Существует ли рекомендуемый верхний предел количества опций, которые может иметь DU? В настоящее время у моего DU есть девять вариантов, но это число будет увеличиваться по мере развития проекта. Что, если я достигну 30 или 40 к концу проекта?

  2. Может ли возникнуть проблема, если некоторые из опций станут «длинными»? В настоящее время средний вариант имеет четыре или пять основных типов — что-то вроде bool * string * XElement * int * string — но самый длинный вариант имеет следующее определение:

    bool * int * int * int * string * XElement * XElement * DateTime option * DateTime option * string * Dictionary * Dictionary

Я не ожидаю, что многие варианты будут где-то рядом с этим долго. Но настраиваю ли я себя на мир боли с точки зрения производительности?

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


person Shredderroy    schedule 14.05.2013    source источник
comment
Здесь есть два возможных ограничения: пределы реализации (говорит ли что-нибудь спецификация языка F#?) или влияние на производительность (почему бы не попробовать: DU из 1000 членов работает медленнее, чем DU из 2 членов?)   -  person Richard    schedule 14.05.2013
comment
Да, я полагаю, что в конце концов мне придется это сделать. Но я не знаю, как сгенерировать DU с 1000 опций. (Может быть, мне нужно больше читать цитаты из кода?) Поэтому мне было интересно, есть ли у кого-то опыт работы с этим сценарием.   -  person Shredderroy    schedule 14.05.2013
comment
Простой способ создать DU со 100 вариантами fprintfn f "type t =";[0..100] |> List.map (fun x -> fprintf f "|T%i" x)   -  person John Palmer    schedule 14.05.2013
comment
@JohnPalmer, большое спасибо. Я использовал эту идею для создания более сложных DU для проверки производительности. Результаты выложу потом.   -  person Shredderroy    schedule 15.05.2013


Ответы (2)


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

  • Что касается количества случаев, то SynExpr размеченное объединение (см. исходный код) содержит более 50 случаев, поэтому я думаю, что это должно быть хорошо.

    Сопоставление шаблонов в размеченном объединении компилируется с использованием кода операции switch IL для целого числа, поэтому вы можете попробовать провести некоторое исследование эффективности этого, если хотите убедиться. Кроме того, если вы просто используете match для поиска одного конкретного случая, то это должно быть просто одно целочисленное сравнение, независимо от количества других случаев.

  • Что касается количества полей, самый длинный случай SynExpr имеет около 7 полей, но я полагаю, вы можете найти другие DU, где длина больше. (Я думаю, что большая проблема с таким количеством атрибутов заключается в удобочитаемости, потому что атрибуты не имеют имен. Поэтому я думаю, что лучше использовать запись для большого количества атрибутов, которые логически связаны друг с другом.)

Я думаю, что размер DU, который вы описали, должен быть в порядке, но я сам не проводил никаких тестов производительности, поэтому, если вы действительно хотите убедиться, вам нужно его измерить. (Но, как я уже сказал, я почти уверен, что это то, что было протестировано как часть разработки компилятора F #)

person Tomas Petricek    schedule 14.05.2013

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

Но даже при оптимизации DU превращаются в довольно большой объем кода. Таким образом, несмотря на то, что они могут работать так же хорошо (и, вероятно, лучше), чем любой эквивалентный поток управления с ручным кодом, существует вероятность того, что вы можете переполнить стек при простом количестве инструкций, выдаваемых для функции / тело метода (но это был бы довольно экстремальный сценарий, поскольку размер стека .NET по умолчанию составляет ~ 1 МБ, однако это, безусловно, может привести к более раннему, чем обычно, переполнению стека в нехвостовом рекурсивном методе / функции, включающем сопоставление большого DU, но опять же вряд ли до такой степени, что вы действительно должны бояться этого сценария).

Я не верю, что это изменит характеристики производительности (поскольку мы говорим об объектах, выделенных кучей в любом случае), но для удобства обслуживания/читабельности иногда помогает обернуть данные вашего случая DU в тип записи, чтобы поля данных были названы и сопоставление с образцом в подмножестве полей данных проще (например, {Name="Stephen"} вместо (_,_,_,_,_,_,_,"Stephen",_,_,_,_,_)). (@TomasPetricek опередил меня в этом предложении, не заметил этого при первом прочтении его ответа)

person Stephen Swensen    schedule 14.05.2013
comment
большое спасибо за понимание. До сих пор я использовал хак, вставляя строку комментария над опцией DU, указывающую, что означают переменные. Но предложение, сделанное вами и Томасом, действительно лучше. - person Shredderroy; 15.05.2013