无法重现:C++ Vector 性能优于 C# List 性能

2024-03-08

在 Microsoft 的 BUILD 会议上,Herb Sutter 解释说 C++ 有“真实数组”,而 C#/Java 语言没有相同或类似的东西。

我被卖了。您可以在这里观看完整的演讲http://channel9.msdn.com/Events/Build/2014/2-661 http://channel9.msdn.com/Events/Build/2014/2-661

这是他描述这一点的幻灯片的快速快照。https://i.stack.imgur.com/DQaiF.png https://i.stack.imgur.com/DQaiF.png

但我想看看我能带来多大的改变。

因此,我编写了非常简单的程序进行测试,该程序从文件中创建一个大型字符串向量,其中行的行数从 5 个字符到 50 个字符不等。

文件链接:

www (dot) dropbox.com/s/evxn9iq3fu8qwss/result.txt

然后我按顺序访​​问它们。

我用 C# 和 C++ 做了这个练习。

注意:我做了一些修改,按照建议删除了循环中的复制。 感谢您帮助我理解真实数组。

在 C# 中,我同时使用了 List 和 ArrayList,因为 ArrayList 已被弃用,取而代之的是 List。

以下是我的配备 Core i7 处理器的戴尔笔记本电脑上的结果:

count       C# (List<string>)   C# (ArrayList)     C++   

1000           24 ms              21 ms             7 ms       
10000         214 ms             213 ms            64 ms     
100000  2 sec 123 ms       2 sec 125 ms           678 ms    

C# code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
namespace CSConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            int count;
            bool success = int.TryParse(args[0], out count);

            var watch = new Stopwatch();
            System.IO.StreamReader isrc = new System.IO.StreamReader("result.txt");

            ArrayList list = new ArrayList();
            while (!isrc.EndOfStream)
            {
                list.Add(isrc.ReadLine());
            }
            double k = 0;
            watch.Start();
            for (int i = 0; i < count; i++)
            {
                ArrayList temp = new ArrayList();
                for (int j = 0; j < list.Count; j++)
                {
                   // temp.Add(list[j]);
                    k++;
                }
            }

            watch.Stop();
            TimeSpan ts = watch.Elapsed;

            //Console.WriteLine(ts.ToString());
            Console.WriteLine("Hours: {0} Miniutes: {1} Seconds: {2} Milliseconds: {3}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
            Console.WriteLine(k);
            isrc.Close();
        }


    }
}

C++代码

#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>

#include <vector>
#include <fstream>
#include <chrono>
#include <iostream>
#include <string>

using namespace std;

std::chrono::high_resolution_clock::time_point time_now()
{
    return std::chrono::high_resolution_clock::now();
}

float time_elapsed(std::chrono::high_resolution_clock::time_point const & start)
{

    return std::chrono::duration_cast<std::chrono::milliseconds>(time_now() - start).count();
    //return std::chrono::duration_cast<std::chrono::duration<float>>(time_now() - start).count();
}


int _tmain(int argc, _TCHAR* argv [])
{
    int  count = _wtoi(argv[1]);

    vector<string> vs;
    fstream fs("result.txt", fstream::in);
    if (!fs) return -1;

    char* buffer = new char[1024];
    while (!fs.eof())
    {
        fs.getline(buffer, 1024);
        vs.push_back(string(buffer, fs.gcount()));
    }
    double k = 0;
    auto const start = time_now();
    for (int i = 0; i < count; i++)
    {
        vector<string> vs2;
        vector<string>::const_iterator iter;
        for (iter = vs.begin(); iter != vs.end(); iter++)
        {
            //vs2.push_back(*iter);
            k++;
        }
    }

    auto const elapsed = time_elapsed(start);
    cout << elapsed << endl;
    cout << k;
    fs.close();
    return 0;
}

示例程序发现的差异与列表或其结构无关。

这是因为在 C# 中,字符串是引用类型,而在 C++ 代码中,您将它们用作值类型。

例如:

string a = "foo bar baz";
string b = a;

分配b = a只是复制指针。

这将贯穿到列表中。将字符串添加到 C# 列表只是将指针添加到列表中。在主循环中,您创建 N 个列表,所有列表都只包含指向相同字符串的指针。

然而,因为您在 C++ 中按值使用字符串,所以每次都必须复制它们。

vector<string> vs2;
vector<string>::const_iterator iter;
for (iter = vs.begin(); iter != vs.end(); iter++)
{
   vs2.push_back(*iter); // STRING IS COPIED HERE
}

