c# - Stackalloc 与 C# 中固定大小的缓冲区。有什么区别
问题描述
就我而言,以下代码将在堆栈上创建一个数组:
unsafe struct Foo
{
public fixed int bar[10];
}
var foo = new Foo();
stackalloc 语句也会做同样的事情:
public void unsafe foo(int length)
{
Span<int> bar = stackalloc int[length];
}
所以我想知道这些方法之间有什么区别。还有固定大小缓冲区的目的是什么?每个人都在谈论性能提升,但我不明白为什么我需要它们,因为我已经可以使用 stackalloc 在堆栈上创建一个数组。MSDN 说固定大小的缓冲区用于与其他平台“互操作”。那么这种“互操作”是什么样的呢?
解决方案
该fixed
语句仅表示数组内联(固定在内部)结构。这意味着存储在数组中的数据直接存储在您的结构中。在您的示例中,Foo
结构将具有存储 10 个整数值所需的大小。由于结构是值类型,它们被分配在堆栈上。但是,例如将它们存储在引用类型中时,它们也可以复制到堆中。
class Test1
{
private Foo Foo = new();
}
unsafe struct Foo
{
public fixed int bar[10];
}
上面的代码将编译,私有Foo
实例将存在于托管堆上。
如果没有固定语句(只是“正常” int[]),数组的数据将不会存储在结构本身中,而是存储在堆中。该结构将只拥有对该数组的引用。
使用stackalloc
时数据是在栈上分配的,不能被CLR自动移动到堆上。这意味着 stackalloc 的数据保留在堆栈上,编译器将通过不允许这样的代码来强制执行此操作:
unsafe class Test1
{
// CS8345: Field or auto-implemented property cannot be of type 'Span<int>' unless it is an instance member of a ref struct.
private Span<int> mySpan;
public Test1()
{
// CS8353: A result of a stackalloc expression of type 'Span<int>' cannot be used in this context because it may be exposed outside of the containing method
mySpan = stackalloc int[10];
}
}
因此,stackalloc
当您绝对要确保分配的数据不会逃逸到堆中导致抓取收集器的压力增加(这是性能问题)时,您可以使用。fixed
另一方面,主要用于与本机C/C++
库的互操作场景,这些库可能出于某种原因使用内联缓冲区。因此,当从本机世界调用将带有内联缓冲区的结构作为参数的方法时,您必须能够在 .NET 中重新创建它,否则您将无法轻松使用本机代码(因此该fixed
语句存在)。使用的另一个原因fixed
是内联结构中的数据,这可以在 CPU 访问它时允许更好的缓存,因为它可以读取所有数据Foo
一口气无需取消引用引用并在内存中跳转以访问存储在其他地方的某个数组。
推荐阅读
- node.js - package.json 文件中的 man 指令有什么用?
- r - 如何将矩阵的第 (j,i)'th 与另一个矩阵中的第 i 列相乘以在 R 中生成 j 个矩阵?
- ruby-on-rails - 查找最后创建的记录 RSpec 测试
- postgresql - 使用 nodejs 在 Lambda 函数中从 RDS PostgreSQL 获取数据
- android - Yandex MapKit 3.0 崩溃
- vba - 未设置运行时 91 变量 - 在表单和模块之间共享变量
- r - 如何使用 filter 和 str_detect 将逻辑 AND 添加到逻辑 OR 的组合中?
- python - 在 VS Code 中使用 IPython REPL
- html - 带有动画的 CSS 叠加层
- php - 如何在 yii kartik 小部件 select2 和 ajax 上使用多项选择