我已经实现了一个基本的二进制堆。我想看看它的表现如何,所以我写了一本快速手册' profiler':
public class MProfile : IDisposable
{
private static Dictionary<string, ProfilerEntry> _funcs = new Dictionary<string, ProfilerEntry>();
private Stopwatch _stopwatch;
private ProfilerEntry _entry;
public MProfile(string funcName)
{
if (!_funcs.ContainsKey(funcName))
{
_funcs.Add(funcName, new ProfilerEntry(funcName));
}
_entry = _funcs[funcName];
_stopwatch = Stopwatch.StartNew();
}
public void Dispose()
{
_stopwatch.Stop();
_entry.Record(_stopwatch.Elapsed.TotalMilliseconds);
}
}
这个想法是用using
将它包装在函数调用中,它会记录所花费的时间。 ProfileEntry类只是一些调用和总时间。通过将它们存储在名称中,我可以将它们添加起来。
现在,如果我把它包裹在我的整个测试中:
Random random = new Random();
int count = 20000;
using (MProfile profile = new MProfile("HeapTest"))
{
PriorityHeap<PretendPathNode> heap = new PriorityHeap<PretendPathNode>(count);
for (int i = 0; i < count; i++)
{
int next = random.Next(-1000, 1000);
heap.Insert(new PretendPathNode(next));
}
while (heap.Count() > 0)
{
heap.Pop();
}
}
它会告诉我这需要:40.6682ms
但是,如果我在Insert
和Pop
电话周围添加更多的探查器,即:
using (MProfile profile2 = new MProfile("Insert"))
{
heap.Insert(new PretendPathNode(next));
}
using (MProfile profile3 = new MProfile("Pop"))
{
heap.Pop();
}
现在所花费的总时间为:452毫秒,107毫秒来自插入,131毫秒来自Pop(注意:我已经在巨大的循环中运行这些测试并取平均值)。我认为我的额外分析&#39;代码显然会产生影响,但是如何将插入和弹出时间膨胀到原始执行时间以上?我认为他们完成一次性意味着 - 只有 - 内部执行时间会被记录下来,这仍然是完全一样的。额外创建一次性,查找字典中的func并处理插入/弹出调用的外部。
是否与JIT和编译/运行时优化等事情有关?扔掉那个一次性有效毁了吗?我想也许它与GC有关,但我尝试了一个不同的探测器(静态手动调用启动/停止),它有0个垃圾而且它是一样的......
编辑:我使用这个稍微更混乱的代码得到相同的时间,它缓存了MProfile对象和Stopwatch对象,因此创建/ GC的次数较少。
public class MProfile : IDisposable
{
private static Dictionary<string, ProfilerEntry> _funcs = new Dictionary<string, ProfilerEntry>();
private static Dictionary<string, Stopwatch> _stopwatches = new Dictionary<string, Stopwatch>();
private static Dictionary<string, MProfile> _profiles = new Dictionary<string, MProfile>();
private ProfilerEntry _entry;
private string _name;
public MProfile(string funcName)
{
_name = funcName;
_entry = new ProfilerEntry(funcName);
_funcs.Add(funcName, _entry);
}
public static MProfile GetProfiler(string funcName)
{
if (!_profiles.ContainsKey(funcName))
{
_profiles.Add(funcName, new MProfile(funcName));
_stopwatches.Add(funcName, new Stopwatch());
}
_stopwatches[funcName].Restart();
return _profiles[funcName];
}
public void Dispose()
{
_stopwatches[_name].Stop();
_entry.Record(_stopwatches[_name].Elapsed.TotalMilliseconds);
}
}
通过以下方式调用:
using (profile2 = MProfile.GetProfiler("Insert"))