这段代码实际上是在复制每个字符串。您最终会得到所有字符串的副本,并且会使用更多的内存。由于显而易见的原因,速度较慢。

如果将循环重写如下:

vector<string*> vs2;
for (auto& s : vs)
{
    vs2.push_back(&(s));
}

那么您现在正在创建指针列表而不是副本列表,并且与 C# 处于平等地位。

在我的系统上,C# 程序的运行时间约为 N of 1000138 毫秒,而 C++ 则运行在44毫秒,C++ 明显获胜。


评论:

根据 Herb sutter 的图片,C++ 向量的主要优点之一是内存布局可以是连续的(即所有项目在内存中彼此相邻)。 你永远不会看到这个作品std::string但是,由于字符串需要动态内存分配(您不能在数组中将一堆字符串彼此相邻放置,因为每个字符串都有不同的长度)

如果您想快速遍历所有项目,这将带来很大的好处,因为它对 CPU 缓存更加友好,但代价是您必须复制所有项目才能将它们放入列表中。

这是一个更好地说明它的例子:

C# Code:

class ValueHolder {
    public int tag;
    public int blah;
    public int otherBlah;

    public ValueHolder(int t, int b, int o)
    { tag = t; blah = b; otherBlah = o; }
};

static ValueHolder findHolderWithTag(List<ValueHolder> buffer, int tag) {
    // find holder with tag i
    foreach (var holder in buffer) {
        if (holder.tag == tag)
            return holder;
    }
    return new ValueHolder(0, 0, 0);
}

