IntPtr бросок против нового - PullRequest
       22

IntPtr бросок против нового

3 голосов
/ 06 августа 2009

Я просто смотрел на пример, и в нем я видел код

return new IntPtr(handle);

После осмотра нашего кода я обнаружил, что мы уже использовали подобный шаблон, но в нашем коде мы имели почти то же самое:

return (IntPtr)handle;

Есть ли разница между этими двумя дублями? Будет ли второй «лучше» в любом случае, поскольку он не выделяет новую память, или приведение просто скрывает тот же самый конструктор под ним?

Ответы [ 4 ]

8 голосов
/ 06 августа 2009

В ваших примерах, я думаю, дескриптор является целочисленным значением? IntPtr объявляет явное преобразование из Int32 (int) и Int64 (long), которое просто вызывает один и тот же конструктор:

public static explicit operator IntPtr(int value)
{
    return new IntPtr(value);
}

Таким образом, фактически нет никакой разницы, кроме возможных проблем с читабельностью.

5 голосов
/ 06 августа 2009

Отражатель говорит, что актер в любом случае вызывает конструктор под капотом:

[Serializable, StructLayout(LayoutKind.Sequential), ComVisible(true)]
public struct IntPtr : ISerializable
{
    ...

    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    public static explicit operator IntPtr(int value)
    {
        return new IntPtr(value);
    }

}
3 голосов
/ 12 декабря 2010

Так что в этой теме все разговоры, а не цифры, поэтому давайте поговорим о метриках. Я запустил некоторый тестовый код, чтобы получить некоторые показатели производительности с помощью Visual Studio 2010 и

Я получил эти метрики, рассчитав среднее время каждого метода за 10 тестовых прогонов с 10 миллионами итераций каждая в режиме отладки, затем выпуска (не оптимизирован, а оптимизирован):

(Debug) Метод литья: ~ 32 мс Метод распределения: ~ 26 мс

(Release) Метод литья: ~ 20 мс Метод распределения: ~ 22 мс

Интересно также сравнить эти метрики с аналогичным кодом с управляемым C ++ только с использованием gcnew, и результаты сильно отличаются.

Та же настройка снова. За исключением сравнения метода приведения: "IntPtr ^ ptr = (IntPtr) i;" против метода выделения: "IntPtr ^ ptr = (IntPtr) i;".

(Debug) Метод литья: ~ 91мс Метод распределения: ~ 127 мс

(Release) Метод литья: ~ 22 мс Метод распределения: ~ 124 мс

Теперь, если вы почесываете голову, говоря, почему C # намного быстрее, чем управляемый C ++, и ответ - нет. Наиболее эффективным способом использования IntPtr является тип значения, а не ссылка на тип значения. Например, вот так: «IntPtr ptr = (IntPtr) i;». Это даст вам ~ 24 мс (больше отладки) или (~ 22 режима выпуска). Посмотрите, как это было оптимизировано выше компилятором, чтобы получить 22 мс, а не 90 мс.

Вывод в C #, если только вы не посмотрите на ДЕЙСТВИТЕЛЬНО ДЕЙСТВИТЕЛЬНО жесткий код, это не имеет значения. Я думаю, что с моим кодом в Релизе он фактически оптимизировал приведение, потому что комментирование приводило к тому же ~ 22 мс. Но по большей части компилятор поддерживает вас в C #, по крайней мере, в VS 2010. Однако в Managed C ++ / CLI, если вы смотрите на код с минимальными ограничениями производительности, будьте внимательны. Компилятор не будет автоматически оптимизировать распределение gcnew для подхода приведения, и это почти в 6 раз быстрее ... Я фактически столкнулся с этой конкретной проблемой в C ++ / CLI, которая и привела меня к публикации в этой теме при работе с аудио в реальном времени обработка. Мой код (C #): (мой управляемый код на C ++ был очень похож, за исключением того, что мне приходилось самостоятельно писать Average () и использовать для вывода консоль, а не окна сообщений).

    static void Main()
    {
        List<int> castTimings = new List<int>();
        List<int> allocTimings = new List<int>();

        for (int i = 0; i < TEST_RUNS; ++i)
        {
            castTimings.Add(RunCastMethod().Milliseconds);
            allocTimings.Add(RunAllocationMethod().Milliseconds);
        }

        MessageBox.Show(string.Format("Casting Method took: {0}ms", castTimings.Average() ));
        MessageBox.Show(string.Format("Allocation Method took: {0}ms", allocTimings.Average() ));
    }

    private static TimeSpan RunAllocationMethod() {
        DateTime start = DateTime.Now;

        for (int i = 0; i < TEST_ITERATIONS; ++i)
        {
            IntPtr ptr = new IntPtr(i);
        }

        return ( DateTime.Now - start );
    }

    private static TimeSpan RunCastMethod()
    {
        DateTime start = DateTime.Now;

        for (int i = 0; i < TEST_ITERATIONS; ++i)
        {
            IntPtr ptr = (IntPtr) i;
        }

        return (DateTime.Now - start);
    }
2 голосов
/ 06 августа 2009

Поскольку IntPtr является типом значения, использование new не выделяет никакой памяти.

Технически, вызовы по-прежнему компилируются в разные IL - один фактически вызывает конструктор, другой вызывает оператор явного преобразования. Однако я не уверен, есть ли какая-либо реальная разница между этими двумя после прохождения JIT - скорее всего, нет (хотя я сомневаюсь, что вы заметили бы в любом случае на практике, это фемтооптимизация).

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

...