[.NET学习笔记] - C++/CLI项目迁移至.NET5.0时Marshal::StructureToPtr的性能问题

背景

手头上有个C++/CLI项目,主要是用来封装C++的dll,方便.NET调用的。之前是在.NET FW 4.8 runtime上,最近想迁移至.NET Core runtime。当前的时间点有两个选择,一个是.NET Core 3.1,一个是.NET 5.0。一个是LTS版,一个是新鲜出炉版。没有多想,觉得.NET 5.0虽然很新,有很多坑不稳定,但不至于被我踩上,加上网上吹的那么多性能提升,首选了升级至.NET 5.0

C++/CLI项目的migrate

C++/CLI项目从.NET FW 迁移至.NET改动不大,操作如下:
在这里插入图片描述
在这里插入图片描述
主要就是选择新版的rumtime,由/clr更改为/clr:netcore。调整对应的.NET Traget Framework Versionv4.8 -> .NET Core 3.1 or .NET 5.0

分析

由于是个C++/CLI项目,对性能还是比较敏感的,虽然网上有很多性能测试的文章,但少有C++/CLI项目在不同runtime下的测试对比。
测试代码在C++/CLI项目中,运行耗时计算使用<chrono>

// 示例伪代码
#include <chrono>
auto tp1 = chrono::high_resolution_clock::now();
// code
auto tp2 = chrono::high_resolution_clock::now();
auto duration = chrono::duration_cast<chrono::microseconds>(tp2 - tp1).count();

C++11 chrono头文件提供了3个标准时钟可以用来计时:

  1. system_clock - 系统提供的实时时钟
  2. high_resolution_clock - 当前系统时钟周期最短的时钟
  3. steady_clock - 不会被调整的单调时钟

测出来的结果让我大跌眼镜。针对C++/CLI项目代码,.NET FW 4.8的性能大概是.NET 5.0的3-10倍(测得比较随意,都是些简单代码重复执行)。一开始我以为是迁移后的C++/CLI项目需要其他设置,所以花了一天时间各种配置调优,结果变化不大。
我尝试一行行代码做性能对比,发现性能差异主要提现在Marshal::StructureToPtrMarshal::PtrToStructure这两个方法上。
就在我心灰意冷,准备回到.NET FW 4.8怀抱的时候,我查到了这个。

Github dotnet 5.0 Marshal.PtrToStructure is slower 8x than netcore3.1 #45100
截止到2021-12-08,.NET6已经发布,这个issue还没有被解决。这个已经被推迟到.NET7的进度里了。而让人难过的是,.NET8才是LTS。

这个github的issue和的我情况差不多,虽然他并不是C++/CLI项目,但也能说明一定的问题。即.NET 5.0里的Marshal::PtrToStructure性能有问题,至少比.NET Core 3.1差了很多。
受到启发,我把C++/CLI项目runtime改成了.NET Core 3.1并与.NET FW 4.8做比较测试。测下来,基本上性能差不多,.NET Core 3.1.NET FW 4.8有略微的优势。至少这给了我信心,可以暂时先迁往3.1,等时机成熟,再前往LTS版的.NET 6.0

总结

本篇文章遇到的问题,严格意义上,不能算是C++/CLI项目的问题,但由于C++/CLI项目用到Marshal::StructureToPtrMarshal::PtrToStructure的场景比较多,所以受到了影响。但寻根溯源,这还是.NET 5.0的问题。毕竟是新版本,一不小心就踩坑翻车了。本文也是提醒大家,如果需要在.NET 5.0中使用Marshal::StructureToPtrMarshal::PtrToStructure等方法,务必小心谨慎。

更新

20230803

针对Marshal::PtrToStructure这个操作,主要用于将native类型转化为managed类型,循环操作10000次。

运行版本dll编译版本平均单次耗时
nfx 4.8nfx 4.8约1.28us-1.35us
netcore 3.1netcore 3.1约1.13us-1.162us
net 6.0netcore 3.1约4.50us-4.89us
net 7.0netcore 3.1约5.13us-7.27us
netcore 3.1net 6.0不支持
net 6.0net 6.0约4.58us-4.99us
net 7.0net 6.0约5.44us-5.86us
netcore 3.1net 7.0不支持
net 6.0net 7.0不支持
net 7.0net 7.0约5.41us-6.11us

由以上粗略的测试可知,Marshal::PtrToStructure的性能,在nfx4.8netcore3.1上基本一致。随着.NET版本提升,性能持续下降。在net7.0上降低比较显著。考虑到该版本为过渡版本,可以忽略。该问题在GitHub上的相关issue仍然没有解决。考虑到netnfx版本逐渐脱节,且C#新版本特性很吸引人。nfx4.8目前只支持到C#8.0net6.0支持最新的C#11
考虑迁移,暂且忍受这块的性能损耗。期待未来版本修复。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值