自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Span 在網(wǎng)絡(luò)編程中可以提供高性能的內(nèi)存訪問和數(shù)據(jù)處理能力

開發(fā) 前端
在 .NET 中,許多常見的數(shù)據(jù)類型,如 `string`、`array` 等,都是引用類型,它們本身并不包含實(shí)際的數(shù)據(jù),而是在堆上分配了一塊內(nèi)存來存儲(chǔ)數(shù)據(jù),然后將其地址傳遞給變量。

`Span<T>` 是 .NET Core 2.1 引入的一個(gè)新類型,它提供任意內(nèi)存的連續(xù)區(qū)域的類型安全和內(nèi)存安全表示形式。`Span<T>` 可以與任意的值類型或引用類型進(jìn)行關(guān)聯(lián),包括原始內(nèi)存指針、數(shù)組、堆上對(duì)象等。通過 `Span<T>`,我們可以對(duì)這些數(shù)據(jù)結(jié)構(gòu)進(jìn)行高效的讀取和寫入操作,而無需進(jìn)行拷貝或者分配額外的內(nèi)存。

在 .NET 中,許多常見的數(shù)據(jù)類型,如 `string`、`array` 等,都是引用類型,它們本身并不包含實(shí)際的數(shù)據(jù),而是在堆上分配了一塊內(nèi)存來存儲(chǔ)數(shù)據(jù),然后將其地址傳遞給變量。這種設(shè)計(jì)在很多情況下非常方便,但也會(huì)帶來一些性能上的問題,比如頻繁的內(nèi)存分配和釋放、GC 壓力等。`Span<T>` 的出現(xiàn)為解決這些問題提供了一種新的方式。

使用 `Span<T>`,我們可以盡可能地避免進(jìn)行內(nèi)存分配和復(fù)制,從而提高代碼的運(yùn)行效率。同時(shí),由于 `Span<T>` 只是一個(gè)“視圖”,它并不會(huì)改變?cè)紨?shù)據(jù)的內(nèi)容或生命周期,因此也非常安全可靠。在 .NET Core 中,許多常見的 API(如網(wǎng)絡(luò)、IO、序列化等)都已經(jīng)開始支持 `Span<T>`,這為我們編寫高性能、低延遲的代碼提供了更多的可能性。

Span是如何實(shí)現(xiàn)的?

通常不需要了解他們正在使用的庫是如何實(shí)現(xiàn)的。但是,就 Span<T> 而言,至少對(duì)其背后的細(xì)節(jié)有一個(gè)基本的了解是值得的,因?yàn)檫@些細(xì)節(jié)暗示了其性能和使用限制。

首先,Span<T> 是一個(gè)包含 ref 和長度的值類型,定義大致如下:

public readonly ref struct Span<T>
{
  private readonly ref T _pointer;
  private readonly int _length;
  ...
}

引用 T 字段的概念一開始可能很奇怪,事實(shí)上,實(shí)際上無法在 C# 甚至 MSIL 中聲明引用 T 字段。但 Span<T> 實(shí)際上是為在運(yùn)行時(shí)中使用一種特殊的內(nèi)部類型而編寫的,該類型被視為實(shí)時(shí) (JIT) 內(nèi)部類型,JIT 為其生成等效的 ref T 字段??紤]一個(gè)可能更熟悉的 ref 用法:

public static void AddOne(ref int value) => value += 1;
...
var values = new int[] { 42, 84, 126 };
AddOne(ref values[2]);
Assert.Equal(127, values[2]);

此代碼通過引用傳遞數(shù)組中的插槽,這樣(撇開優(yōu)化不談)堆棧上有一個(gè) ref T。Span<T> 中的 ref T 是相同的想法,只是封裝在一個(gè)結(jié)構(gòu)中。直接或間接包含此類 ref 的類型稱為類似 ref 的類型,C# 7.2 編譯器允許通過在簽名中使用 ref 結(jié)構(gòu)來聲明此類類似 ref 的類型。

