IntPtr 转换与新转换

发布于 2024-07-29 19:19:06 字数 255 浏览 2 评论 0原文

我只是在看一个示例,在其中我看到了代码

return new IntPtr(handle);

在浏览我们的代码之后,我发现我们已经使用了类似的模式,但在我们的代码中我们几乎有相同的东西:

return (IntPtr)handle;

这两者之间有区别吗需要? 第二个是否会以任何方式“更好”,因为它不分配新的内存,或者强制转换只是在下面隐藏相同的构造函数?

I was just looking at an example, and in it I saw the code

return new IntPtr(handle);

After poking around our code, I found that we have already used a similar pattern, but in our code we had almost the same thing:

return (IntPtr)handle;

Is there a difference between those two takes? Will the second one be "better" in any way, since it doesn't allocate new memory, or is the cast just hiding the same constructor underneath?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

掩耳倾听 2024-08-05 19:19:06

在你的例子中,我猜测句柄是一个整数值? IntPtr 声明了从 Int32 (int) 和 Int64 (long) 的显式转换,它只调用相同的构造函数:

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

因此,除了可能的可读性问题之外,实际上没有任何区别。

In your examples, I'm guessing handle is an integer value? IntPtr declares an explicit conversion from Int32 (int) and Int64 (long) which simply calls the same constructor:

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

So there is effectively no difference other than possible readability concerns.

生活了然无味 2024-08-05 19:19:06

Reflector 表示强制类型转换正在幕后调用构造函数:

[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);
    }

}

Reflector says that the cast is calling the constructor under the hood anyway:

[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);
    }

}
隔岸观火 2024-08-05 19:19:06

所以这个帖子都是空谈,没有数字,所以让我们谈谈指标。 我使用 Visual Studio 2010 运行了一些测试代码来获取一些性能指标,并

通过计算任一方法在“调试”然后“发布”模式(未优化然后优化)下运行 10 次测试(每次 1000 万次迭代)的平均时间来获取这些指标:(

调试)
投射方法:~32 ms
分配方法:~26 ms

(释放)
投射方法:~20 ms
分配方法:~22 ms

同样有趣的是,将这些指标与仅使用 gcnew 的托管 C++ 的类似代码进行比较,结果有很大不同。

再次进行相同的设置。 除了比较转换方法:“IntPtr^ptr = (IntPtr) i;” vs 分配方法:“IntPtr^ptr = (IntPtr) i;”。

(调试)
投射方法:~91ms
分配方法:~127ms

(释放)
投射方法:~22ms
分配方法:~124ms

现在,如果您百思不得其解,为什么 C# 比托管 C++ 快这么多,答案是事实并非如此。 使用 IntPtr 最有效的方法是作为值类型而不是对值类型的引用。 例如像这样“IntPtr ptr = (IntPtr) i;”。 这将为您提供约 24 毫秒(更多调试)或(约 22 发布模式)。 看看上面编译器是如何优化它以获得 22 毫秒而不是 90 毫秒的。

C# 中的结论,除非您正在查看非常非常严格的代码,否则这并不重要。 我认为使用 Release 中的代码,它实际上是在优化强制转换,因为注释掉强制转换会产生相同的约 22 毫秒。 但在大多数情况下,编译器会在 C# 中很好地支持你,至少 VS 2010 是这样。 但是,在托管 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);
    }

So this thread is all talk and no numbers so lets talk metrics. I ran some test code to get some performance metrics using Visual Studio 2010 and

I got these metrics by calculating the average time of either method over 10 test runs with 10 million iterations each in Debug then Release mode (non optimized then optimized):

(Debug)
Casting Method: ~32 ms
Allocation Method: ~26 ms

(Release)
Casting Method: ~20 ms
Allocation Method: ~22 ms

What is also interesting is to compare these metrics to similar code with managed C++ only using gcnew and the results are much different.

Same setup again. Except comparing the casting method: "IntPtr^ ptr = (IntPtr) i;" vs the allocation method: "IntPtr^ ptr = (IntPtr) i;".

(Debug)
Casting Method: ~91ms
Allocation Method: ~127ms

(Release)
Casting Method: ~22ms
Allocation Method: ~124ms

Now if you are scratching your head saying well why is C# so much faster than managed C++ and the answer is it isn't. The most efficient way to use IntPtr is as value type not a reference to a value type. For instance like so "IntPtr ptr = (IntPtr) i;". This would give you ~24ms (Debug more) or (~22 Release mode). See how it was optimized above by the compiler to get the 22ms rather than the 90ms.

Conclusion in C#, unless you are looking at REALLY REALLY tight code it doesn't matter. I think with my code in Release it was actually optimizing the cast right out because commenting out the cast gave the same ~22ms. But for the most part compiler has your back on this one in C# well at least VS 2010 does. However, in Managed C++/CLI if you are looking at code with even minimal performance constraints then watch out. The compiler will not automatically optimize gcnew allocations to the casting approach and it's almost 6 times faster... I actually ran into this particular problem in C++/CLI which is what led me to post on this thread when dealing with some real-time audio processing. My code (C#): (My managed C++ code was very similar except I had to write Average() myself and used console to output rather than message boxes).

    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);
    }
一曲琵琶半遮面シ 2024-08-05 19:19:06

由于 IntPtr 是值类型,因此使用 new 不会分配任何内存。

从技术上讲,这些调用仍然编译为不同的 IL - 一个实际上调用构造函数,另一个调用显式转换运算符。 然而,我不确定 JIT 通过后这两者之间是否有任何实际区别 - 很可能没有(尽管我怀疑您在实践中会注意到这两种方式,这是一种 femto 优化)。

无论如何,强制转换比使用构造函数更惯用,因此我建议仅出于这个原因就使用它。

Since IntPtr is a value type, using new does not allocate any memory.

Technically, the calls still compile down to different IL - one actually calls the constructor, another calls the explicit conversion operator. I'm not sure if there's any actual difference between those two after a JIT pass, however - most likely none (though I doubt you'd notice either way in practice, this being a femtooptimization).

In any case, cast is more idiomatic than using a constructor, so I'd suggest going with it for that reason alone.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文