簡體   English   中英

跨度有什么區別<t>和 Memory<t> 在 C# 7.2?</t></t>

[英]What is the difference between Span<T> and Memory<T> in C# 7.2?

C# 7.2 引入了兩種新類型: Span<T>Memory<T> ,它們比早期的 C# 類型(如string[]具有更好的性能。

問題: Span<T>Memory<T>有什么區別? 為什么我要用一個而不是另一個?

Span<T>本質上是堆棧,而堆Memory<T>可以存在。

Span<T>是我們添加到平台的新類型,用於表示任意內存的連續區域,其性能特征與T []相同。 它的API類似於數組,但與數組不同,它可以指向托管或本機內存,也可以指向堆棧上分配的內存。

Memory <T>是一種補充Span<T>的類型。 如其設計文檔中所述, Span<T>是僅堆棧類型。 Span<T>的僅堆棧特性使其不適用於需要在堆上存儲對緩沖區(用Span<T>表示)的引用的許多場景,例如,對於執行異步調用的例程。

async Task DoSomethingAsync(Span<byte> buffer) {
    buffer[0] = 0;
    await Something(); // Oops! The stack unwinds here, but the buffer below
                       // cannot survive the continuation.
    buffer[0] = 1;
}

為了解決這個問題,我們將提供一組互補類型,旨在用作通用交換類型,就像Span <T> ,一系列任意內存,但與Span <T>這些類型不會是堆棧 - 只是以讀取和寫入內存的重大性能損失為代價。

async Task DoSomethingAsync(Memory<byte> buffer) {
    buffer.Span[0] = 0;
    await Something(); // The stack unwinds here, but it's OK as Memory<T> is
                       // just like any other type.
    buffer.Span[0] = 1;
}

在上面的示例中, Memory <byte>用於表示緩沖區。 它是常規類型,可用於執行異步調用的方法。 其Span屬性返回Span<byte> ,但在異步調用期間返回的值不會存儲在堆上,而是從Memory<T>值生成新值。 從某種意義上說, Memory<T>Span<T>的工廠。

參考文件: 這里

re:這意味着它只能指向堆棧上分配的內存。

Span<T>可以指向任何內存:在堆棧或堆上分配。 Span<T>的僅堆棧特性意味着Span<T>本身(不是它指向的內存)必須僅駐留在堆棧上。 這與“普通”C#結構形成對比,后者可以駐留在堆棧上或堆上(通過值類型裝箱,或者當它們嵌入在類/引用類型中時)。 一些更明顯的實際意義是你不能在類中有一個Span<T>字段,你不能將Span<T>包裝起來,並且你不能創建它們的數組。

一般的

Span 定義引用結構Memory 定義結構

這是什么意思?

  • 引用結構不能存儲在堆上,編譯器會阻止您這樣做,因此不允許以下內容:

    • 使用Span作為 class 中的字段
    • 異步方法中使用Span ,(異步方法正在成熟的 state 機器中擴展)
    • 還有更多,這里是引用結構無法完成的事情的完整列表
  • stackalloc不能與Memory一起使用(因為不能保證它不會存儲在堆上)但可以與Span一起使用

    // this is legit Span<byte> data = stackalloc byte[256]; // legit // compile time error: Conversion of a stackalloc expression of type 'byte' to type 'Memory<byte>' is not possible. Memory<byte> data = stackalloc byte[256];

這意味着什么?

這意味着在某些情況下, Span本身無法進行各種微優化,因此應該使用Memory代替。

例子:

這是一個字符串分配自由Split方法的示例,它適用於ReadOnlyMemory結構,在Span上實現它真的很困難,因為Span是一個引用結構並且不能放入數組IEnumerable中:

(實施摘自C# in a nutshell book

IEnumerable<ReadOnlyMemory<char>> Split(ReadOnlyMemory<char> input)
{
    int wordStart = 0;
    for (int i = 0; i <= input.Length; i++)
    {
        if (i == input.Length || char.IsWhiteSpace(input.Span[i]))
        {
            yield return input.Slice(wordStart, i);
            wordStart = i + 1;
        }
    }
}

這是通過do.net 基准庫針對常規Split方法在 .NET SDK=6.0.403 上進行的非常簡單的基准測試的結果。

|                Method |     StringUnderTest |              Mean |             Error |            StdDev |      Gen0 |      Gen1 |     Gen2 |  Allocated |
|---------------------- |-------------------- |------------------:|------------------:|------------------:|----------:|----------:|---------:|-----------:|
|          RegularSplit |                meow |         13.194 ns |         0.2891 ns |         0.3656 ns |    0.0051 |         - |        - |       32 B |
| SplitOnReadOnlyMemory |                meow |          8.991 ns |         0.1981 ns |         0.2433 ns |    0.0127 |         - |        - |       80 B |
|          RegularSplit | meow(...)meow [499] |      1,077.807 ns |        21.2291 ns |        34.8801 ns |    0.6409 |    0.0095 |        - |     4024 B |
| SplitOnReadOnlyMemory | meow(...)meow [499] |          9.036 ns |         0.2055 ns |         0.2366 ns |    0.0127 |         - |        - |       80 B |
|          RegularSplit | meo(...)eow [49999] |    121,740.719 ns |     2,221.3079 ns |     2,077.8128 ns |   63.4766 |   18.5547 |        - |   400024 B |
| SplitOnReadOnlyMemory | meo(...)eow [49999] |          9.048 ns |         0.2033 ns |         0.2782 ns |    0.0127 |         - |        - |       80 B |
|          RegularSplit | me(...)ow [4999999] | 67,502,918.403 ns | 1,252,689.2949 ns | 2,092,962.4006 ns | 5625.0000 | 2375.0000 | 750.0000 | 40000642 B |
| SplitOnReadOnlyMemory | me(...)ow [4999999] |          9.160 ns |         0.2057 ns |         0.2286 ns |    0.0127 |         - |        - |       80 B |

這些方法的輸入是“meow”字符串重復 1、100、10_000 和 1_000_000 次,我的基准設置並不理想,但它顯示了差異。

Memory<T>可以看作是一個不安全但更通用的Span<T>版本。 如果指向已釋放的數組,則對Memory<T>對象的訪問將失敗。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM