首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何解释BenchmarkDotNet和dotMemory的结果?

如何解释BenchmarkDotNet和dotMemory的结果?
EN

Stack Overflow用户
提问于 2018-09-07 21:33:56
回答 3查看 2K关注 0票数 8

因此,在我的Main()方法中有一段代码

代码语言:javascript
运行
复制
for (int x = 0; x < 100; x++) // to mimic BenchmarkDotnet runs
   for (int y = 0; y < 10000; y++)
     LogicUnderTest();

接下来,我在测试中使用了以下类

代码语言:javascript
运行
复制
[MemoryDiagnoser, ShortRunJob]
public class TestBenchmark
{
    [Benchmark]
    public void Test_1()
    {
        for (int i = 0; i < 10000; i++)
            LogicUnderTest();
    }
}

dotMemory下运行Main()大约6分钟后,我收到以下结果

这款应用程序从10Mb开始,一直到14Mb

但是当我运行BenchmarkDotnet测试时,我得到的结果是

我看到我已经分配了2.6GB。什么?这看起来一点也不好。而且,我看不到Gen1Gen2列。这是否意味着代码没有在它们中分配任何内容,所以没有显示任何内容?

我如何解释结果呢?在DotMemory中看起来完全没问题,但在BenchmarkDotNet中就不行了。我是BenchmarkDotnet的新手,对任何关于结果的信息都很有帮助。

PS。LogicUnderTest()广泛地使用字符串。

PSS。粗略地说,LogicUnderTest是这样实现的

代码语言:javascript
运行
复制
void LogicUnderTest()
{
    var dict = new Dictionary<int, string>();
    for (int j = 0; j < 1250; j++)
        dict.Add(j, $"index_{j}");
    string.Join(",", dict.Values);
}
EN

回答 3

Stack Overflow用户

发布于 2018-09-10 18:15:38

我是MemoryDiagnoser的作者,我也在我的blog上提供了你的问题的答案。我将在这里复制它:

如何阅读结果

代码语言:javascript
运行
复制
|     Method |  Gen 0 | Allocated |
|----------- |------- |---------- |
|          A |      - |       0 B |
|          B |      1 |     496 B |

已分配的

  • 包含已分配的托管内存的大小。堆栈分配/本机堆分配不包括在内。对于每个单个调用, Gen X列包含每个1000个操作的Gen X集合数量。如果该值等于1,则意味着在生成X时,GC每1000次基准测试调用收集一次内存。BenchmarkDotNet在运行基准测试时使用了一些启发式方法,因此对于不同的运行,调用次数可能会有所不同。缩放使得Gen列中的结果为comparable.
  • -意味着没有垃圾收集是因为performed.
  • If Gen X列不存在,然后它意味着没有为生成X执行垃圾收集。如果没有一个基准测试导致GC,则Gen列不存在。

在阅读结果时,请记住:

  • 1 kB =1024字节
  • 每个引用类型实例都有两个额外的字段:对象头和方法表指针。这就是为什么每次对象分配的结果总是包含2倍的指针大小。有关额外开销的更多详细信息,请阅读Konrad Kokosa的这篇很棒的博客文章How does Object.GetType() really work?
  • CLR做了一些调整。如果您尝试分配new byte[7]数组,它将分配byte[8]数组。
票数 13
EN

Stack Overflow用户

发布于 2018-09-10 17:18:38

在dotMemory中,BenchmarkDotNet向您显示的内容称为“内存流量”。在启用了"Start collecting allocation data immediately“的dotMemory下运行你的应用。在分析会话结束时获取内存快照,然后打开"Memory Traffic“视图。您将看到在分析会话期间分配和收集的所有对象。

你的问题是关于内存瓶颈的,因为所有分配的对象都被收集起来了,内存消耗不会增长,而且你在dotMemory中看不到任何问题。

但是每6秒3 GC的流量是相当大的,可能会对性能产生影响,请使用dotTrace (在时间线模式下)查看这6秒中的哪一部分花在GC上。

票数 2
EN

Stack Overflow用户

发布于 2018-09-08 06:44:04

好的,让我们来经历一次循环迭代:

,,

  • ,你将至少分配1250个整数,所以我们称它为5000字节或5K。,
  • ,你将创建一个字典,包含相同的整数和1250个字符串,平均长度为8个字符,所以我们称它为20000字节或20K。加上Dictionary本身的开销。
  • 然后string.Join将使用StringBuilder -因此这是最小的额外20K (可能更多,因为数组是动态调整大小的)。然后将在StrinBuilder上调用ToString (因此将再调用20K)。

5K + 20K + 20K + 20K = 65K。

2.86/ 10,000 = 0.286MB =约286k。

所以,所有这些听起来都是对的。65K是RAM使用率的绝对最小值。考虑到生成字典值时的字符串连接开销,使用Dictionary的开销(额外的数组,额外的int副本等)和StringBuilder的开销(由于字符串的长度,它可能会多次分配大型数组),你可以很容易地从65 -> 286得到。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52223682

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档