问题之源

C# 7.2推出了全新的参数修饰符in,据说是能提升一定的性能,官方MSDN文档描述是:

Add the in modifier to pass an argument by reference and declare your design intent to pass arguments by reference to avoid unnecessary copying.

然而,想当然地使用它却导致更多的副本出现,影响代码运行速度。

MSDN中还有一段隐含的副作用的描述:

You can call any instance method that uses by value parameters. In those instances, a copy of the in parameter is created.

同时文档也提到了readonly ref的目的:

After adding support for in parameters and ref redonly [sic] returns the problem of defensive copying will get worse since readonly variables will become more common.

来看一下MSDN里的这个例子::

private static double CalculateDistance(in Point3D point1, in Point3D point2)
{
double xDifference = point1.X - point2.X;
double yDifference = point1.Y - point2.Y;
double zDifference = point1.Z - point2.Z; return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}

假设Point3D类型是这样定义的:

public struct Point3D
{
public Point3D(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
} public double X { get; }
public double Y { get; }
public double Z { get; }
}

结果C#的几个本不相关的特性以一种闹心的方式结合起来:

  1. 标记了in的结构体参数是readonly只读的
  2. 调用标记为readonly的结构体的实例化方法将产生一个副本
    • 因为这个方法要通过改变this指针来达到确保标记了readonly的原值不会被修改
  3. 属性访问器也是实例方法,受this影响

每次给CalculateDistance方法传递标记了in的结构体参数时,编译器会在访问时自动为这个参数的每个属性创建一个副本,本以为不会创建副本,结果反而每个传进来的参数在方法内部弄出来3个!

这个问题存在已久,看一下Jon Skeet的博客:The Surprising Inefficiency of Readonly Fields。只不过使用in让这个尴尬的场面更频繁易现了。

解决方案

解决办法同样来自C# 7.2:readonly struct.

如果将public struct Point3D改成public readonly struct Point3D,因为所有字段也已经是readonly了,所以整个结构体都无需改变,编译器此时也会省掉副本的操作,只有这样才会出现结构体参数比按值传递获得更快的运行速度。

不过注意在C# 7.1中结构体是可以通过标记ref来传参达到同样避免副本开销的。尽管如此,结构体参数的字段想要改变值仍是可变的,甚至这个结构体都可以指向另一个新的。在函数的参数列表中使用in的声明主要意图还是为了告诉调用者本函数不会去修改传进来的参数,当然编译器也会配合强制保证。

示例

这里有一个关于inrefstruct和readonly struct各种组合的性能测评(结构体size太小看不出差别,因此这个示例把结构体增加到56 bytes以便跑出更明显的对比效果)。结果如下:

总结

  • 当使用in代替ref表示设计意图时,要明白在传递较大且较多的结构体时会有微小的性能损失
  • 当使用in又要避免产生副本或提高性能,在声明结构体时要使用readonly struct

(原文:‘in’ will make your code slower)

