c# - List<struct> 与 List<class> 的性能

标签 c# memory-management struct clr prefetch

出于好奇,我试图测试 List<T> 的性能同时使用 valuereference类型。

结果与我预期的不同,这让我相信我对这些对象在内存中的布局方式的理解可能不正确。

这是我的实验:

  • 创建一个基本的 class仅包含两个成员,一个 int和一个 bool

  • 创建 2 List<T>用于保存我的测试类(List1List2)的对象

  • 随机生成测试对象并添加到List1List2交替

  • Time 遍历 List1 需要多长时间(做一些任意的工作,例如递增计数器然后访问元素)

然后我用 struct 重复了一遍代替 class

我的假设是,当使用 class 时,在 List<T> 中的引用文献将是连续的,但由于我创建它们的方式(在添加到 List1List2 之间切换),它们指向的对象可能不是。

我认为在使用 struct 时,因为它是一个值类型,对象本身将连续保存在内存中(因为 List<T> 保存实际项目而不是引用集合)。

因此,我期望 struct表现更好(由于预取器等)

事实上,两者非常相似。

这是怎么回事?

编辑 - 添加代码以实际访问迭代器中的元素,包括代码示例

测试类(或结构)

public class/struct TestClass
{
    public int TestInt;
    public bool TestBool;
}

创建随机列表:

var list1 = new List<TestClass>();
var list2 = new List<TestClass>();

var toggle = false;
for (var i=0; i < 4000000; i++)
{
    // Random object generation removed for simplicity

    if (toggle)
        list1.Add(randomObject);
    else
        list2.Add(randomObject);

    toggle = !toggle;    
}

测试:

var stopWatch = new Stopwatch();
var counter = 0;
var testBool = false;

stopwatch.Start();

foreach(var item in list1)
{
    // Access the element
    testBool = item.TestBool;
    counter++;
}

stopwatch.Stop();

TestObject 重复作为class和一个 struct .

我意识到差别不大,但我预计 struct表现明显优于class

最佳答案

// Access the element
testBool = item.TestBool;

那没有效果,优化器将删除该语句,因为它没有有用的副作用。您实际上并没有衡量结构和类之间的区别,因为您实际上从未访问过元素。

counter++;

同样的故事,很可能会被优化掉。除非您实际使用 计数器值,否则在循环完成后。让优化器删除太多代码并使测试变得毫无意义是一种常见的微基准测试风险。解决方法是:

foreach(var item in list1)
{
    // Access the element
    counter += item.TestInt;
}
Console.WriteLine(counter);

基准准则是:

  • 仅由发布配置生成的配置文件代码。 Debug 构建会产生过多的额外代码并抑制优化
  • 工具 + 选项、调试、常规,取消勾选“在模块加载时抑制 JIT 优化”。这确保即使您使用调试器运行也能获得优化的代码
  • Debug + Windows + Disassembly 是一个非常重要的调试器窗口,可以向您显示真正 运行的代码。需要对机器代码有一定的了解才能正确解释该窗口
  • 在测试代码周围放置一个外部循环以确保您至少运行测试 10 次非常重要。这消除了冷启动效应,例如处理器必须填充 L1 指令缓存以及必须从程序集中加载 IL 并在其第一次执行时编译它的抖动。并删除随机异常值,因为您必须与机器上运行的其他进程竞争并竞争处理器。
  • 15% 的差异在统计学上不显着。

关于c# - List<struct> 与 List<class> 的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23032494/

相关文章:

c# - 为什么我看不到内容编码 : gzip response in my c# HttpClient response header?

c# - ASP.NET MVC 模型到带有附加辅助实体的 View 模型映射

c++ - 为什么我得到 "Invalid Allocation Size: 4294967295 Bytes"而不是 std::bad_alloc 异常?

c++ - 为什么没有 C++ 的 DELETE 宏的原因

go - 将结构体字段转换为字符串

c# - 为什么我应该使用 continue 而不是空的 if 语句?

C# 无法从 SQL Server SELECT 转换计算列

c# - 当 RAM 在 C# 中结束时会发生什么?

c - 如何在 C 中实现 strcpy() 和结构排序?

c - C 中的结构,具有结构的函数