【问题标题】:Can I always rely on RVO by the compiler?我可以一直依赖编译器的 RVO 吗?
【发布时间】:2021-10-03 15:47:03
【问题描述】:

我有一个将文件读入字符串变量的函数。而且我不确定有什么更好的方法来实现它:


            void readFile(const std::filesystem::path& path, std::string& dest)
            {
                std::ifstream input(path, std::ios::in | std::ios::binary);
                if (!input.is_open())
                {
                    // throw exception
                }
                const std::size_t size = std::filesystem::file_size(path);
                std::string().swap(dest);
                dest.resize(size);
                input.read(&dest[0], size);
                input.close();
                if (input.fail() && !input.eof())
                {
                    // throw exception
                }
            }

或者:

            std::string readFile(const std::filesystem::path& path)
            {
                std::ifstream input(path, std::ios::in | std::ios::binary);
                if (!input.is_open())
                {
                    // throw exception
                }
                const std::size_t size = std::filesystem::file_size(path);
                std::string buffer(size, '\0');
                input.read(&buffer[0], size);
                input.close();
                if (input.fail() && !input.eof())
                {
                    // throw exception
                }
                return buffer;
            }

要读取的文件大小可能是几个字节到几百兆字节,因此读取操作可能非常昂贵。互联网上有很多建议总是更喜欢使用返回值的第二种方法,让编译器进行所需的优化。但是如果效率对我很重要,我可以完全依赖编译器吗?我可以确定编译器总是更喜欢 RVO 而不是要返回的数据的冗余副本吗?

【问题讨论】:

  • 首先,这是NRVO,不像RVO不是强制性的。但是即使 NRVO 失败,字符串也会被移动而不是复制,所以你不会丢失任何东西。
  • 从技术上讲,如果字符串足够短并且您的实现使用短字符串优化,则会发生副本,但这仍然是我的编码方式。我讨厌使用 out 参数。
  • 先写惯用代码,再写profile。过早的优化是万恶之源。
  • @SergeyA 这显然是双曲线的,我的实际建议会更加细微,不适合 StackOverflow 评论。但主要的一点是,在不分析代码的情况下说“我关心性能”是不合逻辑的。
  • @0x5453 没有人说不应该分析代码。然而,编写一个天生缓慢的代码是一种灾难——您将永远无法以后对其进行优化,因为它永远不会被分析器突出显示,因为它是许多小问题。这将是千刀万剐的死亡。人们应该始终知道他们的目标是哪种性能配置文件并相应地编写代码。

标签: c++ return-value-optimization


【解决方案1】:

正如其他人所说,不能保证编译器会对此进行优化,因为返回的值是一个命名变量(参见https://en.cppreference.com/w/cpp/language/copy_elision)。

但是,在我看来,如果启用优化,您几乎总是可以依赖 RVO,因为 RVO 是编译器可以进行的第一个也是最简单的优化之一。原因是,在底层架构中,没有办法直接返回一个大的值(大于 CPU 寄存器)。它的完成方式是调用者函数将为返回值分配一个局部变量,并将指向 this 的指针传递给被调用者。如此有效,编译器已经在堆栈上分配了一个局部变量,并将使用这个变量而不是分配另一个相同的变量。编译器将直接在调用者函数局部变量中就地构造您的字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-03
    • 2011-01-23
    • 1970-01-01
    • 2019-10-25
    • 2015-08-30
    • 2010-12-15
    • 1970-01-01
    相关资源
    最近更新 更多