[英]Eliminating visitor pattern in C#
我有看起来像这样的代码:
var visitor = new ImplementsVisitor();
for(int i = 0; i < array.Length; ++i)
arrray[i].Accept(visitor);
数组中的每个元素都实现IItem
接口,该接口具有Accept(IVisitor)
方法。 只是标准的访客模式。
在评估性能时,我得出的结论是,接口调用太慢,并且在此代码中,性能至关重要。 根据您的经验,消除任何虚拟或接口调用的最佳选择是什么? 是否检查具体类型的if语句? 每个带有开关/大小写的元素上的枚举(在这种情况下,代码的结构使得不需要强制转换)? 还有吗
PS我不能排序数组中的项目。 顺序很重要。 因此,我无法按具体类型对它们进行排序以帮助进行分支预测。
我创建了以下程序。 在我的笔记本电脑上,循环在8ms内运行了100万次(这是Release版本,Debug在11ms左右)。 进行虚拟调度并增加一个int大约需要0.000008ms。 您到底需要多快? 我怀疑您的性能测试或我的问题出了问题。 如果是我的,我将有兴趣听取改进建议。
通常,如果此级别的性能不够好,那么使用C#本身可能就是一个问题。 例如,其垃圾收集器习惯于在循环中间冻结线程。 如果在循环迭代上确实使用0.000008ms是个问题,我怀疑汇编语言或C是更好的选择。
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
const int count = 1000000;
IList<IItem> items = new List<IItem>(count);
for (int i = 0; i < count; i++)
{
var rnd = new Random();
if (rnd.NextDouble() > 0.5)
{
items.Add(new ClassA());
}
else
{
items.Add(new ClassB());
}
}
var visitor = new MyVisitor();
Stopwatch s = Stopwatch.StartNew();
for (int i = 0; i < items.Count; i++)
{
items[i].Accept(visitor);
}
s.Stop();
Console.WriteLine("ExecTime = {0}, Per Cycle = {1}", s.ElapsedMilliseconds, (double)s.ElapsedMilliseconds / count);
visitor.Output();
}
interface IVisitor
{
void Process(ClassA item);
void Process(ClassB item);
}
interface IItem
{
void Accept(IVisitor visitor);
}
abstract class BaseVisitor : IVisitor
{
public virtual void Process(ClassA item)
{
}
public virtual void Process(ClassB item)
{
}
}
class ClassA : IItem
{
public void Accept(IVisitor visitor)
{
visitor.Process(this);
}
}
class ClassB : IItem
{
public void Accept(IVisitor visitor)
{
visitor.Process(this);
}
}
class MyVisitor : BaseVisitor
{
int a = 0;
int b = 0;
public override void Process(ClassA item)
{
a++;
}
public override void Process(ClassB item)
{
b++;
}
public void Output()
{
Console.WriteLine("a = {0}, b = {1}", a, b);
}
}
}
}
您在这里没有一个虚拟呼叫,只有两个,但是您只需要一个。 首先,您的数组可能通过IItem进行了虚拟调用-但是,如果这些都是相同的类型,并且您知道该类型(并且是密封的),则不需要进行虚拟调用。
然后,在访问的对象内,您需要执行访问者想要执行的任何操作。 这可能还会涉及虚拟呼叫。
使用类型化的IVisitor可能会做得更好:
interface IItem<TVisitor> : IItem
where TVisitor : IVisitor
{
void Accept(TVisitor visitor);
}
// Then
SpecialVisitor visitor = ImplementsSpecialVisitor();
foreach(var item in arrayOfSpecialItems){
item.Accept<SpecialVisitor>(visitor);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.