明智地进行向量内存分配

2024-03-09

假设我必须迭代一个可能非常大的数字向量,并将偶数和奇数元素复制到新的单独向量中。 (源向量可以具有任意比例的偶数与奇数;它可以是全偶数、全奇数或介于两者之间。)

为了简单起见,push_back经常用于此类事情:

for (std::size_t Index; Index < Source.size(); Index++)
{
    if (Source[Index] % 2) Odds.push_back(Source[Index]);
    else Evens.push_back(Source[Index]);
}

但是,我担心如果将其用作排序算法(其中性能至关重要)的实现的一部分,这将是低效且有害的。例如,快速排序涉及分离元素,就像这样。

你可以使用reserve()预先分配内存,因此只需要一次分配,但随后您必须对整个源向量进行两次迭代 - 一次是为了计算需要排序的元素数量,另一次是为了实际复制。

当然,您可以分配与源向量大小相同的空间,因为两个新向量都不需要容纳比这更多的空间,但这似乎有点浪费。

我缺少更好的方法吗?是push_back()通常被信任为程序员管理此类事情,或者它会成为敏感算法的负担吗?


我将回答我认为您真正想问的问题,即“应该push_back()在重型算法的内部循环中应该避免吗?”而不是其他人似乎在你的帖子中读到的内容,即“如果我在对大向量进行不相关的排序之前调用push_back,这有什么关系吗?”另外,我要去根据我的经验来回答,而不是花时间寻找引文和同行评审的文章。

