哪个更好:保留向量容量、预分配大小或在循环中推回?

2024-05-07

我有一个函数,它将指向 char 数组和段大小的指针作为输入参数,并调用另一个需要std::array<std::string>。这个想法是将输入的字符数组“分割”成相等的部分,并形成字符串数组。

输入字符数组格式是几个确定大小的较小数组(或字符串)连接在一起。尽管它们可能是零终​​止的,但并不假定它们是零终止的。段大小 5 和元素数量 10 的示例:

char k[] = "1234\0001234\0001234\0001234\0001234\0001234\0001234\0001234\0001234\0001234\000";
char m[] = "1234\00067890987654321\000234567809876\0005432\000\000\0003456789098";
char n[] = "12345678909876543211234567890987654321123456789098";

所有 char 数组的长度为 51(段 * 元素 + 1)。我的目标是使函数有效地使用资源,最重要的是执行时间。

由于给猫剥皮的方法有很多,我有两种(或三种)方法来解决这个问题,问题是,哪种方法“更好”?我的意思是更快、更少的资源浪费。我不是专业人士,所以请耐心等待我。

Here, values是预先分配的,然后为每个字符串分配一个值。

void myfnc_1(void *a_src, uint32_t a_segment) {
    // a_segment = 5 for example
    size_t nSize = GetSize(); // another method, gets 10
    std::vector<std::string> values(nSize);
    char* v = a_src; // take k, n or m for example

    for (size_t i = 0; i < nSize; ++i) {
        values.at(i).assign(v, a_segment);
        v += a_segment;
    }
}

这里,没有分配向量,但每次迭代都会添加一个新字符串。

void myfnc_1(void *a_src, uint32_t a_segment) {
    size_t nSize = GetSize();
    std::vector<std::string> values();
    char* v = a_src;

    for (size_t i = 0; i < nSize; ++i) {
        values.push_back("");
        values.back().assign(v, a_segment);
        v += a_segment;
    }
}

可能还有第三种方法,那更好。我对向量不太有经验,所以我不太清楚。如果段长度和元素数量通常较大 (5, 10) 或较小 (100, 10000),它们是否会产生影响?

第一次发帖,大粉丝:)


向向量添加元素

有多种方法可以将数据添加到向量中:

  • 创建一个空向量,push_back()元素融入其中。
  • 创建一个空向量,分配一些容量reserve(), then push_back()元素融入其中。
  • 创建一个向量n元素,使用索引和复制分配。
  • 创建一个空向量,emplace_back()元素融入其中。
  • 创建一个空向量,分配一些容量reserve(), then emplace_back()元素融入其中。

还有其他方法,例如使用一对迭代器创建容器,或通过标准库算法填充它。我不会在这里考虑这些。

一般考虑因素

以下两个考虑因素对于以下分析很重要:

  • 避免(重新)分配:内存分配很慢。重新分配通常涉及将容器中已有的所有内容复制到新位置。
  • 避免不必要的工作:构造你想要的元素比默认构造然后赋值更好。在您想要的地方构造一个元素比在其他地方构造它然后复制要好。

其他因素也会影响所选解决方案的效率,但这些是我们可以直接控制的重要因素。通过分析代码,其他因素可能会变得显而易见。

推回()

Each push_back()从参数到向量复制构造一个元素push_back()称呼。如果向量size() == capacity(),将执行重新分配。这通常(但可能并不总是)使容量加倍,并可能导致复制all将现有元素放入新存储中。

预分配的push_back()

Using reserve()在开始之前为元素分配足够的内存。如果您知道(或有合理的猜测)元素的数量,那么这样做总是值得的。如果你猜测的话,高估总比低估好。

The push_back()call 仍将使用元素类型的复制构造函数,但不应进行任何分配,因为已经提供了空间。您只需支付期间单次分配的费用reserve()称呼。如果您确实超出了现有容量,push_back()将重新分配,通常将容量加倍。这就是为什么高估尺寸会更好;你不太可能得到重新分配,而如果低估的话,你不仅可能会重新分配,而且你会浪费几乎是你需要的两倍的内存分配!

