相关疑难解决方法(0)

在C#中对小代码示例进行基准测试,是否可以改进此实现?

经常这样我发现自己对小块代码进行基准测试,看看哪个实现最快.

我经常看到基准测试代码没有考虑到jitting或垃圾收集器的评论.

我有以下简单的基准测试功能,我已经慢慢演变了:

  static void Profile(string description, int iterations, Action func) {
        // warm up 
        func();
        // clean up
        GC.Collect();

        var watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < iterations; i++) {
            func();
        }
        watch.Stop();
        Console.Write(description);
        Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
    }
Run Code Online (Sandbox Code Playgroud)

用法:

Profile("a descriptions", how_many_iterations_to_run, () =>
{
   // ... code being profiled
});
Run Code Online (Sandbox Code Playgroud)

这个实现是否有任何缺陷?是否足以表明实现X比Z迭代实现Y更快?您能想出任何可以改善这种情况的方法吗?

编辑 很明显,基于时间的方法(与迭代相反)是首选,是否有人有任何实施时间检查不会影响性能?

.net c# performance profiling

104
推荐指数
6
解决办法
2万
查看次数

我为什么要在这里锁?

请参阅以下并发性能分析,该分析表示并行foreach所完成的工作:

在此输入图像描述

在循环内部,每个线程从DB读取数据并对其进行处理.线程之间没有锁,因为每个处理不同的数据.

由于未知原因,看起来foreach的所有线程中都存在周期性锁定(请参阅黑色垂直矩形).如果您看到所选的锁定段(深红色段),您将看到堆栈显示在StockModel.Quotation构造函数处锁定的线程.那里的代码只构造了两个空列表!

我在某处读过这可能是由GC引起的所以我已经将垃圾收集更改为在服务器模式下运行:

<runtime>
    <gcServer enabled="true"/>
</runtime>
Run Code Online (Sandbox Code Playgroud)

我得到了一个小的改进(大约10% - 快15%),但我仍然有垂直锁定.

我还在所有数据库查询中添加了WITH(NOLOCK),因为我只读数据而没有任何区别.

有什么暗示这里发生的事情?

分析完成的计算机有8个核心.

编辑:启用Microsoft Symbol服务器后,所有线程都在wait_gor_gc_done或WaitUntilGCComplete等调用上被阻止.我认为启用GCServer我为每个线程都有一个GC,所以我会避免"垂直"锁定,但似乎并非如此.我错了吗?

第二个问题:由于机器没有内存压力(使用8个演出中的5个)有没有办法延迟GC执行或暂停它直到并行foreach结束(或将其配置为不经常发射)?

.net c# performance multithreading locking

19
推荐指数
1
解决办法
793
查看次数

在一段代码中将GC置于保持状态

有没有办法让GC完全保留一段代码?我在其他类似问题中找到的唯一的事情是GC.TryStartNoGCRegion它只限于你指定的内存量本身仅限于短暂段的大小.

