3

私はちょうど例を見ていました、そしてその中に私はコードを見ました

return new IntPtr(handle);

私たちのコードを調べてみると、すでに同様のパターンを使用していることがわかりましたが、私たちのコードにはほとんど同じことがありました。

return (IntPtr)handle;

この2つのテイクに違いはありますか?2番目のものは、新しいメモリを割り当てないため、何らかの方法で「より良い」ものになりますか、それともキャストが同じコンストラクターを下に隠しているだけですか?

4

4 に答える 4

8

あなたの例では、ハンドルは整数値だと思いますか? IntPtr は、単に同じコンストラクターを呼び出す Int32 (int) および Int64 (long) からの明示的な変換を宣言します。

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

したがって、可読性の懸念以外に事実上違いはありません。

于 2009-08-06T05:36:21.510 に答える
5

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

}
于 2009-08-06T05:37:02.900 に答える
4

したがって、このスレッドはすべてトークであり、数字はありません。メトリックをトークしましょう。Visual Studio 2010を使用してパフォーマンスメトリックを取得するために、いくつかのテストコードを実行しました。

これらのメトリックは、デバッグモードとリリースモード(最適化されていない状態と最適化された状態)でそれぞれ1,000万回の反復を行った10回のテスト実行でのいずれかの方法の平均時間を計算することで得られました。

(デバッグ)キャスト方法:〜32ミリ秒割り当て方法:〜26ミリ秒

(リリース)キャスト方法:〜20ms割り当て方法:〜22ms

また興味深いのは、これらのメトリックを、gcnewのみを使用するマネージC ++の同様のコードと比較することであり、結果は大きく異なります。

もう一度同じ設定。キャスト方法の比較を除いて: "IntPtr ^ ptr =(IntPtr)i;" vs割り当て方法: "IntPtr ^ ptr =(IntPtr)i;"。

(デバッグ)キャスト方法:〜91ms割り当て方法:〜127ms

(リリース)キャスティング方法:〜22ms割り当て方法:〜124ms

さて、頭を悩ませているのなら、なぜC#はマネージC ++よりもはるかに高速なのか、答えはそうではないということです。IntPtrを使用する最も効率的な方法は、値型への参照ではなく、値型として使用することです。たとえば、「IntPtr ptr =(IntPtr)i;」のようになります。これにより、〜24ms(さらにデバッグ)または(〜22リリースモード)が得られます。90msではなく22msを取得するために、コンパイラによって上記でどのように最適化されたかを確認してください。

C#での結論は、本当に本当にタイトなコードを見ているのでなければ、それは問題ではありません。リリースの私のコードでは、キャストをコメントアウトすると同じ〜22msが得られたため、実際にはキャストをすぐに最適化していたと思います。しかし、ほとんどの場合、コンパイラーはC#でこれに背を向けていますが、少なくともVS2010にはあります。ただし、マネージ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);
    }
于 2010-12-12T09:08:26.830 に答える
2

IntPtrは値型であるため、 usingはnewメモリを割り当てません。

技術的には、呼び出しは依然として異なる IL にコンパイルされます。1 つは実際にコンストラクターを呼び出し、もう 1 つは明示的な変換演算子を呼び出します。ただし、JIT パスの後でこれら 2 つに実際の違いがあるかどうかはわかりません。ほとんどの場合、違いはありません (ただし、実際にはどちらの方法でも気付くとは思いませんが、これはフェムト最適化です)。

いずれにせよ、キャストはコンストラクターを使用するよりも慣用的であるため、その理由だけで使用することをお勧めします。

于 2009-08-06T05:36:35.193 に答える