您的示例基本上做了两件事,这些事情加起来就等于总 CPU 成本:它读取输入向量中的元素并对其进行操作,然后必须将这些元素插入到输出向量中。您担心插入元素的成本,因为:

  1. 当向量为附加元素预先保留足够的空间时,push_back() 是恒定时间(实际上是瞬时的),但当您用完保留空间时,会很慢。
  2. 分配内存的成本很高(malloc()只是很慢 https://stackoverflow.com/questions/470683/memory-allocation-deallocation-bottleneck,即使学究假装new是不同的东西)
  3. 重新分配后将向量的数据从一个区域复制到另一个区域也很慢 https://stackoverflow.com/questions/4008128/one-large-malloc-versus-multiple-smaller-reallocs:当push_back()发现它没有足够的空间时,它必须分配一个更大的向量,然后复制所有元素 http://www.tantalon.com/pete/files/gdc04_common_cpp_mistakes_in_games.ppt。 (理论上,对于大小为许多操作系统页面的向量,STL 的神奇实现可以使用 VMM 在虚拟地址空间中移动它们,而无需复制 — 实际上我从未见过一个可以 http://www.gamedev.net/topic/505481-do-stl-containers-always-move-memory/.)
  4. 过度分配输出向量会导致问题:它会导致碎片,使未来的分配速度变慢;它会燃烧数据缓存,使一切变慢;如果持续存在,它会占用稀缺的可用内存,导致 PC 上的磁盘分页和嵌入式平台上的崩溃。
  5. 分配不足的输出向量会导致问题,因为重新分配向量是一个 O(n) 操作,因此重新分配它m次数为O(m×n)。如果 STL 的默认分配器使用指数重新分配(每次重新分配时使向量的保留大小变为之前大小的两倍),则线性算法的复杂度为 O(n + n log m)。

因此,您的直觉是正确的:尽可能为向量预先保留空间,不是因为 push_back 很慢,而是因为它可以触发重新分配is慢的。另外,如果你看看shrink_to_fit,您会看到它还会进行副本重新分配,暂时使内存成本加倍并导致进一步的碎片。

这里的问题是,您并不总是确切地知道输出向量需要多少空间;通常的反应是使用启发式或者自定义分配器。默认情况下为每个输出向量保留 n/2+k 的输入大小,其中 k 是一些安全裕度。这样你就会usually只要您的输入合理平衡,就有足够的空间用于输出,并且在极少数情况不平衡的情况下,push_back 可以重新分配。如果您发现 push_back 的指数行为浪费了太多内存(导致您在实际上只需要 n+2 时保留 2n 个元素),您可以给它一个自定义分配器,将向量大小扩展为更小的线性块 - 但当然在向量确实不平衡并且最终需要进行大量调整大小的情况下,这会慢得多。

如果不提前遍历输入元素,就无法始终保留正确的空间量;但如果你知道平衡点是什么usually看起来,您可以使用启发式方法对其进行很好的猜测,以便在多次迭代中获得统计性能增益。

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

明智地进行向量内存分配 的相关文章

  • 如何在 DataColumn.Expression 中使用 IF/ELSE 或 CASE?

    我有一个包含 1 列的表 状态 我想添加另一列名为 Action 的列 其值如下 如果 Status Yes 则 Action Go 否则 Action Stop 我使用以下代码添加到 操作 列中 但它不起作用 myDataTable Co
  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • 在 C# 中按元素相乘数组具有意想不到的性能

    我想找到按元素相乘两个数组的最佳方法 这是更广泛项目的一部分 其中性能而不是唯一的考虑因素 我今天开始用 C Linqpad 编写一些函数 因此它还没有以任何方式进行优化 下面代码的输出如下 Environment ProcessorCou
  • 使用 Enumerable.OfType() 或 LINQ 查找特定类型的所有子控件

    Existed MyControl1 Controls OfType
  • 我如何在 C# .NET(win7 手机)中使用“DataContractJsonSerializer”读入“嵌套”Json 文件?

    我有一个问题 如果我的 json 文件看起来像这样 Numbers 45387 Words 空间桶 我可以很好地阅读它 但是如果它看起来像这样 Main Numbers 45387 Words 空间桶 某事 数字 12345 单词 克兰斯基
  • ASP.NET Web API 客户端 ProgressMessageHandler Post 任务卡在 WinForm 应用程序中

    我在用着HttpClient and ProgressMessageHandler来自MS ASP NET Web API 客户端库 http nuget org packages Microsoft AspNet WebApi Clien
  • 如何在 SqlDataReader.Read() 期间从死锁异常中恢复

    我的 NET 应用程序的事件日志显示 它在从 Sql Server 读取数据时偶尔会出现死锁 这种情况通常非常罕见 因为我们已经优化了查询以避免死锁 但有时仍然会发生 过去 我们在调用ExecuteReader函数在我们的SqlComman
  • 指向特征矩阵的指针数组

    我在代码中使用 Eigen 的 MatrixXd 矩阵 在某个时刻我需要一个 3D 矩阵 由于 Eigen 没有三维矩阵类型 因为它仅针对线性代数进行了优化 因此我创建了一个 MatrixXd 类型的指针数组 Eigen MatrixXd
  • Python——捕获异常的效率[重复]

    这个问题在这里已经有答案了 可能的重复 Python 常见问题解答 异常有多快 https stackoverflow com questions 8107695 python faq how fast are exceptions 我记得
  • 如何在 QTabWidget Qt 中展开选项卡

    我有一个QTabWidget像这个 但我想展开选项卡以 填充 整个小部件宽度 如下所示 我怎样才能做到这一点 我在用Qt 5 3 2 and Qt 创建者 3 2 1 Update 我尝试使用setExpanding功能 ui gt myT
  • 单例模式和 std::unique_ptr

    std unique ptr唯一地控制它指向的对象 因此不使用引用计数 单例确保利用引用计数只能创建一个对象 那么会std unique ptr与单例执行相同 单例确保只有一个实例属于一种类型 A unique ptr确保只有一个智能指针到
  • 如何在服务器端按钮点击时关闭当前标签页?

    我尝试在确认后关闭当前选项卡 因此我将以下代码放在确认按钮的末尾 但选项卡没有关闭 string jScript ClientScript RegisterClientScriptBlock this GetType keyClientBl
  • 将二进制数据从 C# 上传到 PHP

    我想将文件从 Windows C 应用程序上传到运行 PHP 的 Web 服务器 我知道 WebClient UploadFile 方法 但我希望能够分块上传文件 以便我可以监控进度并能够暂停 恢复 因此 我正在读取文件的一部分并使用 We
  • 如何分析组合的 python 和 c 代码

    我有一个由多个 python 脚本组成的应用程序 其中一些脚本正在调用 C 代码 该应用程序现在的运行速度比以前慢得多 因此我想对其进行分析以查看问题所在 是否有工具 软件包或只是一种分析此类应用程序的方法 有一个工具可以将 python
  • 我可以让 ungetc 取消阻止阻塞的 fgetc 调用吗?

    我想在收到 SIGUSR1 后使用 ungetc 将 A 字符重新填充到标准输入中 想象一下我有充分的理由这样做 调用 foo 时 stdin 中的阻塞读取不会被收到信号时的 ungetc 调用中断 虽然我没想到它会按原样工作 但我想知道是
  • 新任务中使用的依赖注入服务

    我在需要时使用依赖项注入来访问我的服务 但我现在想要创建一个并发任务 但这会由于依赖项注入对象及其生命周期而导致问题 我读过这篇文章 标题 防止多线程 Link http mehdi me ambient dbcontext in ef6
  • 使用taskkill停止Windows服务

    我需要帮助来使用 C 终止 Windows 服务 现在要终止该服务 请使用以下选项 从命令 sc queryex ServiceName 发现后PID服务的 taskkill pid 1234 exemple f 为了便于阅读 但如果您明白
  • 抛出 Java 异常时是否会生成堆栈跟踪?

    这是假设我们不调用 printstacktrace 方法 只是抛出和捕获 我们正在考虑这样做是为了解决一些性能瓶颈 不 堆栈跟踪是在构造异常对象时生成的 而不是在抛出异常对象时生成的 Throwable 构造函数调用 fillInStack
  • 在简单注入器中解析具有自定义参数的类

    我正在使用以下命令创建 WPF MVVM 应用程序简易注射器作为 DI 容器 现在 当我尝试从简单注入器解析视图时遇到一些问题 因为我需要在构造时将参数传递到构造函数中 而不是在将视图注册到容器时 因此这不是适用的 简单注入器将值传递到构造
  • xsi:type 属性搞乱了 C# XML 反序列化

    我使用 XSD exe 根据 XML 架构 xsd 文件 自动生成 C 对象 我正在反序列化 OpenCover 输出 但其中一个部分类未正确生成 这是导致异常的行

随机推荐