0

この奇妙な問題に気付いたとき、私は自分のクラス ライブラリをプロファイリングして物事を最適化していました。

基本クラスがあり、そこから派生した他のクラスがあります。基本クラスにはパブリック プロパティがあります。コードの他の場所でこのプロパティを含む Linq クエリを実行しています。

現在、100,000 回の反復 (100 万回ではない) を実行すると、派生クラスに同じ名前のプロパティも追加するとわかります (インテリセンスは、「このプロパティは継承されたメンバーを非表示にします」というツールチップでそれを強調表示します)。したがって、基本的に ' Shortcut' (ただし、プロパティの重複) - コードは大幅に高速に実行されます... 私には 350 ミリ秒です。100,000 回以上の反復は非常に重要です。

なぜですか?:) 何ができる?


詳細:

基本クラス:

public abstract class ContentItem: IContent
{
    internal ContentItem() { }

    [DataMember]
    [IndexedField(true, false)]
    public string Guid { get; set; }

    [DataMember]
    [IndexedField(false, true)]
    public string Title { get; set; }
}

派生「POCO」:

[IndexedClass]
public class Channel : ContentItem, IContent
{
    [DataMember(IsRequired = false, EmitDefaultValue = false)]
    [ContentField]
    public string TitleShort { get; set; }
}

リポジトリ クラス (linq クエリを実行): (汎用リポジトリ)

public virtual T ByTitle(string title)
{
    return All.Find(item => item.Title == title);
}

どこAllに があり、List<Channel>2700 個のアイテムがあります。

テスト用のコード:

private static void test(Content.Repository<Content.Channel> channels)
        {
            int iterations = 100000;

            var watch = System.Diagnostics.Stopwatch.StartNew();

            for (int i = 0; i < iterations; i++)
            {
                var channel = channels.ByTitle("Unique-Title");
            }

            watch.Stop();

            Console.WriteLine("Done in {0} ms.", watch.ElapsedMilliseconds);
        }
4

3 に答える 3

3

プロパティを非表示にすると、メンバーへの仮想呼び出しではなく、非仮想呼び出しになります。仮想ディスパッチには特定のコストが関連付けられているため、非仮想プロパティ/メソッドを宣言できます。

とはいえ、ほとんどのアプリケーションでは、仮想メソッド/プロパティに関連するコストはまったく問題になりません。はい、違いはありますが、ほとんどのプログラムのコンテキストではそれほど違いはありません。

于 2013-10-08T15:49:40.997 に答える
3

生成された IL を調べると、コードが派生クラスのローカル プロパティを使用している場合、callではなく が生成さcallvirtれます。これは単純に安価です。

時間が重要なループでない限り、これは時期尚早の最適化のように思えます。

linq を使用してイテレーションを構築するときの と のパフォーマンスの違いを心配するのは、特に時期尚早callです。callvirt

于 2013-10-08T15:49:45.503 に答える
0

call と callvirt の違いは非常に小さいです。

コードを簡略化しました。実行して、どのような答えがあるか教えてください。両方のプロパティを使用することに違いはありません。

public abstract class ContentItem 
{
    public string Title { get; set; }
    public string AnotherTitle { get; set; }
}

public class Channel : ContentItem
{
    public string AnotherTitle { get; set; }
}

private static void Main(string[] args)
{
    var channels = new List<Channel>();
    for (int i = 0; i < 3000; i++)
    {
        channels.Add(new Channel(){Title = i.ToString(), AnotherTitle = i.ToString()});
    }
    int iterations = 100000;
    System.Diagnostics.Stopwatch watch;
    var difs = new List<int>();
    int rounds = 10;
    for (int k = 0; k < rounds ; k++)
    {
        watch = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            var channel = channels.Find(item => item.Title == "2345");
        }
        watch.Stop();
        long timerValue = watch.ElapsedMilliseconds;
        watch = System.Diagnostics.Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            var channel = channels.Find(item => item.AnotherTitle == "2345");
        }
        watch.Stop();
        difs.Add((int)(timerValue - watch.ElapsedMilliseconds));
    }

    Console.WriteLine("result middle dif " + difs.Sum()/rounds);
}

アップデート

callまた、この場合、IL にはメソッドがありません。両方Titleと次のようにAnotherTitle見える

IL_0008:  callvirt   instance string ConsoleApplication4.Program/ContentItem::get_Title()
IL_0016:  callvirt   instance string ConsoleApplication4.Program/Channel::get_AnotherTitle()

callあなたが抱えている問題は、またはとは何の関係もありませんcallvirt。おそらく違いはコードにありますが、それはあなたが私たちに示していないことです.

于 2013-10-08T16:27:25.110 に答える