有没有办法完全绕过它并告诉.NET "分配你需要的任何东西,不做GC期间"或增加段的大小?从我发现它在许多核心服务器上最多1GB,这比我需要分配的少,但我不希望GC发生(我有多达TB的空闲RAM,并且有数千个GC峰值在那一节中,我非常乐意将这些内容换成RAM使用量的10倍甚至100倍.

编辑:

现在有了赏金,我认为如果我指定用例就更容易了.我正在使用LINQ to XML加载和解析一个非常大的XML文件(现在1GB,很快就会12GB)到内存中的对象.我不是在寻找替代方案.我正在创建数以百万计的数百万个小对象,XElements并且GC正试图不间断地收集,而我很高兴保持所有RAM用完.我有100英镑的内存,一旦它达到4GB使用,GC开始收集不间断,这是非常友好的内存,但性能不友好.我不关心记忆但我关心表现.我想采取相反的权衡.

虽然我不能在这里发布实际代码是一些非常接近最终代码的示例代码,可以帮助那些要求更多信息的人:

var items = XElement.Load("myfile.xml")
.Element("a")
.Elements("b") // There are about 2 to 5 million instances of "b"
.Select(pt => new
{
    aa = pt.Element("aa"),
    ab = pt.Element("ab"),
    ac = pt.Element("ac"),
    ad = pt.Element("ad"),
    ae = pt.Element("ae")
})
.Select(pt => new 
{
    aa = new
    {
        aaa = double.Parse(pt.aa.Attribute("aaa").Value),
        aab = double.Parse(pt.aa.Attribute("aab").Value),
        aac = double.Parse(pt.aa.Attribute("aac").Value),
        aad = double.Parse(pt.aa.Attribute("aad").Value),
        aae = double.Parse(pt.aa.Attribute("aae").Value)
    },
    ab = new
    {
        aba = …
Run Code Online (Sandbox Code Playgroud)

.net c# garbage-collection .net-4.6

17
推荐指数
1
解决办法
495
查看次数

我可以告诉.NET GC单独保留一些线程吗?

我正在研究将相对较小的服务从C++重写为C#的可能性.该服务有两个主要功能:

  1. 偶尔执行HTTP请求.它们涉及几个高级任务,如JSON编码/解码,base64编码/解码和HTTP请求本身,对于这些任务,C++并不令人敬畏;
  2. 执行许多具有硬截止期限的实时,与音频相关的任务,C#并不令人敬畏.

实时任务由一个单独的库处理,该库执行自己的线程,并且根本不与服务的其余部分进行交互.服务的其余部分每5分钟左右从HTTP请求中获取一些数据.

问题是,由于实时部分有很长的期限,我无法真正容忍库的线程上的GC暂停.在我自己的代码方面,GC应该有足够的时间在Web请求之间运行,但我不能容忍它在我尝试将数据提供给库时启动.

发现我可以创建一个垃圾收集器不会开始使用的关键部分GC.TryStartNoGCRegion(),这解决了一半的问题.

但是,我仍然不知道是否有办法告诉.NET GC单独留下不运行托管代码的特定线程.那可能吗?

.net c# multithreading garbage-collection

16
推荐指数
1
解决办法
842
查看次数

抑制C#垃圾回收

我的应用程序分配了大量的内存(数百万个小对象,总计几千兆字节)并且长时间保留它.

  1. 是.NET浪费时间检查所有这些数据来做GC吗?
  2. Gen 2 GC多久出现一次(检查所有对象的那个)?
  3. 有没有办法降低它的频率或暂时抑制它的发生?
  4. 我确切地知道当我准备好收集大量内存时,有什么方法可以优化它吗?我目前正在调用GC.Collect(); GC.WaitForPendingFinalizers(); 那时候.

更新:Perf计数器"GC中的%时间"显示平均值为10.6%.

c# garbage-collection

11
推荐指数
4
解决办法
2万
查看次数

是否可以停止.NET垃圾回收?

程序员是否有可能以C#编程语言以编程方式启动/停止垃圾收集?例如,用于性能优化等.

.net c# garbage-collection

11
推荐指数
3
解决办法
5359
查看次数

C#性能分析器显示了长时间的暂停,无法从提供的数据中确定它是什么

渲染框架时,我的C#应用​​程序中出现了意外的峰值。我在探查器中进行了遍历,发现以下内容:

  • 当vsync打开时,该程序似乎在交换缓冲区时将控制权交给了OS(或执行了某些操作)...并且在某种程度上保持了渲染的一致性。这很好,因为它使帧平滑且没有怪异的微笔画。下图显示了被认为是完美的流体。这就是我们的情况,“它可以流畅地渲染而不会出现卡顿现象,从而完美地工作”。

VSync影​​像

  • 当vsync关闭并且程序不执行任何操作,只是尽可能快地进行写入而没有任何中断时,即使每秒的帧数更高,似乎仍然存在这些较大的空白,因此什么也不做。我很确定这是我在游戏中注意到的微结点,因为它会导致FPS急剧下降到60fps以下。下图显示了出问题的地方:

微口吃

在上面,增量时间为21毫秒(每秒60帧超过16.6毫秒),并在游戏中造成明显的停顿。甚至比这更糟,因为它仅在它之后才开始渲染(如您之后所看到的实心矩形),所以这21ms是一个谎言,根据该图,它更像是40ms,这太可怕了。

由于我不了解的原因,当打开vsync时,这种巨大差距永远不会发生。对于不熟悉vsync和游戏的用户,您不能在第一人称射击游戏中使用vsync,因为由于它的工作原理,它会削弱输入处理,因此我无法使用vsync,并且必须研究非vsync版本。我需要找到为什么非vsync版本具有这些主要停滞点的原因。

我的问题是,如何知道您从上面看到的图像中导致此延迟的原因?

性能分析器表示,这里有大量等待时间,超过80%的等待时间需要20%的CPU使用率(相比之下,准备渲染数据时需要100%的CPU使用率)。

探查器还显示该循环中没有运行任何渲染代码...这很奇怪,因为渲染器几乎完全控制了性能,因此,如果每秒不对帧进行任何上限,则应该使用纯蓝色淹没整个图形长方形。

问题在于,探查器显示的代码只是调用我在上面选择的区域中的轮询输入和dll:

DLL调用

请注意,的另一个调用DispatchRenderFrame是进行OpenGL调用,当我完全删除它时,它对程序没有影响,因此您可以忽略它。这可能意味着下面看到的用户输入也不会影响微笔画问题...但是我无法删除它,因为它是我用于窗口管理(OpenTK)的库的一部分。

我不确定CLR Worker线程是什么或正在做什么。它也发生在vsync一个(希望平滑分析一个)中,因此即使我不知道它是否是罪魁祸首,我猜也可能不是,但我不确定,因为它显示在“所需的vsync示例”也位于同一常规位置。

是否发生了一些中断,操作系统正在接管但未恢复我的线程,因为它被归类为CPU吞噬?只是一个想法...但是在示例中再次显示了蓝色条,因此我认为该Main线程实际上并未在我突出显示的内容中处于睡眠状态,并且实际上正在运行?

如您所见,我不确定突出显示的时间段是在告诉我什么,这是我需要帮助的地方。我不知道为什么此时代码中的等待百分比如此之高,或者从这里何处去进一步诊断问题。

编辑:我对Stopwatch类进行了一些粗略的剖析,以查看导致尖峰的位置,并且尖峰来自计算部分。OpenGL调用均不会导致任何延迟。它纯粹是在执行数学运算和访问数据的功能的函数内部发生的,或者写入预先分配的结构数组中。


额外说明:

这也让我好奇是否有办法让我强制使用,以使C#虚拟机获得尽可能多的CPU使用率?

没有产生垃圾。这不是 GC问题。我不能在申请期间运行GC,所以不会产生垃圾。渲染函数中的所有内容都是堆栈上的所有结构,唯一一次进入堆管理对象的是池化数组,其大小足以容纳所有渲染数据。它在探查器中出现的唯一时间是在开始和结束时,但是在渲染阶段不会运行。

c# performance opentk

11
推荐指数
1
解决办法
412
查看次数

在for循环中声明的变量是否会影响循环的性能?

我已经完成了我的作业,并且一再保证,无论是在for循环内部还是外部声明变量,它在性能上都没有区别,它实际上编译为完全相同的MSIL.但是我一直在弄乱它,并发现在循环中移动变量声明确实会导致相当大的一致性能增益.

我编写了一个小型控制台测试类来测量这种效果.我初始化一个静态double[]数组项,两个方法对它执行循环操作,将结果写入静态double[]数组缓冲区.最初,我的方法是那些我注意到差异的方法,即复数的大小计算.对于长度为1000000 的项目数组运行这些100次,我得到的变量(6个double变量)在循环内的运行时间一直较低:例如,32,83±0,64 ms v 43,44±使用Intel Core 2 Duo @ 2.66 GHz的老式配置为0.45 ms.我尝试以不同的顺序执行它们,但它没有影响结果.

然后我意识到计算复数的大小远非最小的工作示例,并测试了两个更简单的方法:

    static void Square1()
    {
        double x;

        for (int i = 0; i < buffer.Length; i++) {
            x = items[i];
            buffer[i] = x * x;
        }
    }


    static void Square2()
    {
        for (int i = 0; i < buffer.Length; i++) {
            double x;
            x = items[i];
            buffer[i] = x * x;
        }
    }
Run Code Online (Sandbox Code Playgroud)

有了这些,结果就出现了另一种方式:在循环外声明变量似乎更有利:对于Square1() …

c# for-loop variable-declaration

10
推荐指数
1
解决办法
715
查看次数

如何知道两个检查点之间是否触发了gen完全垃圾收集?

我正在分析代码的关键部分的时间花费.

//Take a timestamp before
var before = DateTime.UtcNow;

DoCriticalMethod();

//Take a timestamp after
var after = DateTime.UtcNow;
Run Code Online (Sandbox Code Playgroud)

有时我的某些价值明显高于其他价值.

我怀疑它是垃圾收集,所以我想将高值与在进程中发生垃圾收集的事实相关联.

到目前为止我试过这个:我之前拿了一个柜台:

//Take the number before
int gc2CountBefore = GC.CollectionCount(2);

...

//Take the number after
bool hasgc2occured = (GC.CollectionCount(2) - gc2CountBefore) != 0;
Run Code Online (Sandbox Code Playgroud)

我这样做的好吗?

c# garbage-collection

7
推荐指数
1
解决办法
611
查看次数

垃圾回收会影响性能吗?

我只是在阅读几种编程语言的性能,我注意到垃圾收集经常被提及。垃圾收集是否会影响语言的性能?如果是,如何?

performance garbage-collection

7
推荐指数
1
解决办法
3677
查看次数

如何避免对象分配?如何重用对象而不是分配它们?

我的金融软件不断处理几乎相同的对象.例如我在线有这样的数据:

HP 100 1
HP 100 2
HP 100.1 1
etc.
Run Code Online (Sandbox Code Playgroud)

我每秒钟大约有1000次更新.

每个更新都存储在对象中 - 但我不想动态分配这些对象以改善延迟.我只在很短的时间内使用物品 - 我收听它们,申请和免费.一旦对象空闲,它实际上可以重用于另一个数据包.

所以我需要一些存储(可能是环形缓冲区),它可以分配一次所需数量的对象,它们可以"获取"并"释放"它们.在c#中这样做的最佳方法是什么?

每个对象都有id,我id's按顺序分配并释放它们sequentially.例如我收到的ID 1 23,然后我自由1,2,3.所以任何FIFO集合都可以工作,但我正在寻找一些涵盖所需功能的库类.

即我需要FIFO集合,它不分配对象,但重用它们并允许重新配置它们.

UPD

我添加了我想要的实现.这不是经过测试的代码,可能有错误.想法很简单.作家应该调用Obtain Commit方法.读者应该调用TryGet方法.读者和编写者可以从不同的线程访问此结构:

public sealed class ArrayPool<T> where T : class
{
    readonly T[] array;
    private readonly uint MASK;

    private volatile uint curWriteNum;
    private volatile uint curReadNum;

    public ArrayPool(uint length = 1024) // length must be power of 2 …
Run Code Online (Sandbox Code Playgroud)

c# memory-management

1
推荐指数
1
解决办法
2373
查看次数