GC优化:栈内存、span、NativeMemory、指针、池化内存 笔记

news/2024/11/16 19:00:46/文章来源:https://www.cnblogs.com/darklx/p/18549678

stackalloc


使用栈内存,减少GC压力

var wordMatchCounts = stackalloc float[wordCount];

Span


Span 支持 reinterpret_cast 的理念,即可以将 Span 强制转换为 Span

Span 支持 reinterpret_cast 的理念,即可以将 Span 强制转换为 Span(其中,Span 中的索引 0 映射到 Span 的前四个字节)。这样一来,如果读取字节缓冲区,可以安全高效地将它传递到对分组字节(视作整数)执行操作的方法。

Span 也能装在集合之ValueListBuilder & .AsSpan()

.NET 内部提升性能对象:ValueListBuilder & .AsSpan()

ValueListBuilder & .AsSpan()

.NET Core 源码中的内部提升性能对象:ValueListBuilder & .AsSpan()

它在 String.Replace 中被使用

public unsafe string Replace(string oldValue, string? newValue) {ArgumentException.ThrowIfNullOrEmpty(oldValue, nameof (oldValue));if (newValue == null)	newValue = string.Empty;// ISSUE: untyped stack allocationValueListBuilder<int> valueListBuilder = new ValueListBuilder<int>(new Span<int>((void*) __untypedstackalloc(new IntPtr(512)), 128));if (oldValue.Length == 1){if (newValue.Length == 1)return this.Replace(oldValue[0], newValue[0]);char ch = oldValue[0];int elementOffset = 0;while (true){int num = SpanHelpers.IndexOf(ref Unsafe.Add<char>(ref this._firstChar, elementOffset), ch, this.Length - elementOffset);if (num >= 0){valueListBuilder.Append(elementOffset + num);elementOffset += num + 1;}else break;}}else{int elementOffset = 0;while (true){int num = SpanHelpers.IndexOf(ref Unsafe.Add<char>(ref this._firstChar, elementOffset), this.Length - elementOffset, ref oldValue._firstChar, oldValue.Length);if (num >= 0){valueListBuilder.Append(elementOffset + num);elementOffset += num + oldValue.Length;}else break;}}if (valueListBuilder.Length == 0) eturn this;string str = this.ReplaceHelper(oldValue.Length, newValue, **valueListBuilder.AsSpan()**);valueListBuilder.Dispose();return str;}

.NET 内部类直接将集合转回为 Span<T>:CollectionsMarshal.AsSpan<string>(List<string>)

	private static unsafe string JoinCore<T>(ReadOnlySpan<char> separator, IEnumerable<T> values){if (typeof (T) == typeof (string)){if (values is List<string> list)return string.JoinCore(separator, (ReadOnlySpan<string>) CollectionsMarshal.AsSpan<string>(list));if (values is string[] array)return string.JoinCore(separator, new ReadOnlySpan<string>(array));}

ref struct,使用ref读取值类型,避免值类型拷贝


使用ref读取值类型,避免值类型拷贝,但要注意对当前值类型的修改,会影响被ref的那个值类型,因为本质上你在操作一个指针

ref var hierarchy = ref ph[i];
ref var words = ref hierarchy.Words;

Unsafe.IsNullRef

可以使用 Unsafe.IsNullRef 来判断一个 ref 是否为空。如果用户没有对 Foo.X 进行初始化,则默认是空引用:

ref  struct Foo {public  ref  int X;public  bool IsNull => Unsafe.IsNullRef(ref X);public  Foo(ref  int x) { X = ref x; }
}

1.4 NativeMemory

相比 Marshal.AllocHGlobal 和 Marshal.FreeHGlobal,其实现在更推荐 NativeMemory.*,有诸多好处:

  • 支持控制是否零初始化

  • 支持控制内存对齐

  • 参数是 nuint 类型,支持在 64 位进程上支持分配超过 int 上限的大小

1.5 struct 直接转换内存数据

1.5.1 C#使用struct直接转换下位机数据

数据结构
假定下位机(C语言编写)给到我们的数据结构是这个,传输方式为小端方式

typedef struct {unsigned long int time;          // 4个字节float tmpr[3];                   //  4*3 个字节float forces[6];                 //  4*6个字节float distance[6];               // 4*6个字节} dataItem_t;

方法1(略麻烦)

首先需要定义一个struct:

[StructLayout(LayoutKind.Sequential, Size = 64, Pack = 1)]
public struct HardwareData {//[FieldOffset(0)]public UInt32 Time; // 4个字节[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]//[FieldOffset(4)]public float[] Tmpr; //  3* 4个字节//[FieldOffset(16)][MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]public float[] Forces; //  6* 4个字节//[FieldOffset(40)][MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]public float[] Distance; //  6*4个字节}

然后使用以下代码进行转换

// converts byte[] to struct
public static T RawDeserialize(byte[] rawData, int position) {int rawsize = Marshal.SizeOf(typeof(T));if (rawsize > rawData.Length - position) throw new ArgumentException("Not enough data to fill struct. Array length from position: " + (rawData.Length - position) + ", Struct length: " + rawsize);IntPtr buffer = Marshal.AllocHGlobal(rawsize);Marshal.Copy(rawData, position, buffer, rawsize);T retobj = (T)Marshal.PtrToStructure(buffer, typeof(T));Marshal.FreeHGlobal(buffer);return retobj;
}// converts a struct to byte[]
public static byte[] RawSerialize(object anything) {int rawSize = Marshal.SizeOf(anything);IntPtr buffer = Marshal.AllocHGlobal(rawSize);Marshal.StructureToPtr(anything, buffer, false);byte[] rawDatas = new byte[rawSize];Marshal.Copy(buffer, rawDatas, 0, rawSize);Marshal.FreeHGlobal(buffer);return rawDatas;
}

注意这里我使用的方式为LayoutKind.Sequential,如果直接使用LayoutKind.Explicit并设置FieldOffset会弹出一个诡异的错误System.TypeLoadException:"Could not load type 'ConsoleApp3.DataItem' from assembly 'ConsoleApp3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field."。

方法2

既然用上了 unsafe,就干脆直接一点。

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct DataItem {public UInt32 time; // 4个字节public fixed float tmpr[3]; //  3* 4个字节public fixed float forces[6]; //  6* 4个字节public fixed float distance[6]; //  6*4个字节
}

这样,获得数组可以直接正常访问了。


C#使用struct直接转换下位机数据

https://blog.51cto.com/u_15127641/2754559

1.6 string.Join 内部实现解析

CollectionsMarshal.AsSpan(valuesList)

if (values is List<string?> valuesList) {return JoinCore(separator.AsSpan(), CollectionsMarshal.AsSpan(valuesList));
}
if (values is string?[] valuesArray)
{return JoinCore(separator.AsSpan(), new ReadOnlySpan<string?>(valuesArray));
}

Join

public static string Join(string? separator, IEnumerable<string?> values)
{if (values is List<string?> valuesList){return JoinCore(separator.AsSpan(), CollectionsMarshal.AsSpan(valuesList));}if (values is string?[] valuesArray){return JoinCore(separator.AsSpan(), new ReadOnlySpan<string?>(valuesArray));}if (values == null){ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values);}using (IEnumerator<string?> en = values.GetEnumerator()){if (!en.MoveNext()){return Empty;}string? firstValue = en.Current;if (!en.MoveNext()){// Only one value availablereturn firstValue ?? Empty;}// Null separator and values are handled by the StringBuildervar result = new ValueStringBuilder(stackalloc char[256]);result.Append(firstValue);do{result.Append(separator);result.Append(en.Current);}while (en.MoveNext());return result.ToString();}
}

JoinCore

private static string JoinCore(ReadOnlySpan<char> separator, ReadOnlySpan<string?> values)
{if (values.Length <= 1){return values.IsEmpty ?Empty :values[0] ?? Empty;}long totalSeparatorsLength = (long)(values.Length - 1) * separator.Length;if (totalSeparatorsLength > int.MaxValue){ThrowHelper.ThrowOutOfMemoryException();}int totalLength = (int)totalSeparatorsLength;// Calculate the length of the resultant string so we know how much space to allocate.foreach (string? value in values){if (value != null){totalLength += value.Length;if (totalLength < 0) // Check for overflow{ThrowHelper.ThrowOutOfMemoryException();}}}// Copy each of the strings into the result buffer, interleaving with the separator.string result = FastAllocateString(totalLength);int copiedLength = 0;for (int i = 0; i < values.Length; i++){// It's possible that another thread may have mutated the input array// such that our second read of an index will not be the same string// we got during the first read.// We range check again to avoid buffer overflows if this happens.if (values[i] is string value){int valueLen = value.Length;if (valueLen > totalLength - copiedLength){copiedLength = -1;break;}// Fill in the value.FillStringChecked(result, copiedLength, value);copiedLength += valueLen;}if (i < values.Length - 1){// Fill in the separator.// Special-case length 1 to avoid additional overheads of CopyTo.// This is common due to the char separator overload.ref char dest = ref Unsafe.Add(ref result._firstChar, copiedLength);if (separator.Length == 1){dest = separator[0];}else{separator.CopyTo(new Span<char>(ref dest, separator.Length));}copiedLength += separator.Length;}}// If we copied exactly the right amount, return the new string.  Otherwise,// something changed concurrently to mutate the input array: fall back to// doing the concatenation again, but this time with a defensive copy. This// fall back should be extremely rare.return copiedLength == totalLength ?result :JoinCore(separator, values.ToArray().AsSpan());
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/834712.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DM multipath总结---基于LINUX 7

DM multipath总结---基于LINUX 7 DM multipath总结DM Multipath提供的功能: 冗余:DM Multipath 能够在主动/被动配置下提供故障转移。在主动/被动配置下,只有一半的路径在每次进行 I/O 时会被使用。若一条 I/O 路径的任一元素(电缆、交换器或者控制器)出现故障,DM Multi…

如果让你来设计网络

如果让你来设计网络 你是一台电脑,你的名字叫 A 很久很久之前,你不与任何其他电脑相连接,孤苦伶仃。 ​​ 直到有一天,你希望与另一台电脑 B 建立通信,于是你们各开了一个网口,用一根网线连接了起来。 ​​ 用一根网线连接起来怎么就能"通信"了呢?我可以给你讲…

Wincc 7.5SP1下VBA编程练习:批量设置看见权限

这一篇学习笔记我在新浪发表过,那边还在审核。在这里也记录一下。 前两天QQ群里面有人询问能不能快速的给WINCC画面上的控件设置操作权限,这个是比较容易的。比如有个画面有10个IO域,在VBA编辑器写下面的脚本:Sub IOField_PropertyTrigger1() Dim objects Dim obj Dim objd…

项目冲测6

项目冲测6这个作业属于哪个课程 计科12班这个作业的要求在哪里 项目冲测 一.团队简介 队名:菜鸟队姓名 学号 团队责任巴哈尔古丽吾甫尔 3222004679 协调团队工作,对作业任务进行整理分配,整理博客工具人努日曼姑丽阿卜来孜 3222004935 指定测试计划,对软件进行测试,漏洞整…

20222407 2024-2025-1 《网络与系统攻防技术》实验六实验报告

1.实验内容 1.1 本周学习内容回顾 Metasploit 是一个功能强大的渗透测试框架,广泛应用于网络安全领域。它为安全专家、渗透测试人员和红队提供了一个全面的工具集,支持漏洞利用、攻击模拟和安全评估。Metasploit 提供了丰富的攻击模块,涵盖了远程代码执行、服务拒绝、提权等…

项目冲测5

项目冲测5这个作业属于哪个课程 计科12班这个作业的要求在哪里 项目冲测 一.团队简介 队名:菜鸟队姓名 学号 团队责任巴哈尔古丽吾甫尔 3222004679 协调团队工作,对作业任务进行整理分配,整理博客工具人努日曼姑丽阿卜来孜 3222004935 指定测试计划,对软件进行测试,漏洞整…

基于Java+SSM+JSP+MYSQL实现的宠物领养收养管理系统功能设计与实现四

三、系统设计基于SSM整合maven开发的一款宠物收养领养管理系统附带源码指导运行视频,该项目前端模板是借鉴别人的,自己写的后台代码,该系统分为前台和后台,前台功能有:登录注册、领养中心、活动中心等。后台管理员功能有:用户管理、宠物管理、活动管理、领养管理、志愿者…

项目冲测4

项目冲测4这个作业属于哪个课程 计科12班这个作业的要求在哪里 项目冲测 一.团队简介 队名:菜鸟队姓名 学号 团队责任巴哈尔古丽吾甫尔 3222004679 协调团队工作,对作业任务进行整理分配,整理博客工具人努日曼姑丽阿卜来孜 3222004935 指定测试计划,对软件进行测试,漏洞整…

此操作系统不支持 .NET Framework 4.8.1

解决办法: 安装一个4.8的离线包,下载地址:https://download.visualstudio.microsoft.com/download/pr/2d6bb6b2-226a-4baa-bdec-798822606ff1/8494001c276a4b96804cde7829c04d7f/ndp48-x86-x64-allos-enu.exe 感谢吾爱:点击跳转 终于安装上了:作者QQ4577105

MarkText使用教程-cnblog

MarkText使用教程 typora是需要付费的,就算使用破解版的,每次都要点....,可能有的时候不需要 还是觉得不好 找到开源的MarkText,个人觉得还是非常好用的 使用的方法也和typora差不多的,用习惯了就好了 下载安装 首先我们要登录github,搜索Marktext 但基本都会搜索到英文版的,对…

20222427 2024-2025-1 《网络与系统攻防技术》实验六实验报告

1.实验内容 1.1 本周学习内容回顾使用了Metasploit框架,其是一个功能强大的渗透测试框架。在使用的过程当中,Metasploit 提供了种类繁多的攻击模块,涵盖了远程代码执行、服务拒绝、提权等多种攻击方式,支持对多种操作系统和应用程序进行测试。除了漏洞利用,它还具备强大的…

ShardingSphere 如何完美驾驭分布式事务与 XA 协议?

0 前言 基于上一文基础,详细展开 ShardingSphere 分布式事务实现。先看支持强一致性事务的XAShardingTransactionManager。 1 XAShardingTransactionManager 回到 ShardingSphere,来到 sharding-transaction-xa-core 工程的 XAShardingTransactionManager 类,分布式事务的 X…