stackalloc
使用栈内存,减少GC压力
var wordMatchCounts = stackalloc float[wordCount];
Span
Span 支持 reinterpret_cast 的理念,即可以将 Span 强制转换为 Span
Span 支持 reinterpret_cast 的理念,即可以将 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 allocation
ValueListBuilder<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 available
return firstValue ?? Empty;
}
// Null separator and values are handled by the StringBuilder
var 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());
}
标签:span,string,NativeMemory,Length,separator,内存,values,ref,public
From: https://www.cnblogs.com/darklx/p/18549678