`FileStream` 中 `byte[]` 应用 `AsMemory()` 的目的

huangapple go评论54阅读模式
英文:

Purpose of `AsMemory()` applied to `byte[]` in `FileStream`

问题

我注意到在代码修订过程中,我收到了使用扩展方法 AsMemory(...)的建议。我以前从未见过它,也没有理由考虑过它。尽管如此,一个人要么学习新知识,要么被淘汰,所以我产生了兴趣。

在阅读了(相当有限的)文档和(同样贫乏的)一系列博客之后(老实说,这篇是我唯一找到的稍微相关的),我决定我对此一无所知,无法作出判断。

以下是使用的代码示例。

await using FileStream input = new(path, FileMode.Open);
byte[] buffer = new byte[1024];
long remaining = input.Length;
int index = 0;

do
{
  int read = await input.ReadAsync(buffer, 0, Config.Chunk);
  remaining -= read;
  await using FileStream output = new(destination, FileMode.CreateNew);
  await output.WriteAsync(buffer, 0, read);
} while (remaining > 0);
input.Close();

在从流中进行这两种读取操作之间有什么实际区别?我在哪里可以找到更相关和可靠的信息?

int read1 = await input.ReadAsync(buffer, 0, Config.Chunk);
int read2 = await input.ReadAsync(buffer.AsMemory(0, Config.Chunk));

我强烈希望这与性能优化和/或线程管理有关。然而,我也希望自己英俊幽默,但这是一个错误... 对于写入操作,同样的问题也存在。

await output.WriteAsync(buffer, 0, read);
await output.WriteAsync(buffer.AsMemory(0, read));
英文:

I'm revising the code and notice that I get a suggestion to use the extension method AsMemory(...). I've never seen it before nor ha a reason to consider it. Never the less, one's either learn och obsoletes, so I got intrigued.

After reading the (rather limited) docs and (equally poor) set of blogs (honestly, this one) was the only remotely relevant I found, I decided I'm too ignorant to make a judgement call on this.

This is a sample of the code used.

await using FileStream input = new(path, FileMode.Open);
byte[] buffer = new byte[1024];
long remaining = input.Length;
int index = 0;

do
{
  int read = await input.ReadAsync(buffer, 0, Config.Chunk);
  remaining -= read;
  await using FileStream output = new(destination, FileMode.CreateNew);
  await output.WriteAsync(buffer, 0, read);
} while (remaining > 0);
input.Close();

What's the practical difference between those two readings from a stream? Where can I find more relevant and reliable information?

int read1 = await input.ReadAsync(buffer, 0, Config.Chunk);
int read2 = await input.ReadAsync(buffer.AsMemory(0, Config.Chunk));

I strongly hope that it has to do with performance optimization and/or thread management. However, I also hope to be handsome and funny, which was a mistake...

The same question goes for the writing operation, I guess.

await output.WriteAsync(buffer, 0, read);
await output.WriteAsync(buffer.AsMemory(0, read));

答案1

得分: 2

Memory<T>(更具体地说,Span<T>,因为内存基本上是“我可以随时给你一个跨度”的 API,因为跨度有非常具体的使用要求)基本上是将 T[] 泛化的一种方法,使其能够引用任何连续的内存块,而不仅仅是数组。将数组视为内存本身并不能为你带来太多好处;真正的收益在于能够使用其他类型的内存(可能是数组,也可能不是)通过相同的 API,通常甚至不需要知道底层的内存存储方式。实际上,我敢打赌,基于内存的 API 要做的第一件事就是问:“这实际上是一个数组吗?如果是,就使用另一个重载” - 所以实际上你可能只是在这里添加了一个(尽管非常快速的)包装+解包操作(尽管至少它不涉及任何分配)。

所以:如果你不需要这个功能,不用担心,只需使用数组就可以了。如果你对非基于数组的内存位置感兴趣,那么现在你知道你有一个允许这样做的 API。

这主要对库和框架开发人员有用。

英文:

Memory&lt;T&gt; (or more specifically, Span&lt;T&gt;, with memory basically being a "I can give you a span on demand", API, since span has very specific usage requirements) is basically a way of generalizing T[] to allow it to refer to any contiguous block of memory, not just arrays. treating an array as a memory won't give you much by itself; the real gain is being able to use other memory kinds (which might happen to be arrays, or might not) via the same API, usually without even needing to know the underlying memory store involved. In fact, I'd wager that the first thing the memory-based API is going to do is ask "is this actually an array? if so, use the other overload" - so it might actually be that you're adding a (albeit very fast) wrap+unwrap here (although at least it doesn't involve any allocations).

So: if you don't need that: don't worry about it, just use arrays and you'll be fine. If you are interested in non-array-based memory locations, then: it is great to know that you now have an API that allows that.

This is mostly useful to library and framework folks.

答案2

得分: 2

在这种情况下,不需要。

重载方法的作用与您自己的代码完全相同:它从buffer数组创建一个ReadOnlyMemory

源代码在这里:(最终到达一个名为FileStreamStrategy的类)

public sealed override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =&gt;
    WriteAsync(new ReadOnlyMemory&lt;byte&gt;(buffer, offset, count), cancellationToken).AsTask();

public sealed override ValueTask WriteAsync(ReadOnlyMemory&lt;byte&gt; source, CancellationToken cancellationToken)
{
    long writeOffset = CanSeek ? Interlocked.Add(ref _filePosition, source.Length) - source.Length : -1;
    return RandomAccess.WriteAtOffsetAsync(_fileHandle, source, writeOffset, cancellationToken, this);
}

尽管如此,在许多情况下它是有用的。这通常是您会从其他地方获取Memory的地方,例如来自非托管内存块,或者来自某种数组池。

英文:

None, in this case.

The overload does exactly the same thing as your own code: it creates a ReadOnlyMemory from the buffer array.

Source code is here: (it eventually reaches a class called FileStreamStrategy)

public sealed override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =&gt;
    WriteAsync(new ReadOnlyMemory&lt;byte&gt;(buffer, offset, count), cancellationToken).AsTask();

public sealed override ValueTask WriteAsync(ReadOnlyMemory&lt;byte&gt; source, CancellationToken cancellationToken)
{
    long writeOffset = CanSeek ? Interlocked.Add(ref _filePosition, source.Length) - source.Length : -1;
    return RandomAccess.WriteAtOffsetAsync(_fileHandle, source, writeOffset, cancellationToken, this);
}

Having said that, in many cases it is useful. This is normally where you would get a Memory from somewhere else, for example from an unmanaged memory block, or from some kind of array pool.

huangapple
  • 本文由 发表于 2023年4月17日 00:58:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76029160.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定