Каков самый быстрый способ преобразования объекта в double? Я сейчас нахожусь в коде, который гласит:
var d = double.TryParse(o.ToString(), out d); // o is the Object...
Первые мысли заключались в том, чтобы переписать это как
var d = Convert.ToDouble(o);
но будет ли это на самом деле быстрее?
EDIT: В дополнение к запуску профиля (кстати, я настоятельно рекомендую JetBrains dotTrace для любого разработчика), я запустил Reflector, и это помогло мне прийти (более или менее соответствующая часть кода):
if (o is IConvertible)
{
d = ((IConvertible)o).ToDouble(null);
}
else
{
d = 0d;
}
Исходный код double.TryParse()
выполнен в 140 мс. Новый код выполняется в 34 мс. Я почти уверен, что это путь оптимизации, который я должен предпринять, но прежде чем я это сделаю, кто-нибудь видит что-то проблематичное с моим "оптимизированным" кодом? Заранее благодарим за ваши отзывы!
Ответ 1
Вы должны делать целую колоссальную сумму, чтобы иметь смысл потратить на это какое-то время. Однако меня здесь не судить:
Итак, ваш код таков:
if (o is IConvertible)
{
d = ((IConvertible)o).ToDouble(null);
}
else
{
d = 0d;
}
Интересно, будет ли вам лучше с этим
IConvertible convert = o as IConvertible;
if (convert != null)
{
d = convert.ToDouble(null);
}
else
{
d = 0d;
}
Сохраняет двойное нажатие.
Ответ 2
Я пробовал следующие методы.
- double.TryParse
- double.Parse
- Convert.ToDouble
Я использовал следующий код.
public static void Main()
{
string text = "3.14";
var timer = new Stopwatch();
timer.Start();
for (int i = 0; i < 10000000; i++)
{
double d;
d = Convert.ToDouble(text);
//double.TryParse(text, out d);
//d = double.Parse(text);
}
timer.Stop();
Console.WriteLine("Time=" + timer.Elapsed.ToString());
Console.ReadLine();
}
На моей машине я увидел эти результаты. Я усреднил 3 разных прогона.
- double.TryParse = 4.45 секунд
- double.Parse = 4.45 секунд
- Convert.ToDouble = 4.75 секунд
Конечно, я использовал строку, которая была конвертируемой. Если строка не конвертируема, то я очень подозреваю, что double.TryParse
будет самым быстрым на длинный снимок.
Ответ 3
Создайте небольшое тестовое приложение, используя System.Diagnostics.Stopwatch и узнайте, что происходит быстрее. Хотя я бы сказал, что это не будет иметь смысл. Я бы пошел на Convert.ToDouble
исключительно для удобочитаемости.
Ответ 4
Во-первых, если вы действительно хотите узнать, что быстрее, вы должны написать быстрый тест (используя данные, которые вы ожидаете для обработки) и время каждой опции. Не зная, что o
(или, вероятно, будет), очень трудно судить. Я подозреваю, что вы не увидите большой разницы.
Во-вторых, если вы не назовете этот бит кода в чрезвычайно критичной для времени части вашего кода и называя его тысячами времени для загрузки, я сомневаюсь, что это действительно имеет значение. Напишите хороший, чистый код, а затем оптимизируйте.
Ответ 5
Есть несколько разных вещей, которые вы могли бы попытаться сделать, в зависимости от того, что это такое. Это может быть
a) двойное двойное число, и вы просто хотите его удалить:
object o = 53.2;
double d = (double)o;
b) некоторый другой тип, значение или ссылка, которая имеет некоторое преобразование в двойное доступное (реализует IConvertible.ToDouble()), который вы хотите использовать
object o = 53.2M; // a System.Decimal
double d = Convert.ToDouble(o);
или
c) то, что имеет строковое представление по умолчанию, которое может быть проанализировано как двойное
object o = "53.2";
double d;
bool convertedOK = double.TryParse(o.ToString(), out d);
Вариант c, в некотором смысле, самый длинный путь; вы берете свой объект, запрашивая его строковое представление, а затем пытаетесь разобрать эту строку, чтобы получить двойной. Это неудобно, если вам не нужно это делать, и в вашем примере из 40 000 звонков он собирается создавать и удалять 40 000 строк...
Если вы знаете, что ваш объект всегда будет содержать что-то, что реализует преобразование в double, вы можете пропустить все это и перейти к опции b. И если вы знаете, что ваш объект будет просто двойным в штучной упаковке, попробуйте простейший вариант (a) просто удалить его.
Возможно, что-то в этом направлении будет работать для вас, если вы действительно не знаете, что будет?
double d = (o is double) ? (double)o
: (o is IConvertible) ? (o as IConvertible).ToDouble(null)
: double.Parse(o.ToString());
(обратите внимание: это не будет работать, если o содержит что-то, что реализует IConvertible, но не может быть преобразовано в double, или если его строковое представление не может быть проанализировано как double)
Я ничего не говорил об относительных скоростях, но я был бы поражен, если бы распаковка была существенно быстрее, чем преобразование в строку, а затем синтаксический анализ (если оптимизатор не сумасшедший умный).
Быстрый тест в LINQPad с использованием секундомера .NET предлагает большую разницу.
IEnumerable<object> myData = new List<object>() { "53.2", 53.2M, 53.2D };
const int iterations = 10000000;
var sw = new Stopwatch();
var results = new List<string>();
foreach (var o in myData)
{
sw.Reset();
sw.Start();
for (var i=0; i < iterations; i++)
{
double d = (o is double) ? (double)o
: (o is IConvertible) ? (o as IConvertible).ToDouble(null)
: double.Parse(o.ToString());
}
sw.Stop();
results.Add($"{o.GetType()}: {iterations} iterations took {sw.ElapsedMilliseconds}ms");
}
results.Dump();
на моем ПК дает следующие результаты
System.String: 10000000 iterations took 1329ms
System.Decimal: 10000000 iterations took 402ms
System.Double: 10000000 iterations took 38ms