请注意,“容量加倍”行为并未由标准指定,但它是一种常见的实现,旨在减少使用时的重新分配频率push_back()对于未知大小的数据集。

索引和元素分配

在这里,我们创建一个由正确数量的默认构造元素组成的向量,然后使用复制赋值运算符将它们替换为我们想要的元素。它只有一次分配,但如果复制分配执行任何复杂的操作,则速度可能会很慢。这对于未知(或仅猜测)大小的数据集实际上不起作用;仅当您知道索引永远不会超过时,元素索引才是安全的size(),你必须求助于push_back()或者如果您需要更多则调整大小。这不是一个好的通用解决方案,但有时可以发挥作用。

emplace_back()

emplace_back()使用参数就地构造元素emplace_back()称呼。这通常比同等的速度更快push_back()(但不总是)。它仍然以与push_back(),保留一些容量,填充它,然后在需要更多容量时重新分配。大多数相同的论点都适用,但您可以从构造方法中获得一些收益。

emplace_back() 与预分配

这应该是 C++11 或更高版本代码库的首选策略。您将获得emplace_back()效率(如果可能)并避免重复分配。在列出的机制中,这在大多数情况下预计是最快的。

关于效率的注释

效率可以通过多种方式来衡量。执行时间是一种常见的衡量标准,但并不总是最相关的。关于使用哪种策略的一般建议基于经验,本质上是“平均”各种效果,以提供一些关于首先做什么的合理陈述。与往常一样,如果任何类型的效率对您的应用程序至关重要,那么确保优化正确位置的唯一方法是profile您的代码,进行更改,然后profile再次证明这些变化达到了预期的效果。不同的数据类型、硬件、I/O 要求等都会影响这种时序,并且您永远不会知道这些影响如何在您的特定应用程序中结合起来,直到您profile it.

Example

实例:http://coliru.stacked-crooked.com/a/83d23c2d0dcee2ff http://coliru.stacked-crooked.com/a/83d23c2d0dcee2ff

在此示例中,我使用上面列出的每种方法用 10,000 个字符串填充向量。我对每一项进行计时并打印结果。

这和你的问题类似,但不完全相同;你的结果会有所不同!

请注意,emplace_back() with reserve()是最快的,但是这里的索引和分配也很快。这很可能是因为std::string拥有高效的swap(),并且它的默认构造函数没有做太多事情。其他方法要慢一个数量级。

#include <chrono>
#include <iostream>
#include <vector>

using Clock = std::chrono::high_resolution_clock;
using time_point = std::chrono::time_point<Clock>;

std::vector<std::string> strings = {"one", "two", "three", "four", "five"};

std::chrono::duration<double> vector_push_back(const size_t n) {
    time_point start, end;
    start = Clock::now();

    std::vector<std::string> v;
    for (size_t i = 0; i < n; ++i) {
        v.push_back(strings[i % strings.size()]);
    }

    end = Clock::now();
    return end - start;
}

std::chrono::duration<double> vector_push_back_with_reserve(const size_t n) {
    time_point start, end;
    start = Clock::now();

    std::vector<std::string> v;
    v.reserve(n);
    for (size_t i = 0; i < n; ++i) {
        v.push_back(strings[i % strings.size()]);
    }

    end = Clock::now();
    return end - start;
}

std::chrono::duration<double> vector_element_assignment(const size_t n) {
    time_point start, end;
    start = Clock::now();

    std::vector<std::string> v(n);
    for (size_t i = 0; i < n; ++i) {
        v[i] = strings[i % strings.size()];
    }

    end = Clock::now();
    return end - start;
}

std::chrono::duration<double> vector_emplace_back(const size_t n) {
    time_point start, end;
    start = Clock::now();

    std::vector<std::string> v;
    for (size_t i = 0; i < n; ++i) {
        v.emplace_back(strings[i % strings.size()]);
    }

    end = Clock::now();
    return end - start;
}

std::chrono::duration<double> vector_emplace_back_with_reserve(const size_t n) {
    time_point start, end;
    start = Clock::now();

    std::vector<std::string> v;
    v.reserve(n);
    for (size_t i = 0; i < n; ++i) {
        v.emplace_back(strings[i % strings.size()]);
    }

    end = Clock::now();
    return end - start;
}

