成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

站在前人的肩膀上重新透視C# Span<T>數據結構

開發 前端
Span和Memory都是包裝了可以在pipeline上使用的結構化數據的內存緩沖器,他們被設計用于在pipeline中高效傳遞數據。

先談一下我對Span的看法, Span是指向任意連續內存空間的類型安全、內存安全的視圖。

Span和Memory都是包裝了可以在pipeline上使用的結構化數據的內存緩沖器,他們被設計用于在pipeline中高效傳遞數據。

定語解讀

這里面許多定語,值得我們細細揣摩:

1. 指向任意連續內存空間:支持托管堆,原生內存、堆棧, 這個可從Span的幾個重載構造函數窺視一二。

2. 類型安全:Span 是一個泛型。

3. 內存安全: Span[1]是一個readonly ref struct數據結構,用于表征一段連續內存的關鍵屬性被設置成只讀readonly, 保證了所有的操作只能在這段內存內。

// 截取自Span源碼 
public readonly ref struct Span<T>
{
// 表征一段連續內存的關鍵屬性 Pointer & Length 都只能從構造函數賦值
/// <summary>A byref or a native ptr.</summary>
internal readonly ByReference<T> _reference;
/// <summary>The number of elements this Span contains.</summary>
private readonly int _length;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span(T[]? array)
{
if (array == null)
{
this = default;
return; // returns default
}
if (!typeof(T).IsValueType && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
_reference = new ByReference<T>(ref MemoryMarshal.GetArrayDataReference(array));
_length = array.Length;
}
}

4. 視圖:操作結果會直接體現到底層的連續內存。

至此我們來看一個簡單的用法, 利用span操作指向一段堆棧空間。

static  void  Main()
{

Span<byte> arraySpan = stackalloc byte[100]; // 包含指針和Length的只讀指針, 類似于go里面的切片

byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
arraySpan[ctr] = data++;

arraySpan.Fill(1);

var arraySum = Sum(arraySpan);
Console.WriteLine($"The sum is {arraySum}"); // 輸出100

arraySpan.Clear();

var slice = arraySpan.Slice(0,50); // 因為是只讀屬性, 內部New Span<>(), 產生新的切片
arraySum = Sum(slice);
Console.WriteLine($"The sum is {arraySum}"); // 輸出0
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static int Sum(Span<byte> array)
{
int arraySum = 0;
foreach (var value in array)
arraySum += value;

return arraySum;
}
  • 此處Span 指向了特定的堆棧空間, Fill,Clear 等操作的效果直接體現到該段內存。
  • 注意Slice切片方法,內部實質是產生新的Span,是一個新的視圖,對新span的操作會體現到原始底層數據結構。
  [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> Slice(int start)
{
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

return new Span<T>(ref Unsafe.Add(ref _reference.Value, (nint)(uint)start /* force zero-extension */), _length - start);
}

從Slice切片源碼可以看到,實質是利用原ptr & length 產生包含新的ptr & length的操作視圖, ptr其實是指針的移動,也就是定位新的數據塊, 但是終歸是在原始數據塊內部。

衍生技能點

我們再細看Span的定義, 有幾個關鍵詞建議大家溫故而知新。

1. readonly strcut[2]

從C#7.2開始,你可以將readonly作用在struct上,指示該struct不可改變。

span 被定義為readonly struct,內部屬性自然也是readonly,從上面的分析和實例看我們可以針對Span表征的特定連續內存空間做內容更新操作;

如果想限制更新該連續內存空間的內容, C#提供了ReadOnlySpan類型, 該類型強調該塊內存只讀,也就是不存在Span 擁有的Fill,Clear等方法。

一線碼農大佬寫了文章講述[使用span對字符串求和]的姿勢,大家都說使用span能高效操作內存,我們對該用例BenchmarkDotNet壓測。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Buffers;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace ConsoleApp3
{
public class Program
{
static void Main()
{
var summary = BenchmarkRunner.Run<MemoryBenchmarkerDemo>();
}
}

[MemoryDiagnoser,RankColumn]
public class MemoryBenchmarkerDemo
{
int NumberOfItems = 100000;

// 對字符串切割, 會產生字符串小對象
[Benchmark]
public void StringSplit()
{
for (int i = 0; i < NumberOfItems; i++)
{
var s = "97 3";

var arr = s.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
var num1 = int.Parse(arr[0]);
var num2 = int.Parse(arr[1]);

_ = num1 + num2;
}

}

// 對底層字符串切片
[Benchmark]
public void StringSlice()
{
for (int i = 0; i < NumberOfItems; i++)
{
var s = "97 3";
var position = s.IndexOf(' ');
ReadOnlySpan<char> span = s.AsSpan();
var num1 = int.Parse(span.Slice(0, position));
var num2 = int.Parse(span.Slice(position));

_= num1+ num2;

}
}
}
}

壓測解讀:

對字符串運行時切分,不會利用駐留池,于是case1會分配大量小對象;

case2對底層字符串切片,雖然會產生不同的透視對象Span, 但是實際引用了的原始內存塊的偏移區間, 不存在分配新內存。

2. ref struct[3]

從C#7.2開始,ref可以作用在struct,指示該類型被分配在堆棧上,并且不能轉義到托管堆。

Span,ReadonlySpan 包裝了對于任意連續內存快的透視操作,但是只能被存儲堆棧上,不適用于一些場景,例如異步調用,.NET Core 2.1為此新增了Memory[4] , ReadOnlyMemory, 可以被存儲在托管堆上,這個暫時按下不表。

最后用一張圖總結, 本文成文,感謝[ yi念之間 ]大佬參與討論。

引用鏈接

[1] Span: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Span.cs

[2] readonly strcut: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#readonly-struct

[3] ref struct: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct

[4] Memory: https://docs.microsoft.com/en-us/dotnet/standard/memory-and-spans/memory-t-usage-guidelines

責任編輯:武曉燕 來源: 精益碼農
相關推薦

2011-03-31 13:13:24

編程

2015-06-04 17:26:26

2009-08-03 17:38:12

排序算法C#數據結構

2013-05-30 09:53:04

阿里金融阿里巴巴大數據

2014-04-02 12:57:55

袁學鋒HPC天河二號

2017-09-03 13:17:27

深度學習計算機視覺卷積神經網絡

2009-08-12 18:35:17

C#數據結構

2013-09-02 11:33:38

百度

2009-08-11 14:43:42

C#數據結構與算法

2025-01-07 08:20:00

2009-08-11 14:51:11

C#數據結構與算法

2009-08-13 18:34:49

C#數據結構和算法

2024-01-29 00:20:00

GolangGo代碼

2009-06-24 09:52:21

哈希表

2009-08-11 14:30:32

C#數據結構與算法

2018-03-09 11:25:09

微信

2023-12-15 10:11:31

數據結構方式

2009-08-11 14:14:42

C#數據結構與算法

2009-08-19 11:09:00

C# Cast<T>

2009-08-11 14:36:17

C#數據結構與算法線性表
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩有码在线观看 | 日日干日日射 | 久草视频在线播放 | 日韩一区二区三区在线看 | 欧美日韩不卡 | 欧美毛片免费观看 | 91高清在线视频 | 日韩在线观看一区二区三区 | 国产日韩欧美91 | 欧美日韩1区2区3区 欧美久久一区 | 精品乱码一区二区 | 91精品国产一区二区三区动漫 | 日韩视频免费看 | 另类视频在线 | 男人的天堂久久 | 亚洲精品1区 | 一区二区三区欧美在线观看 | 国产一区二区精品在线 | 国产成人精品免费视频大全最热 | 亚洲国产日韩欧美 | 欧美日韩亚洲系列 | 岛国视频| www.婷婷 | 国产成人精品在线播放 | 国产亚洲欧美另类一区二区三区 | 黄色在线免费观看 | 午夜看片网站 | aaaaaa大片免费看最大的 | 国产精品www | 国产伦一区二区三区四区 | 一区二区国产精品 | 久久tv在线观看 | 精品久久久久久亚洲精品 | 国产农村妇女毛片精品久久麻豆 | 日韩成人精品视频 | 亚洲一区二区视频 | 色婷婷影院 | 欧美激情国产日韩精品一区18 | 国产精品国产精品国产专区不片 | 91九色视频 | 自拍偷拍视频网 |