第6.10章: fast_io 视图

English

本章为何存在

在上一章中,你了解到 C 风格数组参数以及 指针加长度组合容易出错:

现代 C++ 用视图类型解决了这个问题:轻量的、 不拥有内存的对象,它们引用已有内存而不复制它。

C++ 标准库提供了 std::span 与 std::string_view,但在 Windows x86-64 上, std::span 受到调用约定限制:

这一 ABI 行为由本教程的作者发现,并向微软报告。 在 ABI 修复之前,std::span 在 Windows x86-64 上 不如简单的 (ptr, n) 组合高效。

wincall 提案

为了解决这些长期存在的 ABI 低效问题,作者提出了一种 名为 wincall 的新调用约定,专为 Intel APX 下的 x86-64 Windows 设计。相关文档见:

微软开发者社区:wincall 提案

以及此处的讨论:

GCC 邮件列表讨论

wincall 的动机包括:

wincall 的目标是:

本章并不依赖 wincall,但理解 ABI 限制有助于解释为什么 std::span 今天在 Windows 上效率不高。

fast_io 的定位

fast_io 库并不“修复”Windows ABI。相反:

在本章中,你将学到如何使用以下 fast_io 视图类型:

这些类型比裸指针更安全,并避开了影响 std::span 的 Windows ABI 低效问题。

1. ::fast_io::span<T>

::fast_io::span<T> 是对 T 连续序列的轻量、 不拥有内存的视图。它存储:

与 std::span 不同,它有意在大小上避开 Windows x86-64 的 16 字节代价。由于 fast_io 不是 ABI 稳定的,它可以 选择一种在所有主流平台上都能高效通过寄存器传递的表示。

示例:传递 span


void process(::fast_io::span<int const> s)
{
    for(auto e : s)
        ::fast_io::io::println(e);
}

示例:从 vector 创建 span


::fast_io::vector v{1,2,3,4};
process(::fast_io::span{v});

示例:从裸数组创建 span


int arr[4]{1,2,3,4};
process(::fast_io::span{arr});

2. ::fast_io::string_view

::fast_io::string_view 类似 std::string_view, 但被设计为在所有主流平台上(包括 Windows x86-64)对 ABI 友好。 它:

示例


void greet(::fast_io::string_view name)
{
    ::fast_io::io::println("Hello, ", name);
}

3. ::fast_io::cstring_view

::fast_io::cstring_view 是对以空字符结尾的 C 字符串的 视图。它只存储一个指针,在需要时再计算长度。

它非常适合与 C API 或遗留代码交互。

示例


void debug(::fast_io::cstring_view s)
{
    ::fast_io::io::println("debug: ", s);
}

4. ::fast_io::index_span<T, N>

::fast_io::index_span<T, N> 是对恰好 N 个元素的 固定大小视图。它:

示例


void print_slice(::fast_io::index_span<std::size_t, 5> idx)
{
    for(std::size_t i{}, n{idx.size()}; i != n; ++i)
        ::fast_io::io::println(idx[i]);
}

5. 性能对比

std::span 与 ::fast_io::span 的关键区别 在于它们在不同 ABI 下的行为。

类型 大小 寄存器传递(SysV) 寄存器传递(Win64) 说明
T* 8 字节 是 是 基线指针语义
(T*, std::size_t) 16 字节 是 否 在 Win64 上通过内存传递
std::span<T> 16 字节 是 否 与 (ptr, n) 相同的代价
::fast_io::span<T> 8 字节 是 是 避开 Win64 的内存传递代价

::fast_io::span 有意在大小上模仿裸指针,在 ABI 层面 确保在所有主流平台上都能高效通过寄存器传递。

6. 何时使用哪种类型

要点