從這個(gè)簡短的描述中,應(yīng)該清楚兩件事:

  • Span<T> 的定義方式使操作可以像數(shù)組一樣高效:索引到 span 中不需要計(jì)算來確定指針的起點(diǎn)及其起始偏移量,因?yàn)?ref 字段本身已經(jīng)封裝了兩者。(相比之下,ArraySegment<T> 具有單獨(dú)的偏移字段,因此索引和傳遞的成本更高。
  • Span<T> 作為類似 ref 類型的性質(zhì),由于其 ref T 字段而帶來了一些約束。

Span使用注意:

Span<T> 是在堆棧而不是托管堆上分配的 ref 結(jié)構(gòu) 。 Ref 結(jié)構(gòu)類型有許多限制,以確保它們不能提升到托管堆,包括不能裝箱、不能分配給 類型的Objectdynamic變量或任何接口類型,它們不能是引用類型中的字段,也不能跨 await 和 yield 邊界使用。 此外,對(duì)和 兩個(gè)NotSupportedException方法的 Equals(Object)GetHashCode調(diào)用會(huì)引發(fā) 。

因?yàn)樗莾H堆棧類型, Span<T> 不適用于許多需要存儲(chǔ)對(duì)堆上的緩沖區(qū)的引用的方案。 例如,進(jìn)行異步方法調(diào)用的例程也是如此。 對(duì)于此類方案,可以使用互補(bǔ) System.Memory<T> 和 System.ReadOnlyMemory<T> 類型。

Span的應(yīng)用場景

Span<T> 可以看作是一個(gè)指向連續(xù)內(nèi)存塊的引用,它可以用于訪問數(shù)組、堆棧、堆等數(shù)據(jù)結(jié)構(gòu)中的連續(xù)元素。Span<T> 對(duì)象本身不會(huì)分配或釋放任何內(nèi)存,因此它非常適用于內(nèi)存密集型的應(yīng)用場景,例如網(wǎng)絡(luò)編程、高性能計(jì)算等。

以下是一些 Span<T> 的常見應(yīng)用場景:

  • 數(shù)組操作:Span<T> 可以用于訪問和操作數(shù)組中的元素,包括讀取、修改、排序等操作。與傳統(tǒng)的數(shù)組訪問方式相比,Span<T> 更加靈活和高效,可以有效地減少內(nèi)存分配和拷貝的開銷。
  • 文件操作:Span<T> 可以用于讀取和寫入文件中的二進(jìn)制數(shù)據(jù)。通過使用 MemoryMappedFile 和 Span<T>,可以實(shí)現(xiàn)高效的文件讀寫操作,并且避免了不必要的內(nèi)存分配和拷貝。
  • 網(wǎng)絡(luò)編程:Span<T> 可以用于訪問網(wǎng)絡(luò)數(shù)據(jù)包中的二進(jìn)制數(shù)據(jù),例如解析 TCP/IP 數(shù)據(jù)包、HTTP 請(qǐng)求等。通過使用 Span<T>,可以避免數(shù)據(jù)拷貝和內(nèi)存分配的開銷,從而提高網(wǎng)絡(luò)編程的性能和效率。
  • 高性能計(jì)算:Span<T> 可以用于訪問和操作大型數(shù)組或矩陣中的元素。通過使用 Span<T>,可以避免不必要的內(nèi)存分配和拷貝,提高計(jì)算速度和效率。

如何使用Span

在 .NET 中,可以通過以下幾種方式來創(chuàng)建 Span<T> 對(duì)象:

直接使用原始內(nèi)存指針

unsafe
{
    int[] array = { 1, 2, 3, 4 };
    fixed (int* ptr = array)
    {
        Span<int> span = new Span<int>(ptr, array.Length);
        // 對(duì) span 進(jìn)行操作
    }
}

在這個(gè)例子中,我們首先通過 fixed 關(guān)鍵字將 array 數(shù)組的地址固定下來,然后使用 new Span<int>(ptr, array.Length) 構(gòu)造函數(shù)創(chuàng)建一個(gè) Span<int> 對(duì)象,該對(duì)象引用了整個(gè) array 數(shù)組。

使用數(shù)組

int[] array = { 1, 2, 3, 4 };
Span<int> span = new Span<int>(array);
// 對(duì) span 進(jìn)行操作

