连接两个向量同时转换一个向量的元素的最佳方法是什么?

2024-02-22

假设我有

std::vector<T1> vec1 {/* filled with T1's */};
std::vector<T2> vec2 {/* filled with T2's */};

和一些功能T1 f(T2)这当然可以是 lambda。连接的最佳方式是什么vec1 and vec2申请时f每一个T2 in vec2?

显而易见的解决方案是std::transform, i.e.

vec1.reserve(vec1.size() + vec2.size());
std::transform(vec2.begin(), vec2.end(), std::back_inserter(vec1), f);

但我说这是not最优为std::back_inserter必须进行不必要的容量检查每个插入的元素。最好的办法是这样的

vec1.insert(vec1.end(), vec2.begin(), vec2.end(), f);

只需进行一次容量检查即可。遗憾的是这不是有效的 C++。本质上这就是同样的原因std::vector::insert是向量串联的最佳选择,请参阅this https://stackoverflow.com/questions/201718/concatenating-two-stl-vectors?lq=1问题和评论this https://stackoverflow.com/questions/28528626/why-does-stdvectorinsert-need-to-copy-assign/28528872?noredirect=1#comment45373435_28528872就这一点进行进一步讨论的问题。

So:

  1. Is std::transform使用STL的最佳方法?
  2. 如果是这样,我们可以做得更好吗?
  3. 是否有充分的理由insertSTL 中省略了上述函数吗?

UPDATE

我尝试验证多次容量检查是否确实有任何明显的成本。为此,我基本上只是传递 id 函数(f(x) = x)到std::transform and push_back答案中讨论的方法。完整代码是:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <cstdint>
#include <chrono>
#include <numeric>
#include <random>

using std::size_t;

std::vector<int> generate_random_ints(size_t n)
{
    std::default_random_engine generator;
    auto seed1 = std::chrono::system_clock::now().time_since_epoch().count();
    generator.seed((unsigned) seed1);
    std::uniform_int_distribution<int> uniform {};
    std::vector<int> v(n);
    std::generate_n(v.begin(), n, [&] () { return uniform(generator); });
    return v;
}

template <typename D=std::chrono::nanoseconds, typename F>
D benchmark(F f, unsigned num_tests)
{
    D total {0};
    for (unsigned i = 0; i < num_tests; ++i) {
        auto start = std::chrono::system_clock::now();
        f();
        auto end = std::chrono::system_clock::now();
        total += std::chrono::duration_cast<D>(end - start);
    }
    return D {total / num_tests};
}

template <typename T>
void std_insert(std::vector<T> vec1, const std::vector<T> &vec2)
{
    vec1.insert(vec1.end(), vec2.begin(), vec2.end());
}

template <typename T1, typename T2, typename UnaryOperation>
void push_back_concat(std::vector<T1> vec1, const std::vector<T2> &vec2, UnaryOperation op)
{
    vec1.reserve(vec1.size() + vec2.size());
    for (const auto& x : vec2) {
        vec1.push_back(op(x));
    }
}

template <typename T1, typename T2, typename UnaryOperation>
void transform_concat(std::vector<T1> vec1, const std::vector<T2> &vec2, UnaryOperation op)
{
    vec1.reserve(vec1.size() + vec2.size());
    std::transform(vec2.begin(), vec2.end(), std::back_inserter(vec1), op);
}

int main(int argc, char **argv)
{
    unsigned num_tests {1000};
    size_t vec1_size {10000000};
    size_t vec2_size {10000000};

    auto vec1 = generate_random_ints(vec1_size);
    auto vec2 = generate_random_ints(vec1_size);

    auto f_std_insert = [&vec1, &vec2] () {
        std_insert(vec1, vec2);
    };
    auto f_push_back_id = [&vec1, &vec2] () {
        push_back_concat(vec1, vec2, [] (int i) { return i; });
    };
    auto f_transform_id = [&vec1, &vec2] () {
        transform_concat(vec1, vec2, [] (int i) { return i; });
    };

    auto std_insert_time   = benchmark<std::chrono::milliseconds>(f_std_insert, num_tests).count();
    auto push_back_id_time = benchmark<std::chrono::milliseconds>(f_push_back_id, num_tests).count();
    auto transform_id_time = benchmark<std::chrono::milliseconds>(f_transform_id, num_tests).count();

    std::cout << "std_insert: " << std_insert_time << "ms" << std::endl;
    std::cout << "push_back_id: " << push_back_id_time << "ms" << std::endl;
    std::cout << "transform_id: " << transform_id_time << "ms" << std::endl;

    return 0;
}

编译为:

g++ vector_insert_demo.cpp -std=c++11 -O3 -o vector_insert_demo

Output:

std_insert: 44ms
push_back_id: 61ms
transform_id: 61ms

编译器将内联 lambda,这样就可以安全地降低成本。除非其他人对这些结果有可行的解释(或者愿意检查组件),否则我认为可以合理地得出多次容量检查的成本显着的结论。


更新:性能差异是由于reserve()调用,至少在 libstdc++ 中,使容量完全符合您的要求,而不是使用指数增长因子。


我做了一些计时测试,得到了有趣的结果。使用std::vector::insert随着boost::transform_iterator这是我发现的最快的方法:

版本1:

void
  appendTransformed1(
    std::vector<int> &vec1,
    const std::vector<float> &vec2
  )
{
  auto v2begin = boost::make_transform_iterator(vec2.begin(),f);
  auto v2end   = boost::make_transform_iterator(vec2.end(),f);
  vec1.insert(vec1.end(),v2begin,v2end);
}

版本2:

void
  appendTransformed2(
    std::vector<int> &vec1,
    const std::vector<float> &vec2
  )
{
  vec1.reserve(vec1.size()+vec2.size());
  for (auto x : vec2) {
    vec1.push_back(f(x));
  }
}

版本 3:

void
  appendTransformed3(
    std::vector<int> &vec1,
    const std::vector<float> &vec2
  )
{
  vec1.reserve(vec1.size()+vec2.size());
  std::transform(vec2.begin(),vec2.end(),std::inserter(vec1,vec1.end()),f);
}

Timing:



    Version 1: 0.59s
    Version 2: 8.22s
    Version 3: 8.42s
  

主要.cpp:

#include <algorithm>
#include <cassert>
#include <chrono>
#include <iterator>
#include <iostream>
#include <random>
#include <vector>
#include "appendtransformed.hpp"

using std::cerr;

template <typename Engine>
static std::vector<int> randomInts(Engine &engine,size_t n)
{
  auto distribution = std::uniform_int_distribution<int>(0,999);
  auto generator = [&]{return distribution(engine);};
  auto vec = std::vector<int>();
  std::generate_n(std::inserter(vec,vec.end()),n,generator);
  return vec;
}

template <typename Engine>
static std::vector<float> randomFloats(Engine &engine,size_t n)
{
  auto distribution = std::uniform_real_distribution<float>(0,1000);
  auto generator = [&]{return distribution(engine);};
  auto vec = std::vector<float>();
  std::generate_n(std::inserter(vec,vec.end()),n,generator);
  return vec;
}

static auto
  appendTransformedFunction(int version) ->
    void(*)(std::vector<int>&,const std::vector<float> &)
{
  switch (version) {
    case 1: return appendTransformed1;
    case 2: return appendTransformed2;
    case 3: return appendTransformed3;
    default:
      cerr << "Unknown version: " << version << "\n";
      exit(EXIT_FAILURE);
  }

  return 0;
}