static void Main(string[] args)
{
    const int MAX = 99999;

    int  count = 1000; // _wtoi(argv[1]);
    List<ValueHolder> vs = new List<ValueHolder>();
    for (int i = MAX; i >= 0; i--) {
        vs.Add(new ValueHolder(i, 0, 0)); // construct valueholder with tag i, blahs of 0
    }

    var watch = new Stopwatch();
    watch.Start();

    for (int i = 0; i < count; i++)
    {
        ValueHolder found = findHolderWithTag(vs, i);
        if (found.tag != i)
            throw new ArgumentException("not found");
    }

    watch.Stop();
    TimeSpan ts = watch.Elapsed;
    Console.WriteLine("Hours: {0} Miniutes: {1} Seconds: {2} Milliseconds: {3}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
}

C++代码:

class ValueHolder {
public:
    int tag;
    int blah;
    int otherBlah;

    ValueHolder(int t, int b, int o) : tag(t), blah(b), otherBlah(o) { }
};

const ValueHolder& findHolderWithTag(vector<ValueHolder>& buffer, int tag) {
    // find holder with tag i
    for (const auto& holder : buffer) {
        if (holder.tag == tag)
            return holder;
    }
    static ValueHolder empty{ 0, 0, 0 };
    return empty;
}

int _tmain(int argc, _TCHAR* argv[])
{
    const int MAX = 99999;

    int  count = 1000; // _wtoi(argv[1]);
    vector<ValueHolder> vs;
    for (int i = MAX; i >= 0; i--) {
        vs.emplace_back(i, 0, 0); // construct valueholder with tag i, blahs of 0
    }

    auto const start = time_now();
    for (int i = 0; i < count; i++)
    {
        const ValueHolder& found = findHolderWithTag(vs, i);
        if (found.tag != i) // need to use the return value or compiler will optimize away
            throw "not found";
    }

    auto const elapsed = time_elapsed(start);
    cout << elapsed << endl;
    return 0;
}

从最初的问题中我们已经知道,在 C# 中创建一堆重复列表比在 C++ 中要快得多,但是搜索列表又如何呢?

这两个程序都只是做了一个愚蠢的线性列表扫描,以简单地尝试展示这一点。

在我的电脑上,C++ 版本运行在72ms而 C# 则需要620ms。为什么速度会增加?因为“真实数组”。

所有的C++ValueHolders被困在彼此旁边std::vector。当循环想要读取下一个时,这意味着它很可能已经在 CPU 缓存中。

C# ValueHolder 位于各种随机内存位置,列表仅包含指向它们的指针。当循环想要读取下一个时,几乎可以肯定not在CPU缓存中,所以我们必须去读取它。内存访问速度很慢,因此 C# 版本需要近 10 倍的时间。

如果您将 C# ValueHolder 从class to struct,那么 C# 列表可以将它们全部粘贴在内存中,时间会下降到161ms。但现在,当您插入列表时,它必须制作副本。

C# 的问题是,在很多情况下您不能或不想使用结构体,而在 C++ 中您对这种事情有更多的控制权。

PS:Java没有结构体,所以你根本不能这样做。你被困在 10 倍慢的缓存不友好版本中

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法重现:C++ Vector 性能优于 C# List 性能 的相关文章

  • pthread_cond_timedwait() 和 pthread_cond_broadcast() 解释

    因此 我在堆栈溢出和其他资源上进行了大量搜索 但我无法理解有关上述函数的一些内容 具体来说 1 当pthread cond timedwait 因为定时器值用完而返回时 它如何自动重新获取互斥锁 互斥锁可能被锁定在其他地方 例如 在生产者
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • C++ 子字符串返回错误结果

    我有这个字符串 std string date 20121020 我正在做 std cout lt lt Date lt lt date lt lt n std cout lt lt Year lt lt date substr 0 4 l
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • 如何将图像和 POST 数据上传到 Azure 移动服务 ApiController 终结点?

    我正在尝试上传图片and POST表单数据 尽管理想情况下我希望它是json 到我的端点Azure 移动服务应用 我有ApiController method HttpPost Route api upload databaseId sea
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • 如何返回 json 结果并将 unicode 字符转义为 \u1234

    我正在实现一个返回 json 结果的方法 例如 public JsonResult MethodName Guid key var result ApiHelper GetData key Data is stored in db as v
  • 在 ASP.NET Core 3.1 中使用包含“System.Web.HttpContext”的旧项目

    我们有一些用 Net Framework编写的遗留项目 应该由由ASP NET Core3 1编写的API项目使用 问题是这些遗留项目正在使用 System Web HttpContext 您知道它不再存在于 net core 中 现在我们
  • clang 实例化后静态成员初始化

    这样的代码可以用 GCC 编译 但 clang 3 5 失败 include
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • 当操作繁忙时,表单不执行任何操作(冻结)

    我有一个使用 C 的 WinForms 应用程序 我尝试从文件中读取一些数据并将其插入数据表中 当此操作很忙时 我的表单冻结并且无法移动它 有谁知道我该如何解决这个问题 这可能是因为您在 UI 线程上执行了操作 将文件和数据库操作移至另一个
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • NHibernate - CreateCriteria 与 CreateAlias

    假设以下场景 class Project public Job Job class Job public Name 假设我想使用 Criteria API 搜索其 Job 名称为 sumthing 的所有项目 我可以使用 CreateAli
  • WCF:将随机数添加到 UsernameToken

    我正在尝试连接到用 Java 编写的 Web 服务 但有些东西我无法弄清楚 使用 WCF 和 customBinding 几乎一切似乎都很好 除了 SOAP 消息的一部分 因为它缺少 Nonce 和 Created 部分节点 显然我错过了一
  • C - 直接从键盘缓冲区读取

    这是C语言中的一个问题 如何直接读取键盘缓冲区中的数据 我想直接访问数据并将其存储在变量中 变量应该是什么数据类型 我需要它用于我们研究所目前正在开发的操作系统 它被称为 ICS OS 我不太清楚具体细节 它在 x86 32 位机器上运行
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif

随机推荐

  • Zend 框架 - Zend_Form 装饰器问题

    我有一个像这样扩展 Zend Form 的类 简化 class Core Form extends Zend Form protected static elementDecorators array ViewHelper Errors a
  • 如何使用 tie() 仅为某些包重定向 STDOUT、STDERR?

    我需要使用一些不幸记录诊断的库 发送至 STDOUT 和 STDERR 的消息 通过使用tie 我可以重定向那些 写入捕获这些的函数 既然我不想要全部 我的程序的 STDOUT 和 STDERR 输出通过 捆绑手柄 我只想对某些包裹执行此操
  • 在 Android 4.4 上使用非默认短信应用程序发送短信

    我可以不使用默认短信应用程序发送短信吗安卓 4 4 奇巧 这意味着 我可以在无法写入短信提供商的情况下发送短信吗 我对 Android 4 4 Kitkat 上的这一点感到困惑 我想知道我是否可以使用非默认短信应用程序发送短信 即使您的应用
  • argparse 参数顺序

    我有一个小问题 I use argparse来解析我的论点 而且效果很好 为了获得参数 我这样做 p args parser parse args argv args dict p args get kwargs 但问题是p args是我不
  • 从前台服务观察LiveData

    我有一个存储库 其中包含 LiveData 对象并由两者使用 通过 ViewModel 的 Activity 和前台服务 当我开始从活动中观察时 一切都按预期进行 但是 从服务中进行观察不会触发观察 这是我使用的代码 class MySer
  • Google 地图 - 如何获取两点之间的距离(以米为单位)?

    我有这些坐标 45 463688 9 18814 46 0438317 9 75936230000002 我需要 我认为是通过 Google API V3 来获取这两点之间的距离 以米为单位 我该怎么做 如果您想使用 v3 谷歌地图 API
  • 如何开始制作 C# RSS 阅读器?

    我想做一个 RSS 阅读器有一段时间了 只是为了好玩 但我完全不知道从哪里开始 我对RSS一无所知 有没有关于 RSS 的好的教程以及如何在应用程序中实现它 不是关于如何制作 RSS 阅读器的教程 那太简单了 See http msdn m
  • 变量名中的美元符号

    我偶然发现了一些像这样的 C 代码 int T S 首先我以为这是某种PHP https en wikipedia org wiki PHP代码或错误粘贴在那里的东西 但它可以很好地编译和运行 在2008年MSVC https en wik
  • Scikit Learn SVC Decision_function 和预测

    我试图理解 Decision function 和 Predict 之间的关系 它们是 SVC 的实例方法 http scikit learn org stable modules generated sklearn svm SVC htm
  • 如何使用新的controllerAs语法和面向对象的控制器在Angular中实现两种方式的绑定?

    我害怕 范围汤 人们将太多的功能挂在 scope 之外 因此 我正在尝试面向 OO 的控制器 新的controllerAs 并在我的控制器中使用 EC5 样式的 getter setter 这很有效 但现在我想以两种方式将指令的范围绑定到控
  • React CSS 模块 - 某些 CSS 未应用(对于 NavLink 组件设置的“活动”类)

    我的CSSactive class尽管 CSS 的其余部分实际上已应用 但似乎并未应用到渲染的组件上 CSS 是使用CSS 模块应用的 自从NavLinkReact router dom 包的组件将类设置为活动类 我在 CSS 文件中选择了
  • 在更改图像资源时在 ImageView 上创建动画

    我只有一个ImageView在我的布局中 当检测到气体事件时 我正在更改其资源 我只想在更改 ImageView 资源时显示动画 我可以用吗ViewFlipper with one图像视图 对于单个图像视图 您可以使用此辅助函数 publi
  • Pandas:如何将某些列移动到行中?

    假设我有df以下 我想合并价格列和价值列 以便所有价格都在一个列中 所有数量都在另一列中 我还想要第三列来确定价格水平 例如 unit1 unit2 and unit3 import numpy as np import pandas as
  • Numpy:ValueError:所需数组的对象深度太小

    我正在尝试将 MATLAB 代码转换为 Python 但我不知道如何将此行导入到 Python YDFA xa p interp1 data 1 data 2 YDFA lam p 1e9 linear 1e 24 现在对于 Python
  • 将文本列表格式化为列

    我正在尝试将字符串值列表输出为 2 列格式 将字符串列表制作为 普通文本 的标准方法是使用字符串连接方法 但是 它只需要 2 个参数 因此我只能使用 n 创建一个列 我认为尝试创建一个循环 只需在列之间添加一个选项卡就可以做到这一点 但逻辑
  • 如何在sbt项目中声明对Scalding的依赖?

    我想弄清楚如何创建一个build sbt为我自己的文件Scalding https github com twitter scalding为基础的项目 烫源结构无build sbt文件 相反 它有project Build scala构建定
  • 无法安装Python和GDAL(DLL加载失败)

    我正在尝试在我的 Windows 7 工作站上安装 GDAL 和 Python Python 版本 2 6 6 Gdal 1 8 视觉 Visual C Studio 2010 Express 我按照以下说明进行操作http ucsb pb
  • 欺骗 IP 地址以使用 Sitecore 8 测试 GEOIP 查找

    我是 Sitecore 的新手 我正在尝试实现以下流程类来覆盖GeoIP用于测试目的的值 我找不到在哪个名称空间Tracker 类位于 请注意 我正在使用站点核心 8托管在本地主机上 Sitecore 博客 sitecorejohn 博客
  • 通过 Python winreg 在注册表中设置 Windows 系统路径

    我编写了一个程序 通过注册表将目录添加到 PATH 变量 HKCU 用户 或 HKLM 系统 路径 具体取决于输入选项 使用用户路径时效果很好 但是 当设置系统路径时 Windows 会认为路径变量为空 例如 notepad is not
  • 无法重现:C++ Vector 性能优于 C# List 性能

    在 Microsoft 的 BUILD 会议上 Herb Sutter 解释说 C 有 真实数组 而 C Java 语言没有相同或类似的东西 我被卖了 您可以在这里观看完整的演讲http channel9 msdn com Events B