2018-12-25-C#-7.2-通过-in-和-readonly-struct-减少方法值复制提高性能
| title | author | date | CreateTime | categories |
|---|---|---|---|---|
|
C# 7.2 通过 in 和 readonly struct 减少方法值复制提高性能
|
lindexi
|
2018-12-25 9:24:6 +0800
|
2018-11-24 16:40:58 +0800
|
C#
|
在 C# 7.2 提供了一系列的方法用于方法参数传输的时候减少对结构体的复制从而可以高效使用内存同时提高性能
在开始阅读之前,希望读者对 C# 的值类型、引用类型有比较深刻的认知。
在 C# 中,如果对内存有严格的要求,同时需要减少 GC 的情况,推荐此时使用结构体。但是结构体有一个缺点在于,结构体在每次调用方法作为参数传递的时候都会新建一个副本,这对于性能要求特别高的情况是不适合的。
定义一个值类型
struct Int256
{
public Int256(long bits0, long bits1, long bits2, long bits3)
{
Bits0 = bits0;
Bits1 = bits1;
Bits2 = bits2;
Bits3 = bits3;
} public long Bits0 { set; get; }
public long Bits1 { get; }
public long Bits2 { get; }
public long Bits3 { get; }
}
此时通过一个简单的赋值就可以获取复制
Int256 f1 = new Int256(0, 1, 2, 1);
var f2 = f1;
f2.Bits0 = 2;
Console.WriteLine($"f1.bits0={f1.Bits0} f2.bits0={f2.Bits0}");
//f1.bits0=0 f2.bits0=2
在调用方法的时候也一样,传入参数就是复制一个新的值
static void Main(string[] args)
{
Int256 f1 = new Int256(0, 1, 2, 1);
Foo(f1);
Console.WriteLine($"{f1.Bits0}"); // 0
} private static void Foo(Int256 foo)
{
foo.Bits0 = 2;
}
对于很小的值类型,如果小于 IntPtr.Size 的传输,会比引用传递的复制速度快,但是对比比较大的值类型,如上面定义的,复制一次需要的时间会比较长
特别是存在很多次的值传递的时候,如下面的代码,会调用 1000 次的值传递。除了性能的问题,还存在堆栈的内存的问题
定义一个很大的值类型,里面包含 10000 个 double 看起来就很大
struct Double10000
{
public double Double0 { get; }
public double Double1 { get; }
public double Double2 { get; }
……
public double Double9999 { get; }
}
用递归的方式进行调用,运行的时候很快就可以看到堆栈都被申请的值传递使用,同时 CPU 的使用很高
static void Main(string[] args)
{
Double10000 foo = new Double10000();
Foo(foo);
} private static void Foo(Double10000 foo, int n = 100)
{
if (n == foo.Double0)
{
return;
} Foo(foo, n - 1);
}
如果可以让值类型和引用一样传递,是不是就可以减少值类型的复制同时减少堆栈的使用,请注意不要纠结值类型是分配在堆中还是栈中的问题,上面的代码更多的是方法的递归
对比内存的使用,更多的时候关心的是运行的速度。添加一些代码用来测试性能,同时减少调用
var st = new Stopwatch();
st.Start();
Foo(foo);
st.Stop();
Console.WriteLine(st.ElapsedTicks);
private static void Foo(Double10000 foo, int n = 10)
{
if (n == foo.Double0)
{
return;
} Foo(foo, n - 1);
}
这里输出的 ElapsedTicks 的单位是 100ns 需要知道 1ms=1000000ns 也就是 1w 的 tick 就是 1 毫秒,下面我运行 3 次代码,收集到的值
10991
14950
16183
在 C# 7.2 可以使用 in 关键字告诉 VisualStudio 当前的方法不会对传进来的结构体进行修改,当前这样写只是语法层面。如果有一些厉害的黑客,可能还继续这样写入,于是为了防止真的进行修改,在底层还是复制了一份。
也就是只是在参数里面使用了 in 是不够的,具体请看这个拖后腿的“in” - Bean.Hsiang - 博客园
如果想要更好的使用内存同时提高性能,只有在可以被标记为只读的结构体的时候使用 in 才可以
先将 Double100 标记为 readonly 如果一个值类型标记为 readonly 也就无法对里面的字段或属性进行设置了
在 Foo 传入的方法参数标记 in 这样就完成了,因为 in 表示对参数不进行修改,而传入的是 readonly struct 本来就不能被修改,于是就传入 struct 的引用
readonly struct Double10000
private static void Foo(in Double10000 foo, int n = 10)
{
if (n == foo.Double0)
{
return;
}
Foo(foo, n - 1);
}
同样运行 3 次,可以看到速度是原来的 10 倍
2052
1837
1683
同时占用的堆栈更小,可以使用更多的递归,修改 Foo 函数调用次数为 1000 可以看到还能运行,但是如果去掉了参数 in 最多只能调用 20 次
没有加 in 的参数,运行了 17 次
添加了 in 之后因为不需要复制值,减少内存的时候,此时运行了 1000 次递归都可以,在使用in之后速度和使用内存都比较好
在很多次方法调用使用参数的时候,如果传入的值是值类型,如果此时的 struct 里面的属性都是只读属性,推荐将 struct 标记为 readonly 同时在方法参数标记 in 这样可以让 struct 作为引用传递,也就是复制的只是指针,只要 struct 的长度比指针小就推荐这个方法
2018-12-25-C#-7.2-通过-in-和-readonly-struct-减少方法值复制提高性能的更多相关文章
- 2018.12.25 SOW
1. Understanding Customer Requirements 11.1. Project Overview 21.2. System Requirements 21.3. Indust ...
- 2018.12.25 Spring中JDBCTemplate模版API学习
1 Spring整合JDBC模版 1.1 spring中土拱了一个可以操作数据库的对象.对象封装了jdbc技术 JDBCTemplateJDBC模板对象 1.2 与DBUtils中的QueryRunn ...
- 12.25模拟赛T1
可以区间dp,但是复杂度太高. 所以应该是贪心,怎么贪心呢? 这种题目,最好还是手玩找一些规律. 可以发现,由于保证可以m次填完,所以颜色之间没有相互包含关系. 比较像分治的模型. 所以考虑拿到一个区 ...
- loli的测试-2018.12.9
模拟赛-2018.12.9 这是NOIP之后第一次模拟赛...但是考的比较悲惨. 非常喜欢写考试总结,不知道为什么... T1:https://www.luogu.org/problemnew/sho ...
- 2018.12.02 Socket编程之初识Socket
Socket编程主要分为TCP/UDP/SCTP三种,每一种都有各自的优点,所以会根据实际情况决定选用何种Socket,今天开始我将会逐步学习Socket编程,并将学习过程记录于此. 今天学习的是TC ...
- OPPO Developers Conference(2018.12.26)
时间:2018.12.26地点:北京国家会议中心
- Tencent Cloud Developers Conference(2018.12.15)
时间:2018.12.15地点:北京朝阳悠唐皇冠假日酒店
- 2018.12.1 Test
目录 2018.12.1 Test A 串string(思路) B 变量variable(最小割ISAP) C 取石子stone(思路 博弈) 考试代码 B C 2018.12.1 Test 题目为2 ...
- 2018/04/25 基于 编译安装的 PHP7 安装 swoole 扩展
在上一篇文章我们知道了如何去编译安装一个自己需要的 PHP 版本. 2018/04/25 PHP7的编译安装 这里还没有完,我们还需要安装我们的扩展,才算完成今天的任务. -- 下载扩展 还是官网下载 ...
- 「版本升级」MyEclipse CI 2018.12.0正式发布
新版本MyEclipse为WildFly 14新增一个新的服务器连接器,改进性能并新增一些Java 10修复程序.新版本为IDE做了几个核心修复,这是MyEclipse 2018一个更棒的升级. [M ...
随机推荐
- 洛谷 P3803 【模板】多项式乘法(FFT)
题目链接:P3803 [模板]多项式乘法(FFT) 题意 给定一个 \(n\) 次多项式 \(F(x)\) 和一个 \(m\) 次多项式 \(G(x)\),求 \(F(x)\) 和 \(G(x)\) ...
- 牛客练习赛48 D 小w的基站网络
链接:https://ac.nowcoder.com/acm/contest/923/D来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 262144K,其他语言52428 ...
- WebServer Project-01-反射
简介 上网浏览网页,离不开服务器,客户请求页面,服务器响应页面,响应的内容是根据每个web请求来产生动态内容的,其内部即启动多个线程来产生不同内容.这种请求响应的交互,都是基于HTTP协议的. 当然现 ...
- 从零开始搭建系统1.1——CentOs安装
本篇主要是记录安装CentOs的过程,为什么会选择CentOs,没有过多的原因,主要是出于CentOs相对来说安装的人比较多, 以后有问题了方便查资料.本次安装是安装在一台笔记本上,WIN7+Cent ...
- ASP.NET MVC 解决账号重复登录问题
解决重复登录 用到了 .net 身份票证 和Global全局处理文件 第一步 登录方法 传入用户名 private void GetOnline(string Name) { Hashtable S ...
- Python:Logging日志处理
程序中,需要添加日志来记录大量信息. import logging # 第一步:创建logger self.logger = logging.getLogger() self.logger.setLe ...
- HDFS(Hadoop Distributed File System)的组件架构概述
1.hadoop1.x和hadoop2.x区别 2.组件介绍 HDFS架构概述1)NameNode(nn): 存储文件的元数据,如文件名,文件目录结构,文件属性(生成时间,副本数,文件权限),以及每个 ...
- DELPHI 异常处理2
今天在写用ADO从EXCEL导入数据的程序时,出现个错误,弄了很久都弄不好,最后还是用异常处理解决掉了. 数据中日期在导入之前要转换成日期格式,虽然说一个strtodate就可以转好了,但是数据中的日 ...
- NX二次开发-创建直线UF_CURVE_create_line与NXOpen->CreateLine
NX11+VS2013 #include <uf.h> #include <uf_curve.h> #include <NXOpen/CurveCollection.hx ...
- hdu多校第六场1005 (hdu6638) Snowy Smilel 线段树/区间最大和
题意: 给定一个矩阵,矩阵上有若干点,每个点有正或负的权值,找一个方框框住一些点使得方框中点权值最大. 题解: 离散化横纵坐标,容易将这个问题转化为在矩阵上求最大和子矩阵的问题. 普通的n*n的矩阵的 ...