C#中谁最快:结构还是类?

前言
在内存当道的日子里,无论什么时候都要考虑这些代码是否会影响程序性能呢?
在现在的世界里,几乎不会去考虑用了几百毫秒,可是在特别的场景了,往往这几百毫米确影响了整个项目的快慢。
通过了解这两者之间的性能差异,希望帮助大家在合适的场景里选择正确的编码。
实例
public class PointClass
{
public int X { get; set; }
public int Y { get; set; }
public PointClass(int x, int y)
{
X = x;
Y = y;
}
}
public class PointClassFinalized : PointClass
{
public PointClassFinalized(int x, int y) : base(x, y)
{
}
~PointClassFinalized()
{
// added a finalizer to slow down the GC
}
}
public struct PointStruct
{
public int X { get; set; }
public int Y { get; set; }
public PointStruct(int x, int y)
{
X = x;
Y = y;
}
}
public class StructsTest : PerformanceTest
{
protected override bool MeasureTestA()
{
// access array elements
var list = new PointClassFinalized[Iterations];
for (int i = 0; i < Iterations; i++)
{
list[i] = new PointClassFinalized(i, i);
}
return true;
}
protected override bool MeasureTestB()
{
// access array elements
var list = new PointClass[Iterations];
for (int i = 0; i < Iterations; i++)
{
list[i] = new PointClass(i, i);
}
return true;
}
protected override bool MeasureTestC()
{
// access array elements
var list = new PointStruct[Iterations];
for (int i = 0; i < Iterations; i++)
{
list[i] = new PointStruct(i, i);
}
return true;
}
}
有一个PointClass 和一个 PointStruct
,这两者用于存放X 和Y 两个变量,而且还有一个 PointClassFinalized。
方法 MeasureTestA 创建了100万个 PointClassFinalized 实例
方法 MeasureTestB 创建了100万个 PointClass 实例
方法 MeasureTestC 创建了100万个 PointStruct 实例
您认为哪种方法最快?

MeasureTestB 和 MeasureTestC 这两个方法的唯一不同在于一个是创建类 一个是创建结构。
MeasureTestC 仅在17毫秒内完成分配并运行,比 MeasureTestB 方法快8.6倍!
为什么会出现这样的事情,这里发生了什么?
不同的在于结构和类如何存储在内存中。
下面是 PointClass 实例 内存布局:

该列表是一个局部变量,存放在堆栈中。引用堆上的一组 PointClass实例
PointClass 是一个引用类型,存放在堆上。
该列表仅维护一个数组,指向存储在堆上 PointClass 实例。
观察到上图的黄色箭头,在堆上引用了很多实例。
数组是一组相同的对象,MeasureTestB 这个方法是将一组相同的对象存放在数组中。
当访问指定数组元素时,.NET运行时需要检索对象引用,然后“跟随”引用以获取PointClass实例。
当数组元素超出范围时,.NET垃圾收集器就会开始回收PointClass对象内存,在 MeasureTestA 方法中 的PointClassFinalized类 其实增加了额外时间。
.NET Framework在单个线程上运行所有终结器,线程必须在垃圾回收器可以回收内存之前依次处理1,000,000个对象。
可以看到MeasureTestA比MeasureTestB慢1.7倍。
我们来看看 PointStruct 的内存布局:

