当前位置: 首页 > news >正文

有哪些网站交互效果做的好的如何让google收录网站

有哪些网站交互效果做的好的,如何让google收录网站,佛山微信网站推广多少钱,网站建设投入产出分析前言我们知道在C#和Java明显的一个区别就是C#可以自定义值类型#xff0c;也就是今天的主角struct#xff0c;我们有了更加方便的class为什么微软还加入了struct呢#xff1f;这其实就是今天要谈到的一个优化性能的Tips使用结构体替代类。那么使用结构体替代类有什么好处呢也就是今天的主角struct我们有了更加方便的class为什么微软还加入了struct呢这其实就是今天要谈到的一个优化性能的Tips使用结构体替代类。那么使用结构体替代类有什么好处呢在什么样的场景需要使用结构体来替代类呢今天的文章为大家一一解答。注意本文全部都以x64位平台为例现实的案例举一个现实系统的例子大家都知道机票购票的流程开始选择起抵城市和机场这是航线然后根据自己的需要日期和时间挑一个自己喜欢的航班和舱位然后付款。内存占用那么全国大约49航司8000多个航线平均每个航线有20个航班每个航班平均有10组舱位价格(经济舱、头等还有不同的折扣权益)一般OTA(Online Travel Agency在线旅游平台)允许预订一年内的机票。也就是说平台可能有8000*20*10*365~5亿的价格数据以上数据均来源网络实际中的数据量不方便透露。OTA平台为了能让你更快的搜索想要的航班会将热门的航线价格数据从数据库拿出来缓存在内存中内存比单独网络和磁盘传输快的多得多详情见下图就取20%也大约有1亿数据在内存中。操作速度执行指令1/1,000,000,000 秒 1 纳秒从一级缓存读取数据0.5 纳秒分支预测失败5 纳秒从二级缓存读取数据7 纳秒使用Mutex加锁和解锁25 纳秒从主存(RAM内存)中读取数据100 纳秒在1Gbps速率的网络上发送2Kbyte的数据20,000 纳秒从内存中读取1MB的数据250,000 纳秒磁头移动到新的位置(代指机械硬盘)8,000,000 纳秒从磁盘中读取1MB的数据20,000,000 纳秒发送一个数据包从美国到欧洲然后回来150 毫秒 150,000,000 纳秒假设我们有如下一个类类里面有这些属性(现实中要复杂的多而且会分航线、日期等各个维度存储而且不同航班有不同的售卖规则这里演示方便忽略)那么这1亿数据缓存在内存中需要多少空间呢public class FlightPriceClass {/// summary/// 航司二字码 如 中国国际航空股份有限公司CA/// /summarypublic string Airline { get; set; }/// summary/// 起始机场三字码 如 上海虹桥国际机场SHA/// /summarypublic string Start { get; set; }/// summary/// 抵达机场三字码 如 北京首都国际机场PEK/// /summarypublic string End { get; set; }/// summary/// 航班号 如 CA0001/// /summarypublic string FlightNo { get; set; }/// summary/// 舱位代码 如 Y/// /summarypublic string Cabin { get; set; }/// summary/// 价格 单位元/// /summarypublic decimal Price { get; set; }/// summary/// 起飞日期 如 2017-01-01/// /summarypublic DateOnly DepDate { get; set; }/// summary/// 起飞时间 如 08:00/// /summarypublic TimeOnly DepTime { get; set; }/// summary/// 抵达日期 如 2017-01-01/// /summarypublic DateOnly ArrDate { get; set; }/// summary/// 抵达时间 如 08:00/// /summarypublic TimeOnly ArrTime { get; set; } }我们可以写一个Benchmark来看看100W的数据需要多少空间然后在推导出1亿的数据// 随机预先生成100W的数据 避免计算逻辑导致结果不准确 public static readonly FlightPriceClass[] FlightPrices Enumerable.Range(0,100_0000).Select(index new FlightPriceClass{Airline $C{(char)(index % 26 A)},Start $SH{(char)(index % 26 A)},End $PE{(char)(index % 26 A)},FlightNo ${index % 1000:0000},Cabin ${(char)(index % 26 A)},Price index % 1000,DepDate DateOnly.FromDateTime(BaseTime.AddHours(index)),DepTime TimeOnly.FromDateTime(BaseTime.AddHours(index)),ArrDate DateOnly.FromDateTime(BaseTime.AddHours(3 index)),ArrTime TimeOnly.FromDateTime(BaseTime.AddHours(3 index)),}).ToArray();// 使用类来存储 [Benchmakr] public FlightPriceClass[] GetClassStore() {var arrays new FlightPriceClass[FlightPrices.Length];for (int i 0; i FlightPrices.Length; i){var item FlightPrices[i];arrays[i] new FlightPriceClass{Airline item.Airline,Start item.Start,End item.End,FlightNo item.FlightNo,Cabin item.Cabin,Price item.Price,DepDate item.DepDate,DepTime item.DepTime,ArrDate item.ArrDate,ArrTime item.ArrTime};}return arrays; }来看看最终的结果图片如下所示。从上面的图可以看出来100W数据大约需要107MB的内存存储那么一个占用对象大约就是112byte了那么一亿的对象就是约等于10.4GB。这个大小已经比较大了那么还有没有更多的方案可以减少一些内存占用呢有小伙伴就说了一些方案。可以用int来编号字符串可以使用long来存储时间戳可以想办法用zip之类算法压缩一下等等我们暂时也不用这些方法对照本文的的标题大家应该能想到用什么办法嘿嘿那就是使用结构体来替代类我们定义了一个一样的结构体如下所示。[StructLayout(LayoutKind.Auto)] public struct FlightPriceStruct {// 属性与类一致...... }我们可以使用Unsafe.SizeOf来查看值类型所需要的内存大小比如像下面这样。可以看到这个结构体只需要88byte比类所需要的112byte少了27%。来实际看看能节省多少内存。结果很不错呀内存确实如我们计算的一样少了27%另外赋值速度快了57%而且更重要的是GC发生的次数也少了。那么为什么结构体可以节省那么多的内存呢这里需要聊一聊结构体和类存储数据的区别下图是类数组的存储格式。我们可以看到类数组只存放指向数组引用元素的指针不直接存储数据而且每个引用类型的实例都有以下这些东西。对象头大小为8ByteCoreCLR上的描述是存储“需要负载到对象上的所有附加信息”比如存储对象的lock值或者HashCode缓存值。方法表指针大小为8Byte指向类型的描述数据也就是经常提到的(Method Table)MT里面会存放GCInfo字段以及方法定义等等。对象占位符大小为8Byte当前的GC要求所有的对象至少有一个当前指针大小的字段如果是一个空类除了对象头和方法表指针以外还会占用8Byte如果不是空类那就是存放第一个字段。也就是说一个空类不定义任何东西也至少需要24byte的空间8byte对象头8byte方法表指针8byte对象占位符。回到本文中由于不是一个空类所以每个对象除了数据存储外需要额外的16byte存储对象头和方法表另外数组需要8byte存放指向对象的指针所以一个对象存储在数组中需要额外占用24byte的空间。我们再来看看值类型(结构体)。从上图中我们可以看到如果是值类型的数组那么数据是直接存储在数组上不需要引用。所以存储相同的数据每个空结构体都能省下24byte无需对象头、方法表和指向实例的指针。另外结构体数组当中的数组数组也是引用类型所以它也有24byte的数据它的对象占位符用来存放数组类型的第一个字段-数组大小。我们可以使用ObjectLayoutInspector这个Nuget包打印对象的布局信息类定义的布局信息如下可以看到除了数据存储需要的88byte以外还有16byte额外空间。结构体定义的布局信息如下可以看到每个结构体都是实际的数据存储不包含额外的占用。那可不可以节省更多的内存呢我们知道在64位平台上一个引用(指针)是8byte而在C#上默认的字符串使用Unicode-16也就是说2byte代表一个字符像航司二字码、起抵机场这些小于4个字符的完全可以使用char数组来节省内存比一个指针占用还要少那我们修改一下代码。// 跳过本地变量初始化 [SkipLocalsInit] // 调整布局方式 使用Explicit自定义布局 [StructLayout(LayoutKind.Explicit, CharSet CharSet.Unicode)] public struct FlightPriceStructExplicit {// 需要手动指定偏移量[FieldOffset(0)]// 航司使用两个字符存储public unsafe fixed char Airline[2];// 由于航司使用了4byte 所以起始机场偏移4byte[FieldOffset(4)]public unsafe fixed char Start[3];// 同理起始机场使用6byte 偏移10byte[FieldOffset(10)]public unsafe fixed char End[3];[FieldOffset(16)]public unsafe fixed char FlightNo[4];[FieldOffset(24)]public unsafe fixed char Cabin[2];// decimal 16byte[FieldOffset(28)]public decimal Price;// DateOnly 4byte[FieldOffset(44)]public DateOnly DepDate;// TimeOnly 8byte[FieldOffset(48)]public TimeOnly DepTime;[FieldOffset(56)]public DateOnly ArrDate;[FieldOffset(60)]public TimeOnly ArrTime;}在来看看这个新结构体对象的布局信息。可以看到现在只需要68byte了最后4byte是为了地址对齐因为CPU字长是64bit我们不用管。按照我们的计算能比88Byte节省了29%的空间。当然使用unsafe fixed char以后就不能直接赋值了需要进行数据拷贝才行代码如下。// 用于设置string值的扩展方法 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void SetTo(this string str, char* dest) { fixed (char* ptr str) { Unsafe.CopyBlock(dest, ptr, (uint)(Unsafe.SizeOfchar() * str.Length)); } }// Benchmark的方法 public static unsafe FlightPriceStructExplicit[] GetStructStoreStructExplicit() { var arrays new FlightPriceStructExplicit[FlightPrices.Length]; for (int i 0; i FlightPrices.Length; i) { ref var item ref FlightPrices[i]; arrays[i] new FlightPriceStructExplicit { Price item.Price, DepDate item.DepDate, DepTime item.DepTime, ArrDate item.ArrDate, ArrTime item.ArrTime }; ref var val ref arrays[i]; // 需要先fixed 然后再赋值fixed (char* airline val.Airline) fixed (char* start val.Start) fixed (char* end val.End) fixed (char* flightNo val.FlightNo) fixed (char* cabin val.Cabin) { item.Airline.SetTo(airline); item.Start.SetTo(start); item.End.SetTo(end); item.FlightNo.SetTo(flightNo); item.Cabin.SetTo(cabin); } } return arrays; }再来跑一下看看这样存储提升是不是能节省29%的空间呢。是吧从84MB-65MB节省了大约29%的内存不错不错基本可以达到预期了。但是我们发现这个Gen0 Gen1 Gen2这些GC发生了很多次在实际中的话因为这些都是使用的托管内存GC在进行回收的时候会扫描这65MB的内存可能会让它的STW变得更久既然这些是缓存的数据一段时间内不会回收和改变那我们能让GC别扫描这些嘛答案是有的我们可以直接使用非托管内存使用Marshal类就可以申请和管理非托管内存可以达到你写C语言的时候用的malloc函数类似的效果。// 分配非托管内存 // 传参是所需要分配的字节数 // 返回值是指向内存的指针 IntPtr Marshal.AllocHGlobal(int cb);// 释放分配的非托管内存 // 传参是由Marshal分配内存的指针地址 void Marshal.FreeHGlobal(IntPtr hglobal);再修改一下Benchmark的代码将它改成使用非托管内存。// 定义了out ptr参数用于将指针传回 public static unsafe int GetStructStoreUnManageMemory(out IntPtr ptr) { // 使用AllocHGlobal分配内存大小使用SizeOf计算结构体大小乘需要的数量var unManagerPtr Marshal.AllocHGlobal(Unsafe.SizeOfFlightPriceStructExplicit() * FlightPrices.Length); ptr unManagerPtr; // 将内存空间指派给FlightPriceStructExplicit数组使用var arrays new SpanFlightPriceStructExplicit(unManagerPtr.ToPointer(), FlightPrices.Length); for (int i 0; i FlightPrices.Length; i) { ref var item ref FlightPrices[i]; arrays[i] new FlightPriceStructExplicit { Price item.Price, DepDate item.DepDate, DepTime item.DepTime, ArrDate item.ArrDate, ArrTime item.ArrTime }; ref var val ref arrays[i]; fixed (char* airline val.Airline) fixed (char* start val.Start) fixed (char* end val.End) fixed (char* flightNo val.FlightNo) fixed (char* cabin val.Cabin) { item.Airline.SetTo(airline); item.Start.SetTo(start); item.End.SetTo(end); item.FlightNo.SetTo(flightNo); item.Cabin.SetTo(cabin); } } // 返回长度return arrays.Length; }// 切记非托管内存不使用的时候 需要手动释放 [Benchmark] public void GetStructStoreUnManageMemory() { _ FlightPriceCreate.GetStructStoreUnManageMemory(out var ptr); // 释放非托管内存Marshal.FreeHGlobal(ptr); }再来看看Benchmark的结果。结果非常Amazing呀没有在托管内存上分配空间赋值的速度也比原来快了很多后面发生GC的时候也无需扫描这一段内存降低了GC压力。这样的结果基本就比较满意了。到现在的话存储1亿的数据差不多6.3GB如果使用上文中提高的其它方法应该还能降低一些比如像如下代码一样使用枚举来替换字符串金额使用分存储只存时间戳。[StructLayout(LayoutKind.Explicit, CharSet CharSet.Unicode)] [SkipLocalsInit] public struct FlightPriceStructExplicit {// 使用byte标识航司 byte范围0~255[FieldOffset(0)]public byte Airline;// 使用无符号整形表示起抵机场和航班号 2^16次方[FieldOffset(1)]public UInt16 Start;[FieldOffset(3)]public UInt16 End;[FieldOffset(5)]public UInt16 FlightNo;[FieldOffset(7)]public byte Cabin;// 不使用decimal 价格精确到分存储[FieldOffset(8)]public long PriceFen;// 使用时间戳替代[FieldOffset(16)]public long DepTime;[FieldOffset(24)]public long ArrTime; }最后的出来的结果每个数据只需要32byte的空间存储这样存储一亿的的话也不到3GB。本文就不继续讨论这些方式了。计算速度那么使用结构体有什么问题吗我们来看看计算这个计算很简单就是把符合条件的航线筛选出来首先类和结构体都定义了如下代码的方法Explicit结构体比较特殊我们使用Span比较。// 类和结构体定义的方法 当然实际中的筛选可能更加复杂 // 比较航司 public bool EqulasAirline(string airline) { return Airline airline; } // 比较起飞机场 public bool EqualsStart(string start) { return Start start; } // 比较抵达机场 public bool EqualsEnd(string end) { return End end; } // 比较航班号 public bool EqualsFlightNo(string flightNo) { return FlightNo flightNo; } // 价格是否小于指定值 public bool IsPriceLess(decimal min) { return Price min; } // 对于Explicit结构体 定义了EqualsSpan方法 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe bool SpanEquals(this string str, char* dest, int length) { // 使用span来比较两个数组return new Spanchar(dest, length).SequenceEqual(str.AsSpan()); }// 实现的方法如下所示 public static unsafe bool EqualsAirline(FlightPriceStructExplicit item, string airline) { // 传需要比较的长度return airline.SpanEquals(item.Airline, 2); } // 下面的方式类似不再赘述 public static unsafe bool EqualsStart(FlightPriceStructExplicit item, string start) { return start.SpanEquals(item.Start, 3); } public static unsafe bool EqualsEnd(FlightPriceStructExplicit item, string end) { return end.SpanEquals(item.End, 3); } public static unsafe bool EqualsFlightNo(FlightPriceStructExplicit item, string flightNo) { return flightNo.SpanEquals(item.FlightNo, 4); } public static unsafe bool EqualsCabin(FlightPriceStructExplicit item, string cabin) { return cabin.SpanEquals(item.Cabin, 2); } public static bool IsPriceLess(FlightPriceStructExplicit item, decimal min) { return item.Price min; }最后Benchmark的代码如下所示对于每种存储结构都是同样的代码逻辑由于100W数据一下就跑完了每种存储方式的数据量都为150W。// 将需要的数据初始化好 避免对测试造成影响 private static readonly FlightPriceClass[] FlightPrices FlightPriceCreate.GetClassStore(); private static readonly FlightPriceStruct[] FlightPricesStruct FlightPriceCreate.GetStructStore(); private static readonly FlightPriceStructUninitialized[] FlightPricesStructUninitialized FlightPriceCreate.GetStructStoreUninitializedArray(); private static readonly FlightPriceStructExplicit[] FlightPricesStructExplicit FlightPriceCreate.GetStructStoreStructExplicit(); // 非托管内存比较特殊 只需要存储指针地址即可 private static IntPtr _unManagerPtr; private static readonly int FlightPricesStructExplicitUnManageMemoryLength FlightPriceCreate.GetStructStoreUnManageMemory(out _unManagerPtr); [Benchmark(Baseline true)] public int GetClassStore() { var caAirline 0; var shaStart 0; var peaStart 0; var ca0001FlightNo 0; var priceLess500 0; for (int i 0; i FlightPrices.Length; i) { // 简单的筛选数据var item FlightPrices[i]; if (item.EqualsAirline(CA))caAirline; if (item.EqualsStart(SHA))shaStart; if (item.EqualsEnd(PEA))peaStart; if (item.EqualsFlightNo(0001))ca0001FlightNo; if (item.IsPriceLess(500))priceLess500; } Debug.WriteLine(${caAirline},{shaStart},{peaStart},{ca0001FlightNo},{priceLess500}); return caAirline shaStart peaStart ca0001FlightNo priceLess500; } [Benchmark] public int GetStructStore() { var caAirline 0; var shaStart 0; var peaStart 0; var ca0001FlightNo 0; var priceLess500 0; for (int i 0; i FlightPricesStruct.Length; i) { var item FlightPricesStruct[i]; if (item.EqualsAirline(CA))caAirline; if (item.EqualsStart(SHA))shaStart; if (item.EqualsEnd(PEA))peaStart; if (item.EqualsFlightNo(0001))ca0001FlightNo; if (item.IsPriceLess(500))priceLess500; } Debug.WriteLine(${caAirline},{shaStart},{peaStart},{ca0001FlightNo},{priceLess500}); return caAirline shaStart peaStart ca0001FlightNo priceLess500; } [Benchmark] public int GetFlightPricesStructExplicit() { var caAirline 0; var shaStart 0; var peaStart 0; var ca0001FlightNo 0; var priceLess500 0; for (int i 0; i FlightPricesStructExplicit.Length; i) { var item FlightPricesStructExplicit[i]; if (FlightPriceStructExplicit.EqualsAirline(item,CA))caAirline; if (FlightPriceStructExplicit.EqualsStart(item,SHA))shaStart; if (FlightPriceStructExplicit.EqualsEnd(item,PEA))peaStart; if (FlightPriceStructExplicit.EqualsFlightNo(item,0001))ca0001FlightNo; if (FlightPriceStructExplicit.IsPriceLess(item,500))priceLess500; } Debug.WriteLine(${caAirline},{shaStart},{peaStart},{ca0001FlightNo},{priceLess500}); return caAirline shaStart peaStart ca0001FlightNo priceLess500; } [Benchmark] public unsafe int GetFlightPricesStructExplicitUnManageMemory() { var caAirline 0; var shaStart 0; var peaStart 0; var ca0001FlightNo 0; var priceLess500 0; var arrays new SpanFlightPriceStructExplicit(_unManagerPtr.ToPointer(), FlightPricesStructExplicitUnManageMemoryLength); for (int i 0; i arrays.Length; i) { var item arrays[i]; if (FlightPriceStructExplicit.EqualsAirline(item,CA))caAirline; if (FlightPriceStructExplicit.EqualsStart(item,SHA))shaStart; if (FlightPriceStructExplicit.EqualsEnd(item,PEA))peaStart; if (FlightPriceStructExplicit.EqualsFlightNo(item,0001))ca0001FlightNo; if (FlightPriceStructExplicit.IsPriceLess(item,500))priceLess500; } Debug.WriteLine(${caAirline},{shaStart},{peaStart},{ca0001FlightNo},{priceLess500}); return caAirline shaStart peaStart ca0001FlightNo priceLess500; }Benchmark的结果如下。我们看到单独使用结构体比类要慢一点点但是后面那些使用Explicit布局方式和非托管内存的就慢很多很多了有一倍多的差距鱼和熊掌真的不可兼得吗我们来分析一下后面2种方式比较慢的原因原因是因为值拷贝我们知道在C#中默认引用类型是引用传递而值类型是值传递。引用类型调用方法传递时只需要拷贝一次长度为CPU字长32位系统就是4byte64位就是8byte值类型调用方法是值传递比如值需要占用4byte那么就要拷贝4byte在小于等于CPU字长时有优势大于时优势就变为劣势。而我们的结构体都远远大于CPU字长64位8byte而我们的后面的代码实现发生了多次值拷贝这拖慢了整体的速度。那么有没有什么办法不发生值拷贝呢当然值类型在C#中也可以引用传递我们有ref关键字只需要在值拷贝的地方加上就好了代码如下所示。// 改造比较方法使其支持引用传递 // 加入ref public static unsafe bool EqualsAirlineRef(ref FlightPriceStructExplicit item, string airline) { // 传递的是引用 需要fixed获取指针fixed(char* ptr item.Airline) { return airline.SpanEquals(ptr, 2); } }// Benchmark内部代码也修改为引用传递 [Benchmark] public unsafe int GetStructStoreUnManageMemoryRef() { var caAirline 0; var shaStart 0; var peaStart 0; var ca0001FlightNo 0; var priceLess500 0; var arrays new SpanFlightPriceStructExplicit(_unManagerPtr.ToPointer(), FlightPricesStructExplicitUnManageMemoryLength); for (int i 0; i arrays.Length; i) { // 从数组里面拿直接引用ref var item ref arrays[i];// 传参也直接传递引用if (FlightPriceStructExplicit.EqualsAirlineRef(ref item,CA))caAirline; if (FlightPriceStructExplicit.EqualsStartRef(ref item,SHA))shaStart; if (FlightPriceStructExplicit.EqualsEndRef(ref item,PEA))peaStart; if (FlightPriceStructExplicit.EqualsFlightNoRef(ref item,0001))ca0001FlightNo; if (FlightPriceStructExplicit.IsPriceLessRef(ref item,500))priceLess500; } Debug.WriteLine(${caAirline},{shaStart},{peaStart},{ca0001FlightNo},{priceLess500}); return caAirline shaStart peaStart ca0001FlightNo priceLess500; }我们再来跑一下结果我们的Explicit结构体遥遥领先比使用类足足快33%而上一轮中使用非托管内存表现也很好排在了第二的位置。那么同样是引用传递使用类会更慢一些呢这就要回到更加底层的CPU相关的知识了我们CPU里面除了基本的计算单元以外还有L1、L2、L3这些数据缓存如下图所示。这个和CPU的性能挂钩记得文章开头那一个图吗CPU内部的缓存是速度最快的所以第一个原因就是对于结构体数组数据是存放的连续的地址空间非常利于CPU缓存而类对象由于是引用类型需要指针访问对于CPU缓存不是很有利。第二个原因是因为引用类型在访问时需要进行解引用操作也就是说需要通过指针找到对应内存中的数据而结构体不需要。那么如何验证我们的观点呢其实BenchmarkDotNet提供了这样的指标展示只需要引入BenchmarkDotNet.Diagnostics.WindowsNuget包然后在需要评测的类上面加入以下代码。[HardwareCounters(HardwareCounter.LlcMisses, // 缓存未命中次数 HardwareCounter.LlcReference)] // 解引用次数 public class SpeedBench : IDisposable {...... }结果如下所示由于需要额外的统计Windows ETW的信息所以跑的会稍微慢一点。我们可以从上图看出使用引用类型缓存未命中的次数最多解引用的次数也很多这些拖慢了性能。如下图所示顺序存储的结构体要比跳跃式的引用类型内存访问效率高。另外对象的体积越小对于缓存就越友好。总结在本文章中我们讨论了如何使用结构体替换类达到降低大量内存占用和提升几乎一半计算性能的目的。也讨论了非托管内存在.NET中的简单使用。结构体是我非常喜欢的东西它有着相当高效的存储结构和相当优异的性能。但是你不应该将所有的类都转换为结构体因为它们有不同的适用场景。那么我们在什么时候需要使用结构体什么时候需要使用类呢微软官方给出了答案。✔️ 如果类型的实例比较小并且通常生存期较短或者常嵌入在其他对象中则考虑定义结构体而不是类。❌ 避免定义结构除非具有所有以下特征它逻辑上表示单个值类似于基元类型int、double 等等- 比如我们的缓存数据基本都是基元类型。它的实例大小小于16字节 - 值拷贝的代价是巨大的不过现在有了ref能有更多的适用场景。它是不可变的 - 在我们今天的例子中缓存的数据是不会改变的所以具有这个特征。它不必频繁装箱 - 频繁装拆箱对性能有较大的损耗在我们的场景中函数都做了ref适配所以也不存在这种情况。在所有其他情况下都应将类型定义为类。其实大家从这些方式也能看出来C#是一门入门简单但是上限很高的语言平时可以利用C#的语法特性快速的进行需求变现而如果有了性能瓶颈你完全可以像写C代码一样写C#代码获得和C媲美的性能。附录本文源码链接-晚点会上传: https://github.com/InCerryGit/BlogCode-Dotnet-Opt-Perf-Use-Struct-Instead-Of-Class选择结构体还是类:https://docs.microsoft.com/zh-cn/dotnet/standard/design-guidelines/choosing-between-class-and-struct结构体设计原则: https://docs.microsoft.com/zh-cn/dotnet/standard/design-guidelines/struct.NET Marshal类: https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal?viewnet-6.0 .NET Span类 : https://docs.microsoft.com/zh-cn/dotnet/api/system.span-1?viewnet-6.0CPU不同硬件的速度 : http://norvig.com/21-days.html#answers
http://www.pierceye.com/news/135819/