int main(int argc,char **argv)
{
  if (argc!=2) {
    cerr << "Usage: appendtest (1|2|3)\n";
    exit(EXIT_FAILURE);
  }
  auto version = atoi(argv[1]);
  auto engine = std::default_random_engine();
  auto vec1_size = 1000000u;
  auto vec2_size = 1000000u;
  auto count = 100;
  auto vec1 = randomInts(engine,vec1_size);
  auto vec2 = randomFloats(engine,vec2_size);
  namespace chrono = std::chrono;
  using chrono::system_clock;
  auto appendTransformed = appendTransformedFunction(version);
  auto start_time = system_clock::now();
  for (auto i=0; i!=count; ++i) {
    appendTransformed(vec1,vec2);
  }
  auto end_time = system_clock::now();
  assert(vec1.size() == vec1_size+count*vec2_size);
  auto sum = std::accumulate(vec1.begin(),vec1.end(),0u);
  auto elapsed_seconds = chrono::duration<float>(end_time-start_time).count();

  cerr << "Using version " << version << ":\n";
  cerr << "  sum=" << sum << "\n";
  cerr << "  elapsed: " << elapsed_seconds << "s\n";
}

编译器:g++ 4.9.1

选项:-std=c++11 -O2

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

连接两个向量同时转换一个向量的元素的最佳方法是什么? 的相关文章

  • 我如何才能等待多个事情

    我正在使用 C 11 和 stl 线程编写一个线程安全队列 WaitAndPop 方法当前如下所示 我希望能够将一些内容传递给 WaitAndPop 来指示调用线程是否已被要求停止 如果 WaitAndPop 等待并返回队列的元素 则应返回
  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • 按成员序列化

    我已经实现了template
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • 不支持将数据直接绑定到存储查询(DbSet、DbQuery、DbSqlQuery)

    正在编码视觉工作室2012并使用实体模型作为我的数据层 但是 当页面尝试加载时 上面提到的标题 我使用 Linq 语句的下拉控件往往会引发未处理的异常 下面是我的代码 using AdventureWorksEntities dw new
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • 将 VSIX 功能添加到 C# 类库

    我有一个现有的单文件生成器 位于 C 类库中 如何将 VSIX 项目级功能添加到此项目 最终目标是编译我的类库项目并获得 VSIX 我实际上是在回答我自己的问题 这与Visual Studio 2017 中的单文件生成器更改 https s
  • 使用 WebClient 时出现 System.Net.WebException:无法创建 SSL/TLS 安全通道

    当我执行以下代码时 System Net ServicePointManager ServerCertificateValidationCallback sender certificate chain errors gt return t
  • 将多个表映射到实体框架中的单个实体类

    我正在开发一个旧数据库 该数据库有 2 个具有 1 1 关系的表 目前 我为每个定义的表定义了一种类型 1Test 1Result 我想将这些特定的表合并到一个类中 当前的类型如下所示 public class Result public
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 如何将带有 IP 地址的连接字符串放入 web.config 文件中?

    我们当前在 web config 文件中使用以下连接字符串 add name DBConnectionString connectionString Data Source ourServer Initial Catalog ourDB P
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐

  • Maven 构建配置文件激活

    我知道有一些相关的主题 但我仍然不明白该怎么做 我正在学习 Maven 目前正在创建构建配置文件 我希望 Maven 自动检测我的机器上当前安装的 java 版本 假设我在我们的办公室工作 使用 jdk7 或家 jdk8 我想要
  • 捆绑包无效负载原因:0x80070570

    维克斯 3 6 我正在尝试运行捆绑包
  • Python将列表重塑为ndim数组

    您好 我有一个长度为 2800 的平面列表 它包含 28 个变量中每个变量的 100 个结果 下面是 2 个变量的 4 个结果的示例 0 0 1 1 2 2 3 3 我想将列表重塑为数组 2 4 以便每个变量的结果都在单个元素中 0 1 2
  • 是否可以在 postgresql 命令中引用环境变量?

    例如 假设我想从运行 postgres 服务器的同一台计算机上的路径导入 CSV 文件 有一个环境变量MyPath在系统上设置为 path to my csv file 我可以轻松导入此 CSV 文件 如下所示 COPY MyTable F
  • Python 中 *tuple 和 **dict 是什么意思? [复制]

    这个问题在这里已经有答案了 正如 PythonCookbook 中提到的 可以添加在元组之前 什么是 意思是这里 第 1 18 章 将名称映射到序列元素 from collections import namedtuple Stock na
  • Weblogic 12c (12.1.3) - 字符串索引超出范围:51968

    我正在尝试部署一个Spring BootWeblogic 12 1 3 上的 Web 应用程序 当我从控制台部署时 我收到以下错误 在应用程序上 Message icon Error Unable to access the selecte
  • 如何区分页面刷新和关闭页面

    我有一个网络应用游戏 在游戏中我想拥有它 这样如果用户关闭页面或浏览器 它会自动注销 我尝试使用附加到窗口的 onbeforeunload 事件 window onbeforeunload function perform logout f
  • WCF / WebService充当MQ消息的侦听器?

    也许我找错了方向 但我有一组服务 WebAPI 和 WCF 它们使用 WebSphere MQ 与其他系统交互 这没有问题 直到我现在需要找到一种方法listening对于队列之一上的消息 这是否可能 或者我是否需要走 Windows 服务
  • Retrofit 2.0:无法为类创建调用适配器

    我正在使用 Retrofit 2 0 beta1 如何发出简单的同步请求并将 JSON 响应反序列化为 POJO 列表 这是我的界面 public interface ArticlesAPI GET articles List
  • Python递归挑战[已关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我目前正在上Python和计算理论入门课 最近期中考试中有一道难题我根本无法解决 它涉及为添加数字的程序编写代码 我相信这个问题应该使
  • 通过 api 访问 Wikimedia Commons 上的版权信息

    我想使用 MediaWiki API 来获取图像的版权信息 当您单击维基百科中的图像时 将打开包含该图像的页面 其中包含 更多详细信息 按钮 单击此按钮 您将进入一个包含 在网络上使用此文件 链接的页面 单击此链接 运行脚本 stockPh
  • C# 中析构函数有必要吗?

    我有一个担忧 我是计算机科学专业的一年级学生 通常我在课堂上很好奇 但老师并不总是有答案 或者并不总是知道答案 C 中析构函数有必要吗 我的意思是 如果我必须像通常对构造函数那样实现析构函数方法 这是一个好的做法还是我可以避免它并且垃圾收集
  • macOS 钥匙串 ACL 如何确定哪些应用程序具有访问权限?

    当应用程序将项目保存到钥匙串时 macOS 会将该应用程序添加到访问控制列表中 以便您的应用程序稍后可以访问它 如果您尝试从其他应用程序访问该项目 macOS 将显示系统提示 询问用户是否允许访问 这是有记录的here https deve
  • sockaddr_in 未声明的标识符

    我正在遵循 beej 的网络指南 进展非常顺利 因为我对一切都很了解 而且他解释得很好 然而 当我想测试他向我展示的一些很酷的东西时 这是行不通的 我不确定 sockaddr in 到底在哪里声明 但也许这里有人会帮助我 这是我到目前为止所
  • Office 文档提示登录匿名 SharePoint 网站

    我有一个配置为匿名访问的 MOSS 07 站点 该站点内有一个文档库 也启用了匿名访问 当匿名用户单击该库中的 PDF 文件时 他或她可以毫无问题地阅读或下载该文件 当用户单击 Office 文档时 系统会提示他或她登录框 用户可以在不登录
  • 内联 MSIL/CIL

    我创建了以下简单方法 public static void Main Console WriteLine Hello world Console ReadKey true 然后我使用ILSpy获取MSIL代码 method public h
  • System.InvalidOperationException:无法解析类型依赖注入.net core web api的服务

    System InvalidOperationException 尝试激活 Pwc EMSWebapi UserManagementController 时无法解析类型 Pwc EMSWebapi IUserManagementServic
  • 如何从一组用户中提取电子邮件

    If I do User all pluck email 然后就可以正常工作了 但如果我这样做 arr Array new arr User all and then arr pluck email 这引发了以下错误 undefined m
  • 重写构造函数类

    下面是我的代码 我不明白这是什么错误 有谁可以指导一下吗 class State static String country static String capital State Constructor country America s
  • 连接两个向量同时转换一个向量的元素的最佳方法是什么?

    假设我有 std vector