Альтернативы обнуляемым типам в C # - PullRequest
12 голосов
/ 18 мая 2009

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

Наиболее часто используемый тип данных - double, и в настоящее время выбранная альтернатива null - double.NaN. Однако я понимаю, что это не точное предполагаемое использование значения NaN, поэтому я не уверен, есть ли какие-либо проблемы с этим, которые я не могу предвидеть, и каков будет наилучший опыт.

Мне интересно узнать, какие наилучшие нулевые альтернативы для следующих типов данных, в частности: double / float, decimal, DateTime, int / long (хотя другие приветствуются)

Редактировать: я думаю, что мне нужно уточнить мои требования к производительности. Концерты числовых данных обрабатываются с помощью этих алгоритмов одновременно, что занимает несколько часов. Поэтому, хотя разница между, например, 10 мс или 20 мс обычно незначительна, в этом сценарии она действительно оказывает значительное влияние на затраченное время.

Ответы [ 6 ]

18 голосов
/ 18 мая 2009

Что ж, если вы исключили Nullable<T>, у вас останутся значения доменов - то есть магическое число, которое вы рассматриваете как ноль. Хотя это не идеал , это также не редкость - например, большая часть основного кода фреймворка обрабатывает DateTime.MinValue так же, как ноль. Это по крайней мере отодвигает урон далеко от общих ценностей ...

изменить, чтобы выделить только там, где нет NaN

Так что там, где нет NaN, возможно, используйте .MinValue - но просто помните, что случается зло, если вы случайно используете это же значение, означающее то же число ...

Очевидно, что для неподписанных данных вам понадобится .MaxValue (избегайте нуля !!!).

Лично я бы попробовал использовать Nullable<T> для более безопасного выражения своих намерений ... возможно, есть способы оптимизировать ваш код Nullable<T>. А также - к тому времени, когда вы проверили магическое число во всех нужных вам местах, возможно, оно не будет намного быстрее, чем Nullable<T>?

4 голосов
/ 18 мая 2009

Я работаю над большим проектом, в котором в качестве значения null используется NaN. Я не совсем доволен этим - по тем же причинам, что и вы: не зная, что может пойти не так. Пока мы не сталкивались с какими-либо реальными проблемами, но имейте в виду следующее:

NaN-арифметика - Хотя "промо-акция NaN" в большинстве случаев полезна, она не всегда соответствует ожиданиям.

Сравнение - Сравнение значений становится довольно дорогим, если вы хотите, чтобы значения NaN сравнивались одинаково. Теперь проверить поплавки на равенство в любом случае непросто, но порядок (a Заражение кода - Я вижу много арифметического кода, который требует правильной обработки NaN. Таким образом, в результате вы получаете «функции, которые принимают NaN» и «функции, которые не принимают» по соображениям производительности.

Другие неконечные NaN - единственное неконечное значение. Следует иметь в виду ...

Исключения с плавающей точкой не являются проблемой при отключении. Пока кто-то не позволяет им. Правдивая история: Статическая инициализация NaN в элементе управления ActiveX. Звучит не страшно, пока вы не измените установку на использование InnoSetup, в котором используется ядро ​​Pascal / Delphi (?), Для которого по умолчанию включены исключения FPU. Мне понадобилось время, чтобы понять.

Так что, в общем, ничего серьезного, хотя я бы предпочел не рассматривать NaN так часто.


Я бы использовал типы Nullable как можно чаще, если только они (как доказано) не являются ограничениями производительности / ресурсов. Один случай может быть большими векторами / матрицами со случайными NaN или большими наборами именованных отдельных значений , где поведение NaN по умолчанию является правильным .


В качестве альтернативы, вы можете использовать индексный вектор для векторов и матриц, стандартных реализаций "разреженной матрицы" или отдельного вектора bool / bit.

4 голосов
/ 18 мая 2009

Я несколько не согласен с Грэвеллом в этом конкретном краевом случае: переменная с нулевым значением считается «не определенной», у нее нет значения. Итак, все, что используется для сигнализации, это нормально: даже магические числа, но с магическими числами вы должны учитывать, что магическое число всегда будет преследовать вас в будущем, когда оно внезапно станет «действительным» значением. С Double.NaN вам не нужно бояться этого: он никогда не станет действительным двойником. Хотя вы должны учитывать, что NaN в смысле последовательности двойников можно использовать только как маркер для «не определено», вы не можете использовать его как код ошибки в последовательностях, очевидно.

Таким образом, все, что используется для обозначения «неопределенного»: должно быть ясно в контексте набора значений, что это конкретное значение считается значением для «неопределенного» И это не изменится в будущем.

Если Nullable доставит вам слишком много хлопот, используйте NaN или что-то еще, если вы учитываете последствия: выбранное значение представляет собой «неопределенное», и оно останется.

2 голосов
/ 18 мая 2009

Частичный ответ:

Float и Double обеспечивают NaN (не число). NaN немного сложнее, так как, согласно спецификации, NaN! = NaN. Если вы хотите узнать, является ли число NaN, вам нужно использовать Double.IsNaN ().

См. Также Двоичные числа с плавающей запятой и .NET .

0 голосов
/ 22 декабря 2012

Можно избежать некоторого снижения производительности, связанного с Nullable<T>, определив собственную структуру

struct MaybeValid<T>
{
    public bool isValue;
    public T Value;
}

При желании можно определить конструктор или оператор преобразования от T до MaybeValid<T> и т. Д., Но чрезмерное использование таких вещей может привести к неоптимальной производительности. Структуры открытого поля могут быть эффективными, если избегать ненужного копирования данных. Некоторые люди могут не одобрять понятие открытых полей, но они могут быть намного более эффективными, чем свойства. Если функция, которая будет возвращать T, должна иметь переменную типа T для хранения ее возвращаемого значения, использование MaybeValid<Foo> просто увеличивает на 4 размер возвращаемого объекта. Напротив, использование Nullable<Foo> потребовало бы, чтобы функция сначала вычислила Foo, а затем передала его копию конструктору для Nullable<Foo>. Кроме того, для возврата Nullable<Foo> потребуется, чтобы любой код, который хочет использовать возвращаемое значение, должен был сделать хотя бы одну дополнительную копию в месте хранения (переменном или временном) типа Foo, прежде чем он сможет сделать что-нибудь полезное с ним. Напротив, код может использовать поле Value переменной типа Foo примерно так же эффективно, как и любая другая переменная.

0 голосов
/ 18 мая 2009

Возможно, значительное снижение производительности происходит при вызове одного из членов или свойств Nullable (бокс).

Попробуйте использовать структуру с двойным + логическим значением, указывающим, указано ли значение или нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...