1. ホーム
  2. c#

デリゲートとメソッドの呼び出しのパフォーマンス

2023-12-04 09:07:58

質問

この質問に続く C#を使用してパラメータとしてメソッドを渡す という質問と私の個人的な経験に従って、C#でメソッドを呼び出す場合と比較してデリゲートを呼び出す場合のパフォーマンスについてもう少し知りたいと思います。

デリゲートは非常に便利ですが、デリゲートを介して多くのコールバックを行うアプリがあり、これをコールバック インターフェイスを使用するように書き直したところ、1 桁の速度向上が得られました。 これは .NET 2.0 でのことなので、3 と 4 でどのように変わったかはわかりません。

デリゲートへの呼び出しはコンパイラ/CLRで内部的にどのように処理され、これがメソッド呼び出しのパフォーマンスにどのように影響するのでしょうか。


EDIT - デリゲートとコールバックインターフェースの意味を明確にするために。

非同期呼び出しのために、私のクラスはOnCompleteイベントと、呼び出し側がサブスクライブできる関連デリゲートを提供することができます。

代わりに、呼び出し側が実装するOnCompleteメソッドを持つICallbackインターフェースを作成し、完了時にそのメソッドを呼び出すクラスに自分自身を登録することができます(つまり、Javaがこれらのことを処理する方法)。

どのように解決するのですか?

そのような効果は見たことがありません。確かに、それがボトルネックになっていることには遭遇したことがありません。

これは非常に大雑把なベンチマークですが、(私の環境では) デリゲートが実際に より速い です。

using System;
using System.Diagnostics;

interface IFoo
{
    int Foo(int x);
}

class Program : IFoo
{
    const int Iterations = 1000000000;

    public int Foo(int x)
    {
        return x * 3;
    }

    static void Main(string[] args)
    {
        int x = 3;
        IFoo ifoo = new Program();
        Func<int, int> del = ifoo.Foo;
        // Make sure everything's JITted:
        ifoo.Foo(3);
        del(3);

        Stopwatch sw = Stopwatch.StartNew();        
        for (int i = 0; i < Iterations; i++)
        {
            x = ifoo.Foo(x);
        }
        sw.Stop();
        Console.WriteLine("Interface: {0}", sw.ElapsedMilliseconds);

        x = 3;
        sw = Stopwatch.StartNew();        
        for (int i = 0; i < Iterations; i++)
        {
            x = del(x);
        }
        sw.Stop();
        Console.WriteLine("Delegate: {0}", sw.ElapsedMilliseconds);
    }
}

結果(.NET 3.5; .NET 4.0b2もほぼ同じです)。

Interface: 5068
Delegate: 4404

さて、私は特に信じてはいませんが、それはデリゲートが 本当に しかし、それは、デリゲートが桁違いに遅いわけではないと確信させてくれます。さらに、これはデリゲート/インターフェースのメソッドの中ではほとんど何もしていません。呼び出しごとの作業が多くなればなるほど、呼び出しのコストは明らかに少なくなっていきます。

注意すべき点は、単一のインターフェイスインスタンスしか使用しないような場合に、新しいデリゲートを何度も作成していないことです。これは ガベージコレクションを引き起こすことになり、問題が発生します。ループ内でインスタンスメソッドをデリゲートとして使用する場合、ループ外でデリゲート変数を宣言し、単一のデリゲートインスタンスを作成し、それを再利用することがより効果的であることがわかります。例えば

Func<int, int> del = myInstance.MyMethod;
for (int i = 0; i < 100000; i++)
{
    MethodTakingFunc(del);
}

の方が効率的です。

for (int i = 0; i < 100000; i++)
{
    MethodTakingFunc(myInstance.MyMethod);
}

これが、あなたが見ていた問題だったのでしょうか?