uninitialized_copy memcpy/memmove 优化

2024-05-03

我最近开始研究 MSVC 实现中的 STL。那里有一些不错的技巧,但是我不知道为什么使用以下标准。

The std::uninitialized_copy被优化为一个简单的memcpy/memmove如果满足某些条件。据我了解,输入范围可以是memcpy'd 到未初始化区域,如果目标类型为 Uis_trivially_copy_constructible来自源类型 T。

然而,MSVC 实现在选择之前会检查很多事情memcpy而不是元素的一对一复制构造。我不想在这里粘贴相关代码,如果有人感兴趣的话,我通过 Pastebin 分享它:https://pastebin.com/Sa4Q7Qj0 https://pastebin.com/Sa4Q7Qj0

的基本算法为uninitialized_copy是这样的(为了可读性省略了异常处理)

template <typename T, typename... Args>
inline void construct_in_place(T& obj, Args&&... args)
{
    ::new (static_cast<void*>(addressof(obj)) T(forward<Args>(args)...);
}

template <typename In, typename Out>
inline Out uninitialized_copy(In first, In last, Out dest)
{
    for (; first != last; ++first, ++dest)
        construct_in_place(*dest, *first);
}

这可以优化为memcpy/memmove如果复制构造不做任何“特殊”的事情(简单地复制构造)。

MS 的实施需要满足以下条件:

  • T 可以简单地分配给 U
  • T 可以简单地复制到 U
  • T 是微不足道的
  • 额外检查(如 sizeof(T) == sizeof(U))如果 T != U

因此,例如以下结构不能是memcpy'd:

struct Foo
{
    int i;
    Foo() : i(10) { }
};

但以下是可以的:

struct Foo
{
    int i;
    Foo() = default; // or simply omit
};

检查类型 U 是否可以从类型 T 简单地复制构造难道还不够吗?因为所有这些都是 uninitialized_copy 所做的。

例如,我不明白为什么 MS 的 STL 实现没有对以下内容进行 memcpy(注意:我知道原因,它是用户定义的构造函数,但我不明白其背后的逻辑):

struct Foo
{
    int i;

    Foo() noexcept
        : i(10)
    {
    }

    Foo(const Foo&) = default;
};

void test()
{
    // please forgive me...
    uint8 raw[256];
    Foo* dest = (Foo*)raw;
    Foo src[] = { Foo(), Foo() };

    bool b = std::is_trivially_copy_constructible<Foo>::value;  // true
    bool b2 = std::is_trivially_copyable<Foo>::value;           // true

    memcpy(dest, src, sizeof(src)); // seems ok

    // uninitialized_copy does not use memcpy/memmove, it calls the copy-ctor one-by-one
    std::uninitialized_copy(src, src + sizeof(src) / sizeof(src[0]), dest);
}

相关SO帖子:为什么 gcc 不在 std::uninitialized_copy 中使用 memmove? https://stackoverflow.com/questions/33482610/why-doesnt-gcc-use-memmove-in-stduninitialized-copy

Update

正如 @Igor Tandetnik 在评论中指出的那样,如果没有用户定义的复制构造函数,则不能安全地说类型 T 是可复制构造的。他提供了以下示例:

struct Foo
{
    std::string data;
};

在此示例中,没有用户定义的复制构造函数,并且它仍然不是普通的复制构造函数。感谢您的指正,我根据反馈修改了原帖。


uninitialized_copy有两个职责:首先,它必须确保正确的位模式进入目标缓冲区。其次,它必须开始生命周期该缓冲区中的 C++ 对象的数量。也就是说,它必须调用某种构造函数,除非 C++ 标准明确授予它跳过该构造函数调用的权限。

根据我非常不完整的研究,目前看来只有可以简单地复制 https://en.cppreference.com/w/cpp/types/is_trivially_copyable类型保证其位模式被保留memcpy/memmove; memcpping 任何其他类型的类型(即使它碰巧是普通复制构造和/或普通复制分配!)会正式产生未定义的行为。

而且,现在看来只有trivial https://en.cppreference.com/w/cpp/types/is_trivial类型可以在没有构造函数调用的情况下“突然出现”。 (P0593“隐式创建对象...” http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0593r5.html提出了这方面的很多改变,也许是在 C++2b 中。)

乔纳森·韦克利的评论libstdc++ 错误 68350 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68350似乎表明 GNU libstdc++ 试图遵守法律规定,永远不会“突然出现”任何非平凡类型的对象 - 尽管作为 C++ 实现,它们确实有自由度来利用特定于平台的行为表演的名称。我猜测 MSVC 出于类似的原因(无论这些原因是什么)也遵循类似的逻辑。


通过比较供应商的优化意愿,您可以看到供应商不愿意“将对象弹出存在”std::copy versus std::uninitialized_copy类类型“可以简单复制,但并不简单”。可简单复制意味着std::copy可以使用memcpy分配现有对象;但std::uninitialized_copy,为了让这些对象首先出现,仍然需要调用some循环中的构造函数 - 即使它是简单的复制构造函数!

class C { int i; public: C() = default; };
class D { int i; public: D() {} };
static_assert(std::is_trivially_copyable_v<C> && !std::is_aggregate_v<C>);
static_assert(std::is_trivially_copyable_v<D> && !std::is_aggregate_v<D>);

void copyCs(C *p, C *q, int n) {
    std::copy(p, p+n, q);  // GNU and MSVC both optimize
    std::uninitialized_copy(p, p+n, q);  // GNU and MSVC both optimize
}
void copyDs(D *p, D *q, int n) {
    std::copy(p, p+n, q);  // GNU and MSVC both optimize
    std::uninitialized_copy(p, p+n, q);  // neither GNU nor MSVC optimizes :(
}

你写了:

检查类型 U 是否可以从类型 T 简单地复制构造难道还不够吗?因为这就是 uninitialized_copy 所做的一切。

是的,但是当 T 和 U 是不同的,你不是在做“琐碎的复制构造”;你正在做一个“琐碎的构造”not复制构造。不幸的是 C++ 标准定义了is_trivially_constructible<T,U>意思是与人类所说的“琐碎”不同的东西!我的博文“Trivially-constructible-from”(2018 年 7 月) https://quuxplusone.github.io/blog/2018/07/03/trivially-constructible-from/给出这个例子:

assert(is_trivially_constructible_v<u64, u64b>);
// Yay!

using u16 = short;
assert(is_trivially_constructible_v<u64, u16>);
// What the...

assert(is_trivially_constructible_v<u64, double>);
// ...oh geez.

这解释了 MSVC 的一些

额外检查(如 sizeof(T) == sizeof(U))如果 T != U

具体来说,MSVC_Ptr_cat_helper<T*,U*>::_Really_trivial https://github.com/microsoft/STL/blob/4aaa0135d965e41690bbd555c71d74e34d459c26/stl/inc/xutility#L1382-L1417Trait 依赖于这些额外的检查来检测一些(但不是全部)常见情况,其中从 T 到 U 的转换在人类/按位意义上“确实”微不足道,而不仅仅是在 C++ 标准意义上微不足道。这允许 MSVC 优化复制数组int*到一个数组中const int*,这是 libstdc++ 无法做到的:

using A = int*;
using B = const int*;

void copyAs(A *p, B *q, int n) {
    std::uninitialized_copy(p, p+n, q);  // only MSVC optimizes
}
void copyBs(B *p, B *q, int n) {
    std::uninitialized_copy(p, p+n, q);  // GNU and MSVC both optimize
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

uninitialized_copy memcpy/memmove 优化 的相关文章

  • 如何使 Windows 窗体的关闭按钮不关闭窗体但使其不可见?

    该表单有一个 NotifyIcon 对象 当用户单击 关闭 按钮时 我希望表单不关闭而是变得不可见 然后 如果用户想再次查看该表单 可以双击系统托盘中的图标 如果用户想关闭表单 可以右键单击该图标并选择 关闭 有人可以告诉我如何使关闭按钮不
  • ASP.NET Core Serilog 未将属性推送到其自定义列

    我有这个设置appsettings json对于我的 Serilog 安装 Serilog MinimumLevel Information Enrich LogUserName Override Microsoft Critical Wr
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 从父类调用子类方法

    a doStuff 方法是否可以在不编辑 A 类的情况下打印 B did stuff 如果是这样 我该怎么做 class Program static void Main string args A a new A B b new B a
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • 将布尔参数传递给 SQL Server 存储过程

    我早些时候问过这个问题 我以为我找到了问题所在 但我没有 我在将布尔参数传递给存储过程时遇到问题 这是我的 C 代码 public bool upload false protected void showDate object sende
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • 在 Visual Studio 2008 上设置预调试事件

    我想在 Visual Studio 中开始调试程序之前运行一个任务 我每次调试程序时都需要运行此任务 因此构建后事件还不够好 我查看了设置的 调试 选项卡 但没有这样的选项 有什么办法可以做到这一点吗 你唯一可以尝试的 IMO 就是尝试Co
  • C 预处理器库

    我的任务是开发源分析工具C程序 并且我需要在分析本身之前预处理代码 我想知道什么是最好的图书馆 我需要一些重量轻 便于携带的东西 与其推出自己的 为什么不使用cpp这是的一部分gcc suite http gcc gnu org onlin
  • 如果使用 SingleOrDefault() 并在数字列表中搜索不在列表中的数字,如何返回 null?

    使用查询正数列表时SingleOrDefault 当在列表中找不到数字时 如何返回 null 或像 1 这样的自定义值 而不是类型的默认值 在本例中为 0 你可以使用 var first theIntegers Cast
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • 在数据库中搜索时忽略空文本框

    此代码能够搜索数据并将其加载到DataGridView基于搜索表单文本框中提供的值 如果我将任何文本框留空 则不会有搜索结果 因为 SQL 查询是用 AND 组合的 如何在搜索 从 SQL 查询或 C 代码 时忽略空文本框 private
  • 如何衡量两个字符串之间的相似度? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给定两个字符串text1 and text2 public SOMEUSABLERETURNTYPE Compare string t
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • 如何在 VBA 中声明接受 XlfOper (LPXLOPER) 类型参数的函数?

    我在之前的回答里发现了问题 https stackoverflow com q 19325258 159684一种无需注册即可调用 C xll 中定义的函数的方法 我之前使用 XLW 提供的注册基础结构 并且使用 XlfOper 类型在 V
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • 需要哪个版本的 Visual C++ 运行时库?

    microsoft 的最新 vcredist 2010 版 是否包含以前的版本 2008 SP1 和 2005 SP1 还是我需要安装全部 3 个版本 谢谢 你需要所有这些
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 为什么我收到“找不到编译动态表达式所需的一种或多种类型。”?

    我有一个已更新的项目 NET 3 5 MVC v2 到 NET 4 0 MVC v3 当我尝试使用或设置时编译出现错误 ViewBag Title财产 找不到编译动态表达式所需的一种或多种类型 您是否缺少对 Microsoft CSharp

随机推荐

  • 观察 CALayer 中的动画属性变化

    我有一个CABasicAnimation动画 a 的属性CALayer e g bounds origin 我希望能够观察属性随时间的变化 但还没有真正找到一种 100 有效的方法 我尝试使用 KVO 键值观察 presentationLa
  • 如何为 SSMS 2019 (v18) 创建扩展

    SQL Server Management Studio 18 RC1 https learn microsoft com en us sql ssms download sql server management studio ssms
  • 如何自动创建 WPF Viewmodel 属性

    我在 WPF 项目中使用 Visual Studio 17 和 Resharper 该项目向用户显示大量信息 因此我有很多如下所示的属性 private double foo public double Foo get return foo
  • 从 Restful 身份验证迁移到 Devise

    许多 Rails 2 3 应用程序正在使用 Restful Authentication 但该插件似乎与 Rails 3 存在一些问题 在升级到 Rails 3 时 我一直在使用 Devise 有什么办法可以顺利地从 Restful Aut
  • 明确的控制不会处置它们——风险是什么?

    有多个线程 a https stackoverflow com questions 1757116 remove tabpage dispose or clear or both b https stackoverflow com ques
  • 如何使用 JavaScript 或 jQuery 从 URL 下载文件?

    我使用 jQuery fileDownload 插件从 URL 下载文件 fileDownload url contentType text csv contentDisposition attachment filename url sp
  • 正确解析包含“+”字符的字段

    我遇到了一个奇怪的情况 我在其中重现了https github com lgueye uri parameters behavior https github com lgueye uri parameters behavior 自从我们迁
  • 加入或合并 pandas 中的覆盖

    我想对具有日期时间索引的数据框执行联接 合并 追加操作 假设我有df1我想添加df2到它 df2可以有更少或更多的列以及重叠的索引 对于索引匹配的所有行 如果df2具有相同的列df1 我想要的值df1被那些来自df2 我怎样才能获得想要的结
  • 在 ES6 Node.js 中导入“.json”扩展名会引发错误

    我们正在尝试使用 Node js 导出和导入 ES6 模块的新方法 对于我们来说 从package json文件 下面的代码应该做到这一点 import name version from package json 但是 执行时会抛出以下错
  • 如何在 Android 中嵌入和播放 YouTube 视频

    我们可以在 Android 应用程序中观看 YouTube 视频吗 我的意思是 如果我们有 YouTube 上的视频链接 我们可以在 VideoView 或其他小部件中播放它吗 有什么想法吗 答案很简单 是 请查看以下链接 如何在我的 An
  • 如何在fastapi中调用另一个api?

    我能够从另一个 API 获取一个 API 的响应 但无法将其存储在某处 在返回响应之前存储在文件或其他内容中 response RedirectResponse url apiname 我想访问带有标题和正文的发布请求 我想存储此响应内容而
  • Webview 不显示带有颜色的文本

    在我的应用程序中 我在 webview 中显示一些 Html 内容 String webViewConent this is some span style color 2ecc71 sample span string webView l
  • 使用 libsvm 交叉验证后重新训练

    我知道交叉验证用于选择好的参数 找到它们后 我需要在不使用 v 选项的情况下重新训练整个数据 但我面临的问题是 在使用 v 选项训练后 我得到了交叉验证精度 例如 85 没有模型 我看不到 C 和 gamma 的值 在这种情况下我该如何重新
  • 带数据注释的枚举类型的 Json.NET 自定义序列化

    我想序列化一个枚举类型 以便它返回一个数组 其中枚举作为对象 其中包含 值 名称 和数据注释值 我需要序列化方面的帮助 这是我到目前为止所做的 枚举 public enum Status Display Name Active status
  • 如何获取UIImage的大小(KB)

    我从以下位置获取图像didFinishPickingMediaWithInfo UIImage originalImage UIImage info valueForKey UIImagePickerControllerOriginalIm
  • Gatsby 在 graphql 查询返回后获取图像路径

    所以我用 Gatsby 和 Remark 写了一个博客网站 我的帖子结构如下 Library category name article name index md 这非常有效 使我能够制作类似的路径 category name artic
  • Tesseract OCR 将削减的 0 混淆为 8

    我已经在终点字体上训练了 tesseract 但无论如何 我都无法让它识别 0 我正在使用 jTessEditor 创建训练 tif 和框 即使在验证时 它也会将所有 0 读取为 8 我有什么遗漏的吗 下面是 0 的示例 它将其读作 8 我
  • 为 Pipenv 提供不同的源或 git url 以进行生产和开发

    我们正在使用Pipenv https github com pypa pipenv and Pipfiles https github com pypa pipfile用于管理我们的 Python 包需求 我们使用私有 GitLab 服务器
  • Azure Nvidia 中的 apt-update 出现公钥错误

    我在 AZURE 上启动了 NVIDIA VM 并尝试使用进行更新sudo apt update但给出错误 Hit 2 http azure archive ubuntu com ubuntu focal InRelease Hit 3 h
  • uninitialized_copy memcpy/memmove 优化

    我最近开始研究 MSVC 实现中的 STL 那里有一些不错的技巧 但是我不知道为什么使用以下标准 The std uninitialized copy被优化为一个简单的memcpy memmove如果满足某些条件 据我了解 输入范围可以是m