int main() {
    const size_t n = 10000;
    std::cout << "vector push_back: " << vector_push_back(n).count() << "\n";
    std::cout << "vector push_back with reserve: " << vector_push_back(n).count() << "\n";
    std::cout << "vector element assignment: " << vector_element_assignment(n).count() << "\n";
    std::cout << "vector emplace_back: " << vector_emplace_back(n).count() << "\n";
    std::cout << "vector emplace_back with reserve: " << vector_emplace_back_with_reserve(n).count() << "\n";
}

Results:

vector push_back: 0.00205563
vector push_back with reserve: 0.00152464
vector element assignment: 0.000610934
vector emplace_back: 0.00125141
vector emplace_back with reserve: 0.000545451

结论

对于大多数新代码,使用reserve() and emplace_back() (or push_back()对于较旧的代码)应该会给你一个很好的效率第一近似值。如果还不够,请对其进行分析并找出瓶颈所在。它可能不会在你想象的地方。

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

哪个更好:保留向量容量、预分配大小或在循环中推回? 的相关文章

  • 读取大文件并制作字典

    我有一个大文件 我需要读取它并从中制作字典 我希望这一切能够尽可能快 然而我的Python代码太慢了 这是一个显示问题的最小示例 首先制作一些假数据 paste lt seq 20000000 lt seq 2 20000001 gt la
  • 如何部署包含第三方 DLL 文件的 C# 应用程序?

    首先 我对部署了解不多 我希望我的问题有意义 我需要将 C 应用程序安装 部署到多个桌面 它需要一个第三方 DLL 文件 一个 C 库 lpsolve55 dll 对于那些感兴趣的人 它是一个免费的 MIP LP 求解器 请参阅 lpsol
  • 如何从 C# 调用 F# 类型扩展(静态成员函数)

    FSharp 代码的结构如下 我无法控制源代码 namespace FS
  • C# 实体框架我们应该使用 POCO.Id 还是仅使用 POCO 设置关系?

    我在服务方法中遇到一种情况 将 POCO 分配为另一个 POCO 的子对象无法按预期工作 我正在使用实体框架 4 public void ChangeOrderCurrency Currency currency order Currenc
  • 打开位置设置页面或提示用户启用位置

    我一直在绞尽脑汁 徒劳地谷歌搜索 我正在尝试找到一种方法来提示用户通过直接进入设置页面或仅点击屏幕上的 是 来切换位置 我见过的所有代码似乎都不起作用 有人有有效的方法吗 一个详细的例子将不胜感激 谢谢 我对 Xamarin 开发非常陌生
  • PartialView Action 正在调用自身

    我有 MVC 应用程序 它用于从主视图 ProductMaster 将 ProductAreaGrid 列表显示为 PartialView 并且它将在局部视图内将 CreateProductArea 作为 PartialView 我的 Gr
  • 使用 catch all 字典属性将 json 序列化为对象

    我想使用 JSON net 反序列化为对象 但将未映射的属性放入字典属性中 是否可以 例如给定 json one 1 two 2 three 3 和 C 类 public class Mapped public int One get se
  • C# 反序列化过程中创建指向父对象的指针

    我有这样的课程 Serializable public class child public Parent parent Serializable public class Parent public List
  • 确定相关词的编程方式?

    使用网络服务或软件库 我希望能够识别与词根相关的单词 例如 座位 和 安全带 共享词根 座位 但 西雅图 不会被视为匹配 简单的字符串比较对于这类事情似乎是不可行的 除了定义我自己的字典之外 是否有任何库或 Web 服务不仅可以返回单词定义
  • 这些工作队列标志意味着什么?

    在研究工作队列时 我遇到了内核中定义的工作队列标志和常量 我有以下我无法理解的疑问 这里的排水和救援到底是什么意思 WQ DRAINING 1 lt lt 6 internal workqueue is draining WQ RESCUE
  • 在 Windows 上使用 C/C++ 开发时省略 msvcr100.dll?

    是否可以在 Windows 上使用 C C 进行开发而不链接到 msvcr100 dll 我知道这是 Windows 的标准 c 库 但我想知道如果我没有安装 Visual Studio 或 Redistributable 软件包 我的计算
  • List 或其他类型上的 string.Join

    我想将整数数组或列表转换为逗号分隔的字符串 如下所示 string myFunction List
  • 使用联合对 IP 地址进行多种解释?

    在工作中 我们使用以下构造来将 IP 地址解释为 4 字节数组或 32 位整数 union IPv4 std uint32 t ip std uint8 t data 4 这很好用 但是读完这本书的第 97 章 不要使用联合来重新解释表示
  • 如何阻止 Control-I 在 CoreWindow 范围内的 UWP 文本框中插入选项卡?

    当我在 UWP 应用程序中有一个 TextBox 时 对我来说 奇怪的行为 在 Windows 10 中创建通用的空白应用程序 UWP 应用程序 使用以下代码将文本框添加到默认网格
  • 模板定义中的友元函数

    我的问题有点相关this https stackoverflow com questions 1297609 overloading friend operator for template class one 我想重载某些类的运算符 te
  • 如何在 SQLite 中检查数据库是否存在 C#

    我目前正在用 C 编写一个应用程序 并使用 sqlite 作为嵌入式数据库 我的应用程序在启动时创建一个新数据库 但如何让它检查数据库是否存在 如果它确实存在 我如何让它使用它 如果不存在如何创建一个新数据库 这是我到目前为止所拥有的 pr
  • 动态菜单创建IoC

    我想知道是否有人知道我如何创建如何使用 AutoFac 之类的东西来让我动态地允许 dll 创建自己的表单和菜单项以在运行时调用它们 所以如果我有一个 员工 dll 新入门表格 证书表格 供应商 dll 供应商详细信息来自 产品形态 在我的
  • 在 C# 窗口应用程序中运行 C/C++ 控制台应用程序?

    现在 我想开发一个简单的应用程序 因此我决定最快的编码方式是 C NET 但现在 我很难实现我需要的功能之一 我想做的是在 C 应用程序的窗口内运行 C C 控制台应用程序 就像在虚幻前端中一样 添加一点通信方式 以便我可以为控制台应用程序
  • 使用方法的状态模式

    我正在尝试使用方法作为状态而不是类来基于状态模式的修改版本来实现一个简单的状态机 如下所示 private Action
  • boost::spirit::qi::语法和可变参数模板

    我在使用可变参数模板定义语法时面临一个问题 我首先定义一些包含在某些结构中的简单语法 例如纬度 经度 如下所示 include