在這個(gè)例子中,我們直接使用 array 數(shù)組創(chuàng)建了一個(gè) Span<int> 對(duì)象,該對(duì)象引用了整個(gè)數(shù)組。

使用數(shù)組的一部分

int[] array = { 1, 2, 3, 4 };
Span<int> span = new Span<int>(array, 1, 2);
// 對(duì) span 進(jìn)行操作

在這個(gè)例子中,我們使用 new Span<int>(array, 1, 2) 構(gòu)造函數(shù)創(chuàng)建了一個(gè) Span<int> 對(duì)象,該對(duì)象引用了 array 數(shù)組的第二個(gè)元素和第三個(gè)元素。

使用字符串

string str = "hello world";
Span<char> span = str.AsSpan();
// 對(duì) span 進(jìn)行操作

在這個(gè)例子中,我們使用 AsSpan 方法將一個(gè)字符串轉(zhuǎn)換為 Span<char> 對(duì)象,該對(duì)象引用了字符串的所有字符。

除了上述方式外,還可以使用 Memory<T> 或者 ReadOnlyMemory<T> 類型來創(chuàng)建 Span<T> 對(duì)象。Memory<T> 表示一個(gè)可變的內(nèi)存區(qū)域,而 ReadOnlyMemory<T> 表示一個(gè)不可變的內(nèi)存區(qū)域,它們都可以用來創(chuàng)建 Span<T> 對(duì)象。例如:

int[] array = { 1, 2, 3, 4 };
Memory<int> memory = new Memory<int>(array);
Span<int> span = memory.Span;
// 對(duì) span 進(jìn)行操作

在這個(gè)例子中,我們首先使用 new Memory<int>(array) 構(gòu)造函數(shù)創(chuàng)建了一個(gè) Memory<int> 對(duì)象,然后使用 Span 屬性獲取了其對(duì)應(yīng)的 Span<int> 對(duì)象。

使用 Span<T> 可以避免數(shù)據(jù)拷貝和內(nèi)存分配的開銷,從而提高網(wǎng)絡(luò)編程的性能和效率。一般情況下,網(wǎng)絡(luò)數(shù)據(jù)包的二進(jìn)制數(shù)據(jù)往往是連續(xù)存儲(chǔ)在內(nèi)存中的,Span<T> 可以直接引用該內(nèi)存塊,而不需要進(jìn)行額外的拷貝操作。

Span在網(wǎng)絡(luò)編程中的應(yīng)用

使用 Span<T> 解析網(wǎng)絡(luò)數(shù)據(jù)包的一般步驟:

  • 從網(wǎng)絡(luò)中接收到數(shù)據(jù):使用網(wǎng)絡(luò)編程庫(如Socket)接收網(wǎng)絡(luò)數(shù)據(jù),將數(shù)據(jù)存儲(chǔ)在一個(gè)字節(jié)數(shù)組或內(nèi)存緩沖區(qū)中。
  • 創(chuàng)建 Span<T> 對(duì)象:通過將字節(jié)數(shù)組或內(nèi)存緩沖區(qū)傳遞給 Span<T> 的構(gòu)造函數(shù),創(chuàng)建一個(gè) Span<T> 對(duì)象。例如:`Span<byte> dataSpan = new Span<byte>(dataBuffer);`
  • 解析數(shù)據(jù):利用 Span<T> 的索引和切片功能,可以方便地訪問和解析二進(jìn)制數(shù)據(jù)。可以通過索引獲取特定位置的字節(jié),也可以使用切片操作獲取指定范圍的字節(jié)。例如:`byte firstByte = dataSpan[0];` 或 `Span<byte> headerSpan = dataSpan.Slice(0, headerLength);`
  • 處理數(shù)據(jù):根據(jù)具體的網(wǎng)絡(luò)協(xié)議,對(duì)二進(jìn)制數(shù)據(jù)進(jìn)行解析和處理,提取需要的信息。可以使用 Span<T> 提供的方法或自定義的處理邏輯進(jìn)行操作。

Span和網(wǎng)絡(luò)編程的結(jié)合

