C++温暖标准矢量

 皮天荷冰兰8_47 发布于 2023-02-10 13:15

为什么第二次填充std :: vector更快?即使从开始预留空间?

int total = 1000000;

struct BaseClass {
  float m[16];
  int id;

  BaseClass(int _id) { id = _id; }
};

int main() {

  std::vector ar;
  ar.reserve(total);

  {
    auto t_start = std::chrono::high_resolution_clock::now();
    for (int var = 0; var < total; ++var) {
      ar.emplace_back(var);
    }
    auto t_end = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast(
                     t_end - t_start).count() << "\n";
    ar.clear();
  }

  {
    auto t_start = std::chrono::high_resolution_clock::now();
    for (int var = 0; var < total; ++var) {
      ar.emplace_back(var);
    }
    auto t_end = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast(
                     t_end - t_start).count() << "\n";
    ar.clear();
  }

  {
    auto t_start = std::chrono::high_resolution_clock::now();
    for (int var = 0; var < total; ++var) {
      ar.emplace_back(var);
    }
    auto t_end = std::chrono::high_resolution_clock::now();
    std::cout << std::chrono::duration_cast(
                     t_end - t_start).count() << "\n";
    ar.clear();
  }

  return 0;
}

在线预览:http: //coliru.stacked-crooked.com/a/229e4ba47adddb1a

结果:
118
23
21

PS我问为什么它变得更快如果向量减速的唯一原因是分配/重新分配.我们在开始时分配了数组BEFORE.

1 个回答
  • 第一次运行比另外两次运行慢的原因是运行时尚未从OS获取内存页.

    我指示您的程序输出任务在开始时以及在上述三个阶段中的每个阶段之后所执行的主要和次要页面错误的数量.(注意:这适用于Linux.不知道它是否适用于您所使用的任何操作系统.)代码:

    注意:更新到最新,reserve()移动到顶部并包装在自己的getrusage调用中.

    #include <ctime>
    #include <chrono>
    #include <iostream>
    #include <vector>
    
    #include <sys/time.h>
    #include <sys/resource.h>
    
    using namespace std;
    
    int total = 1000000;
    
    struct BaseClass {
      float m[16];
      int id;
    
      BaseClass(int _id) { id = _id; }
    };
    
    int main() {
    
      std::vector<BaseClass> ar;
      struct rusage r;
      {
        auto t_start = std::chrono::high_resolution_clock::now();
         }
    
      getrusage(RUSAGE_SELF, &r);
      cout << "minflt: " << r.ru_minflt << " majflt: " << r.ru_majflt << endl;
    
      ar.reserve(total);
    
      getrusage(RUSAGE_SELF, &r);
      cout << "minflt: " << r.ru_minflt << " majflt: " << r.ru_majflt << endl;
    
      {
        auto t_start = std::chrono::high_resolution_clock::now();
        for (int var = 0; var < total; ++var) {
          ar.emplace_back(var);
        }
        auto t_end = std::chrono::high_resolution_clock::now();
        std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(
                         t_end - t_start).count() << "\n";
        ar.clear();
      }
    
      getrusage(RUSAGE_SELF, &r);
      cout << "minflt: " << r.ru_minflt << " majflt: " << r.ru_majflt << endl;
    
      {
        auto t_start = std::chrono::high_resolution_clock::now();
        for (int var = 0; var < total; ++var) {
          ar.emplace_back(var);
        }
        auto t_end = std::chrono::high_resolution_clock::now();
        std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(
                         t_end - t_start).count() << "\n";
        ar.clear();
      }
    
      getrusage(RUSAGE_SELF, &r);
      cout << "minflt: " << r.ru_minflt << " majflt: " << r.ru_majflt << endl;
    
      {
        auto t_start = std::chrono::high_resolution_clock::now();
        for (int var = 0; var < total; ++var) {
          ar.emplace_back(var);
        }
        auto t_end = std::chrono::high_resolution_clock::now();
        std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(
                         t_end - t_start).count() << "\n";
        ar.clear();
      }
    
      getrusage(RUSAGE_SELF, &r);
      cout << "minflt: " << r.ru_minflt << " majflt: " << r.ru_majflt << endl;
    
      return 0;
    }
    

    然后我把它放在我的盒子上.结果是启发:

    minflt: 343 majflt: 0
    minflt: 367 majflt: 0
    48    minflt: 16968 majflt: 0
    16
    minflt: 16968 majflt: 0
    15
    minflt: 16968 majflt: 0
    

    请注意,第一个测量的for-loop引发了超过16,000个小故障.这些故障使应用程序可以使用内存,并解决了运行时间较慢的问题.此后不会发生其他故障.相反,reserve()呼叫本身仅产生24个小故障.

    在大多数现代虚拟内存操作系统中,操作系统实现了惰性内存分配,即使其上运行的软件没有.当运行时从OS请求额外的内存时,操作系统会记录该请求.如果请求成功,则运行时现在可以使用新的虚拟地址范围.(细节因所调用的API和操作系统而异,但实质上是相同的.)操作系统可能会将虚拟地址范围指向一个标记为只读的零填充页面.

    操作系统并没有必然使这些页面立即可用的任务.而是,操作系统等待,直到任务实际尝试写入它分配的内存.此时,OS分配物理页面以支持分配给任务的虚拟页面.在UNIX用语中注册为"次要错误".这个过程可能很昂贵.

    这是你的任务正在测量的懒惰分配.

    为了证明这一点,我也做了一个strace应用程序.有意义的部分如下.

    getrusage(RUSAGE_SELF, {ru_utime={0, 0}, ru_stime={0, 0}, ...}) = 0
    fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe3aa339000
    write(1, "minflt: 328 majflt: 0\n", 22) = 22
    mmap(NULL, 68001792, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe3a551c000
    getrusage(RUSAGE_SELF, {ru_utime={0, 0}, ru_stime={0, 0}, ...}) = 0
    write(1, "minflt: 352 majflt: 0\n", 22) = 22
    write(1, "52\n", 3)                     = 3
    getrusage(RUSAGE_SELF, {ru_utime={0, 30000}, ru_stime={0, 20000}, ...}) = 0
    write(1, "minflt: 16953 majflt: 0\n", 24) = 24
    write(1, "20\n", 3)                     = 3
    getrusage(RUSAGE_SELF, {ru_utime={0, 50000}, ru_stime={0, 20000}, ...}) = 0
    write(1, "minflt: 16953 majflt: 0\n", 24) = 24
    write(1, "15\n", 3)                     = 3
    getrusage(RUSAGE_SELF, {ru_utime={0, 70000}, ru_stime={0, 20000}, ...}) = 0
    write(1, "minflt: 16953 majflt: 0\n", 24) = 24
    munmap(0x7fe3a551c000, 68001792)        = 0
    exit_group(0)                           = ?
    

    如您所见,任务mmap通过前两个getrusage系统调用之间的调用分配内存.然而,这一步只发生了24次小故障.因此,即使C++ 不是懒惰,Linux也很懒惰地为这项任务提供内存.

    具体来说,第一个mmap调用似乎为第一个write消息分配了一个I/O缓冲区.第二次mmap调用(分配68001792字节)发生第二次getrusage调用之前.然而,在这次运行中,您可以看到两者之间仅发生了24个额外故障.

    你们中间的鹰派人士会注意到这次行动的数字与我上面展示的数字略有不同.我已多次运行此可执行文件,并且每次都会略微移动数字.但是,他们总是处于同一个笼子里.

    2023-02-10 13:16 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有