随机推荐

  • 在 C# 中实现记忆化 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我知道这个话题 记忆 已经被讨论了很多 比如here https stackoverflow com questions 285216
  • Python:定义多个相同类型的变量?

    可能是重复的 但至少我无法通过搜索这些术语找到答案 在Python中有没有更快的方法来做到这一点 level1 level2 level3 我试过了 level1 level2 level3 但这似乎创建了该对象的副本 这不是我想要的 和
  • CSS:最后一个子元素的高度应基于前一个兄弟元素,但不能溢出父元素

    相关 JS Fiddlehttp jsfiddle net arosen FMQtR http jsfiddle net arosen FMQtR Problem 我的 HTML 看起来像这样 div div A variable amou
  • 黄瓜和 Rspec

    任何人都可以向我推荐黄瓜和 rspec 教程 rails 3 的好来源 简单示例 吗 Edit 实际上我正在寻找带有很好示例的免费在线资源 我觉得R规格书 http www pragprog com titles achbd the rsp
  • 从纪元到相对日期的秒数

    我正在处理自纪元以来的日期 并且已经得到了 例如 date 6928727 56235 我想将其转换为另一种相对格式 以便我能够将其转换为与纪元相关的格式 使用 time gmtime date 它返回 year 1970 mon 3 da
  • 如何从PathName获取Project Guid和Model Guid?

    我的 Revit 模型有一个 RVT 链接 路径名称 BIM 360 BIM360 ArchitectureBIM360 rvt 中的测试链接编辑 我想构建一个 ModelPath 并使用它来打开云托管文件 如下所示 ModelPath m
  • 类方法的自定义代码完成?

    在 MATLAB 中 可以定义代码建议和完成 如标题为 的文档页面中所述 自定义代码建议和完成 https www mathworks com help matlab matlab prog customize code suggestio
  • 如何从 Ant 构建文件设置 Eclipse 构建路径和类路径?

    关于 Ant 和 Eclipse 有很多讨论 但之前的答案似乎对我没有帮助 事情是这样的 我正在尝试构建一个可以从命令行使用 Ant 成功编译的 Java 程序 更令人困惑的是 我尝试编译的程序是 Ant 本身 我真正想做的是将这个项目引入
  • Android Studio 不允许我更改 SDK 位置

    我打开 Android Studio 然后我打开 SDK 管理器 我拥有最新版本 但是我的 SDK 平台需要 Android 6 0 它甚至不让我点击任何东西 在此图像中 您可以看到文本和复选框变色 我无法单击 SDK 平台内的任何内容 甚
  • 压缩 Log4j 文件

    是否可以压缩日志文件 我通过 RollingFileAppender 进行 log4j 附加功能 http logging apache org log4j extras 对此表示支持 只需将以下内容添加到您的RollingFileAppe
  • 实施策略模式的函数式方法

    我正在尝试解决一个处理从一种温度单位到另一种温度单位 摄氏度 开尔文 华氏度 转换的问题 在Java中 我需要创建一个接口并提供多个实现来封装输入类型并将结果作为输出类型的单元返回 例如开尔文到摄氏度或摄氏度到华氏度等 我已经在 scala
  • 当查询没有返回记录时,如何通过 PDO/Sqlite 获取列名?

    下面的代码允许我将 SQL 语句传递给一个类并调用其方法来显示一个漂亮的结果表 包括列名 然而 如果没有结果 我仍然想要列名要显示 很遗憾 getColumnMeta没有像我发现的其他示例中那样返回任何数据 有谁知道如何让 getColum
  • 删除超过 2 个月的分区

    我有一个基于日期字段进行分区的表 现在 我必须编写一个过程来删除所有超过 2 个月的分区 即 test date 超过 2 个月 我该怎么做 create table test table test id number test date
  • 从values() 或values_list() 中排除字段

    有没有一种有效的方法从函数中排除字段values or values list e g Videos objects filter id 1 get values 我想从此查询集中排除该字段duration 我知道我可以指定我想要在结果中包
  • 在java中获取调用层次结构

    我在追踪错误时遇到了很大的困难 了解哪个方法调用了某个方法会很有帮助 有没有一种简单的方法可以从java获取调用层次结构 Java 是应用程序的一小部分 因此我无法在 eclipse net beans 中编译和运行整个应用程序 因此我无法
  • 无法访问jsf组件中的javascript文件

    我有一个必须访问 javascript 文件的 jsf 组件 我添加了这个输出脚本 如下面的代码所示 我在生成的 html 中收到错误 并且无法访问 javascript javascript 文件位于 document root js 目
  • 类的自定义格式

    我当前正在使用 Serilog 我希望能够将一个类传递给记录器并让它在输出到文本文件之前以自定义格式记录 有点类似于格式提供者 https github com serilog serilog wiki Formatting Output
  • 在 Ubuntu 11 上的 Apache 2 上使用 virtualenv 的多个 Django 应用程序

    我已经使用以下命令成功设置了一个 Django 应用程序virtualenv在 Ubuntu 和 Apache 2 上 使用WSGIPythonHome指令指向我的virtualenv地点 现在我需要创建一个单独的 Django 应用程序
  • 使用 sidekiq 只执行众多重复作业之一?

    我有一个后台作业 在 MongoDB 上执行映射 归约作业 当用户向文档发送更多数据时 它会启动在文档上运行的后台作业 如果用户发送多个请求 它将启动同一文档的多个后台作业 但实际上只有一个需要运行 有没有办法可以防止多个重复实例 我正在考
  • 哪个更好:保留向量容量、预分配大小或在循环中推回?

    我有一个函数 它将指向 char 数组和段大小的指针作为输入参数 并调用另一个需要std array