`Span<T>` 在網(wǎng)絡(luò)編程中可以提供高性能的內(nèi)存訪問和數(shù)據(jù)處理,從而提升網(wǎng)絡(luò)應(yīng)用程序的效率。下面是幾個(gè)使用 `Span<T>` 進(jìn)行網(wǎng)絡(luò)編程的常見場景:

數(shù)據(jù)接收和解析:使用 `Socket` 接收到的字節(jié)數(shù)據(jù)可以直接轉(zhuǎn)換為 `Span<byte>`,避免了額外的內(nèi)存拷貝操作。然后,可以使用 `Span<T>` 提供的方法對(duì)數(shù)據(jù)進(jìn)行解析,例如檢查數(shù)據(jù)包的長度、提取字段值等。

byte[] buffer = new byte[1024];

int bytesRead = socket.Receive(buffer); // 從 Socket 接收數(shù)據(jù)

Span<byte> data = buffer.AsSpan(0, bytesRead);

// 解析數(shù)據(jù)包...`

數(shù)據(jù)發(fā)送:使用 `Span<T>` 可以直接將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)中,而無需將數(shù)據(jù)復(fù)制到新的緩沖區(qū)中。這樣可以避免內(nèi)存拷貝的開銷,提高發(fā)送數(shù)據(jù)的效率。

byte[] data = GetPacketData(); // 獲取待發(fā)送的數(shù)據(jù)

socket.Send(data.AsSpan()); // 直接發(fā)送數(shù)據(jù)

數(shù)據(jù)處理和轉(zhuǎn)換:在網(wǎng)絡(luò)通信中,涉及到各種數(shù)據(jù)格式的轉(zhuǎn)換和處理操作。使用 `Span<T>` 可以方便地對(duì)字節(jié)數(shù)據(jù)進(jìn)行解析、轉(zhuǎn)換和修改。

byte[] receivedData = ReceiveDataFromSocket(); // 從 Socket 接收數(shù)據(jù)

// 將接收到的數(shù)據(jù)轉(zhuǎn)換為字符串

string message = Encoding.UTF8.GetString(receivedData.AsSpan());

// 修改數(shù)據(jù)并發(fā)送回去

receivedData.AsSpan().Reverse(); // 反轉(zhuǎn)字節(jié)順序

SendDataToSocket(receivedData);`

緩沖區(qū)池化:在高并發(fā)的網(wǎng)絡(luò)應(yīng)用程序中,使用緩沖區(qū)池化技術(shù)可以避免頻繁的內(nèi)存分配和釋放操作,提高性能。`Span<T>` 可以與緩沖區(qū)池化技術(shù)相結(jié)合,共享和重用緩沖區(qū),減少內(nèi)存開銷。

ArrayPool<byte> bufferPool = ArrayPool<byte>.Shared;

byte[] buffer = bufferPool.Rent(1024); // 從緩沖區(qū)池中租借一個(gè)緩沖區(qū)

int bytesRead = socket.Receive(buffer); // 從 Socket 接收數(shù)據(jù)

Span<byte> data = buffer.AsSpan(0, bytesRead);

// 處理接收到的數(shù)據(jù)...

bufferPool.Return(buffer); // 將緩沖區(qū)歸還給緩沖區(qū)池`

通過合理地利用 `Span<T>` 的特性和方法,我們可以在網(wǎng)絡(luò)編程中實(shí)現(xiàn)高效的數(shù)據(jù)處理和傳輸,提升網(wǎng)絡(luò)應(yīng)用程序的性能和可伸縮性。但需要注意的是,使用 `Span<T>` 時(shí)要小心懸掛指針和內(nèi)存安全問題,確保操作的內(nèi)存是有效的并且不會(huì)被修改。

使用Socket和Span結(jié)合示例

使用 Socket 和 Span<T> 進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)包解析是一種高效、低內(nèi)存消耗的方式。下面是一個(gè)簡單的示例,演示如何使用這兩個(gè)類型進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)包解析:

// 假設(shè)已經(jīng)建立了一個(gè) TCP 連接,并且從 Socket 接收到了一段字節(jié)數(shù)據(jù)

byte[] buffer = new byte[1024]; // 接收數(shù)據(jù)的緩沖區(qū)
int bytesRead = socket.Receive(buffer); // 從 Socket 接收數(shù)據(jù)