这个拖后腿的“in”的更多相关文章

  1. 2020年9月程序员工资统计,平均14459元!你给程序员拖后腿了吗?https://jq.qq.com/?_wv=1027&k=JMPndqoM

    2020年9月全国招收程序员362409人.2020年9月全国程序员平均工资14459元,工资中位数12500元,其中95%的人的工资介于5250元到35000元. 工资与上个月持平,但是岗位有所增加 ...

  2. 设计爬虫Hawk背后的故事

    本文写于圣诞节北京下午慵懒的午后.本文偏技术向,不过应该大部分人能看懂. 五年之痒 2016年,能记入个人年终总结的事情没几件,其中一个便是开源了Hawk.我花不少时间优化和推广它,得到的评价还算比较 ...

  3. ios培训机构排名

    移动互联网的时代,智能手机的作用已经无所不在,APP在人们的生活中也起到了非常重要的作用,iOS开发行业同样受到越来越多人的关注,更多的人选择参加iOS培训机构来加入这个行列,而如何选择一个真正可以学 ...

  4. 事后分析报告(Postmortem Report)

    小组讨论照片 设想和目标 1.我们的团队项目为英语单词学习助手,名为“我爱记单词”.主要提供服务包括:单词查询,单词测试,单词记忆和中英互译.目前开发的是单机版本,用户可以根据自己的需求灵活的使用相应 ...

  5. 《奥威Power-BI案例应用:带着漫画看报告》腾讯课程开课啦

    元旦小假期过去了,不管是每天只给自己两次下床机会的你,还是唱K看电影逛街样样都嗨的你,是时候重振旗鼓,重新上路了!毕竟为了不给国家的平均工资水平拖后腿,还是要努力工作的.话说2016年已经过去了,什么 ...

  6. 分享自己的超轻量级高性能ORM数据访问框架Deft

    Deft 简介 Deft是一个超轻量级高性能O/R mapping数据访问框架,简单易用,几分钟即可上手. Deft包含如下但不限于此的特点: 1.按照Transact-SQL的语法语义风格来设计,只 ...

  7. 一些对数学领域及数学研究的个人看法(转载自博士论坛wcboy)

    转自:http://www.math.org.cn/forum.php?mod=viewthread&tid=14819&extra=&page=1 原作者: wcboy 现在 ...

  8. 8.31 js基础总结1

    JavaScript是一种脚本语言,由web浏览器进行解释和执行.它给予页面灵魂,让页面可以动起来,包括动态的数据,动态的标签,动态的样式等等. 将JavaScript应用到网页中常用的方法有两种,第 ...

  9. 2016 ICPC China-Final 现场赛总结

    距离比赛结束快有一个礼拜了才抽出时间来写这篇总结.今年比赛打了也有5场了(4场区域赛+1场省赛),也取得了不错的成绩(区域赛两银),总的来说第一年就取得这成绩还是挺高兴的.我们队,我自己都渐渐的趋于成 ...

随机推荐

  1. typecho 文章归档调用

    <style> div { display: block; } .title { position: relative; margin: 0; line-height: 32px; fon ...

  2. java调用执行cmd指令启动weblogic

    这里的例子是启动weblogic import java.io.BufferedReader; import java.io.IOException; import java.io.InputStre ...

  3. MySQL 安装与使用(一)

    操作系统:CentOS release 5.10 (Final) 文件准备: MySQL-server-community-5.1.73-1.rhel5.i386.rpm MySQL-client-c ...

  4. C++对象模型:成员变量<一>非静态成员变量

    非静态成员变量,分别两种可能,要么类自定义,要么继承而来.根据<深度探索C++对象模型>的解读. class X { private: int x,y,z; }; 在这个类中,有三个私有成 ...

  5. *单链表[递归&不带头结点]

    不带头结点的单链表,递归法比较简明!(必背!) 单链表的结构: typedef struct node{ int data; struct node *next; }*List,Node; 创建第一种 ...

  6. 2.2.10数据类型String的常量池特性

    在JVM中具有String常量池缓存的功能 package com.cky.test; /** * Created by edison on 2017/12/8. */ public class Te ...

  7. currentTarget

    定义和用法 currentTarget 事件属性返回其监听器触发事件的节点,即当前处理该事件的元素.文档或窗口. 在捕获和起泡阶段,该属性是非常有用的,因为在这两个节点,它不同于 target 属性. ...

  8. The Seven Percent Solution

    Problem Description Uniform Resource Identifiers (or URIs) are strings like http://icpc.baylor.edu/i ...

  9. 第85讲:Scala中For表达式的强大表现力实战

    今天来学一下scala中的For表达式的用法. package scala.learn case class Persons(name:String,isMale:Boolean,children:P ...

  10. hdu2710 Max Factor

    题目 //下面这个是最先用的方法,因为学姐先讲完这个,所以懒得写代码,就将就着这个用,结果搞了老半天,还是错了,心累.. #include<stdio.h> #include<str ...