0

当我注意到这个奇怪的问题时,我正在分析我的类库并优化事物:

有一个基类,我还有其他类,从它派生而来。基类具有公共属性。我正在执行涉及代码中其他地方的此属性的 Linq 查询。

现在,进行 100,000 次迭代(甚至不是一百万次),我可以看到,如果我还向派生类添加一个具有相同名称的属性(intellisense 用工具提示“此属性隐藏继承的成员”突出显示它),因此基本上是一个 '快捷方式'(但也是属性的重复) - 代码运行速度明显更快......对我来说是 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是 aList<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

When you're hiding the property you're making it a non-virtual call, rather than a virtual call to a member. Virtual dispatch does have a certain cost associated with it, which is why you're able to declare non-virtual properties/methods.

That said, in most applications the costs associated with a virtual method/property are not a problem at all. There is a difference, yes, but it's not much at all in the context of most programs.

于 2013-10-08T15:49:40.997 回答
3

If you examine the generated IL, when your code is using the derived class' local property, it's generating a call rather than a callvirt, which is just plain cheaper.

This seems like a premature optimization unless you're in a time critical loop.

Worrying about the difference between call and callvirt performance when building iteration using linq seems.... particularly premature.

于 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您遇到的问题与or无关callvirt。可能区别在于代码,您没有向我们展示。

于 2013-10-08T16:27:25.110 回答