相关文章:

  • wordpress到服务器配置云南seo
  • 常见网站安全漏洞行业网站如何推广
  • 网站开发实战项目苏州行业网站建设费用
  • 大团企业网站制作东莞网站制作的公司
  • 石家庄做网站公司的电话网站建设费用大概多少
  • 襄阳市网站建设怎么注册工作邮箱
  • 在百度里面做个网站怎么做的摄影大赛官网
  • 网站建设需要哪些的ps网站策划
  • 网站维护的意义上海知名进出口贸易公司
  • 青岛中小微企业互联网站建设补贴微信小程序怎么发布上线
  • 贺州做网站哪家公司温州移动网站建设服务商
  • 网站变灰兼容代码北京计算机培训学校
  • 网站导航包括海拉尔网站建设+网站设计
  • flashfxp 上传网站佛山哪里有网站开发
  • qq互联 网站开发济南建设集团有限公司官网
  • 网站开发兼职网站学校网站构建
  • 简约网站后台媒体网站开发
  • 广东营销网站建设网页设计理念及设计思路
  • 咋自己做网站桂林生活网官网首页
  • 电子商务网站建设的展望自己做壁纸的网站
  • 国外h5建站网站建设方案总结评语
  • 百度开放平台白城整站优化
  • 搜狗整站优化广州市网站建站
  • 最方便建立网站北京定制网络营销收费
  • 烟台放心的一站式网站建设桐梓网站建设
  • 如何高效的完成网站建设步骤美食分享网站建设策划书
  • 建立网站的软件网站建设数据库的购买
  • 建网站需要多大的宽带wordpress 分享后可见
  • 自建营销型企业网站阿里网 网站备案流程
  • 与网站建设相关的论文题目wordpress图片上文字