结构是值类型,所有 PointStruct 实例都存储在数组本身中。堆上只有一个对象。
初始化数组,.NET运行库可以将X和Y值直接写入数组里。无需在堆上创建新对象,也不需要引用它。
当访问指定数组元素时,.NET运行时可以直接检索结构。
当超出范围时,.NET垃圾回收器只需要处理单个对象。
总结
我们总要使用结构吗?要分情况看:
- 当您存储超过30-40个字节的数据时,请使用类。
- 存储引用类型时,请使用类。
- 当您存储多于几千个实例时,请使用类。
- 如果列表是长的生命周期的,请使用类。
- 在所有其他情况下,使用结构。
相关链接:
C#中谁最快:结构还是类?的更多相关文章
- .net(C#)中结构和类的区别
static void Main(string[] args) { //类型 //结构:值类型 //类:引用类型 //声明的语法:class struct //在类中,构造函数里,既可以给字段赋值,也 ...
- C#中结构与类的区别
一.类与结构的示例比较: 结构示例: public struct Person { string Name; int height; int weight public bool overWeight ...
- .NET中结构和类的区别
最近在学习Swift语言,看到了枚举这一章,Swift可以支持在枚举中定义方法...于是想到了回顾一下.NET中枚举.结构.类之间区别. 枚举在.NET较为简单,这里就不作比较,只谈谈结构和类. 1. ...
- C++中结构和类的区别
首先从从语言角度来看,c语言是一种结构化的语言,便于按照模块化的方式来组织程序,易于程序员的调试和维护,而对于c++来说,我么可以认为它是标准c的超集.实际上所有的c程序也是c++程序.但两者之间还是 ...
- 如何使用CSS3中的结构伪类选择器和伪元素选择器
结构伪类选择器介绍 结构伪类选择器是用来处理一些特殊的效果. 结构伪类选择器属性说明表 属性 描述 E:first-child 匹配E元素的第一个子元素. E:last-child 匹配E元素的最后一 ...
- [AI开发]视频结构化类应用的局限性
算法不是通用的,基于深度学习的应用系统不但做不到通用,即使对于同一类业务场景,还需要为每个场景做定制.特殊处理,这样才能有可能到达实用标准.这种局限性在计算机视觉领域的应用中表现得尤其突出,本文介绍基 ...
- 5.Swift枚举|结构体|类|属性|方法|下标脚本|继承
1. 枚举: ->在Swift中依然适用整数来标示枚举值,需搭配case关键字 enum Celebrity{ case DongXie,XiDu,Nandi,BeiGai } // 从左 ...
- CSS3 结构伪类选择器 详解
1 CSS3 结构伪类选择器 1.E:root 匹配E元素所在的根元素 即:html 2. E:nth-child(n) (1)匹配E元素的父元素中第n个子元素,(2)且该位置的子元素类型必须是E类型 ...
- Atitit java的异常exception 结构Throwable类
Atitit java的异常exception 结构Throwable类 1.1. Throwable类 2.StackTrace栈轨迹1 1.2. 3.cause因由1 1.3. 4.Suppres ...
随机推荐
- abp(net core)+easyui+efcore仓储系统——创建应用服务(五)
abp(net core)+easyui+efcore仓储系统目录 abp(net core)+easyui+efcore仓储系统——ABP总体介绍(一) abp(net core)+easyui+e ...
- pomelo使用中的常见问题
1.端口被占用, 有进程没杀干净. 用 pomelo kill --force 命令清一下进程.
- Linux下docker的安装
前言: 因为之前在自己的mac上直接使用HomeBrew的包管理安装的,使用brew install docker即可,这种方法简单,但最近想尝试在Linux下安装,费了一些时间,主要是启动docke ...
- T4生成实体和简单的CRUD操作
主要跟大家交流下T4,我这里针对的是mysql,我本人比较喜欢用mysql,所以语法针对mysql,所以你要准备mysql的DLL了,同理sqlserver差不多,有兴趣可以自己写写,首先网上找了一个 ...
- Redis 学习笔记(篇四):整数集合和压缩列表
整数集合 Redis 中当一个集合(set)中只包含整数,并且元素不多时,底层使用整数集合实现,否则使用字典实现. 那么: 为什么会出现整数集合呢?都使用字典存储不行吗? 整数集合在 Redis 中的 ...
- 前端学习【第一篇】: HTML内容
内容概要: HTML介绍 常用标签介绍 一. HTML介绍 web服务的本质 #!/usr/bin/env python3 # _*_ coding:utf- _*_ import socket sk ...
- 每日一问:谈谈 SharedPreferences 的 apply() 和 commit()
SharedPreferences 应该是任何一名 Android 初学者都知道的存储类了,它轻量,适合用于保存软件配置等参数.以键值对的 XML 文件形式存储在本地,程序卸载后也会一并清除,不会残留 ...
- 驰骋工作流引擎ccflow-ccbpm工作流引擎sdk表单装载逻辑处理注意事项
cbpm工作流引擎sdk表单装载逻辑处理注意事项 关键字:驰骋工作流引擎 sdk表单 装载接口调用 说明:如果您要采用ccbpm的sdk表单开发,在表单 加载前您需要调用一个接口来获得当前节点的信 ...
- redis RDB 和AOF
参考文献 Redis源码学习-AOF数据持久化原理分析(0) Redis源码学习-AOF数据持久化原理分析(1) Redis · 特性分析 · AOF Rewrite 分析 深入剖析 redis AO ...
- Android native进程间通信实例-socket本地通信篇之——基本通信功能
导读: 网上看了很多篇有关socket本地通信的示例,很多都是调通服务端和客户端通信功能后就没有下文了,不太实用,真正开发中遇到的问题以及程序稳定性部分没有涉及,代码健壮性不够,本系列(socket本 ...