Span<byte> data = buffer.AsSpan(0, bytesRead); // 將接收到的字節(jié)數(shù)據(jù)轉(zhuǎn)換為 Span<byte>

// 解析數(shù)據(jù)包
while (data.Length > 0)
{
    // 檢查數(shù)據(jù)包的長度是否足夠
    if (data.Length < sizeof(int))
    {
        // 數(shù)據(jù)不完整,等待下一次接收
        break;
    }

    // 讀取數(shù)據(jù)包的長度
    int packetLength = BitConverter.ToInt32(data);
    
    // 檢查數(shù)據(jù)包是否完整
    if (data.Length < packetLength + sizeof(int))
    {
        // 數(shù)據(jù)不完整,等待下一次接收
        break;
    }
    
    // 提取數(shù)據(jù)包內(nèi)容
    Span<byte> packetData = data.Slice(sizeof(int), packetLength);

    // 處理數(shù)據(jù)包
    ProcessPacket(packetData);

    // 調(diào)整指針,繼續(xù)處理剩余數(shù)據(jù)
    data = data.Slice(sizeof(int) + packetLength);
}

在上面的示例中,我們首先從 Socket 接收數(shù)據(jù),并將接收到的字節(jié)數(shù)據(jù)存儲(chǔ)在一個(gè)字節(jié)數(shù)組中。然后,我們使用 AsSpan 方法將字節(jié)數(shù)組轉(zhuǎn)換為 Span<byte>,以便進(jìn)行高效的數(shù)據(jù)包解析。

接下來,我們使用一個(gè)循環(huán)來處理數(shù)據(jù)包。在每次循環(huán)中,我們首先檢查是否有足夠的數(shù)據(jù)來讀取數(shù)據(jù)包的長度。如果數(shù)據(jù)不完整,我們等待下一次接收。如果有足夠的數(shù)據(jù),我們讀取數(shù)據(jù)包的長度,并檢查是否有足夠的數(shù)據(jù)來完整解析數(shù)據(jù)包。如果數(shù)據(jù)不完整,我們等待下一次接收。

一旦我們獲得了完整的數(shù)據(jù)包,我們可以使用 Slice 方法提取數(shù)據(jù)包的內(nèi)容,并進(jìn)行相應(yīng)的處理操作(例如解析數(shù)據(jù)、驗(yàn)證數(shù)據(jù)、處理數(shù)據(jù)等)。

通過使用 Span<T> 和適當(dāng)?shù)难h(huán)邏輯,我們可以高效地解析網(wǎng)絡(luò)數(shù)據(jù)包,減少內(nèi)存拷貝和數(shù)據(jù)處理的開銷,從而提高網(wǎng)絡(luò)應(yīng)用程序的性能和效率。

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2016-12-11 10:08:31

高性能組網(wǎng)能力

2020-11-06 18:51:17

LinuxTCP服務(wù)器

2023-11-01 11:51:08

Linux性能優(yōu)化

2018-03-08 12:17:38

大數(shù)據(jù)HPCHadoop

2023-11-01 11:59:13

2023-12-13 10:22:04

APISpark數(shù)據(jù)

2024-03-18 13:43:20

Linux架構(gòu)

2018-08-14 11:05:25

2013-11-08 10:12:07

2023-11-01 10:38:46

Linux高性能網(wǎng)絡(luò)編程

2024-01-29 08:26:13

Span高性能數(shù)組數(shù)據(jù)結(jié)構(gòu)

2020-02-06 13:40:35

編程緩存優(yōu)化

2024-04-01 10:07:47

應(yīng)用程序數(shù)據(jù)數(shù)據(jù)庫

2023-12-18 11:21:40

MongoDB數(shù)據(jù)庫

2013-12-16 17:17:01

OpenMp數(shù)據(jù)處理

2011-09-01 15:12:43

SQL ServerHadoop

2018-10-08 15:22:36

IO模型

2010-01-21 16:12:28

NetFlow交換

2013-09-03 09:14:12

2023-11-01 10:58:31

系統(tǒng)調(diào)用高性能網(wǎng)絡(luò)編程Linux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)