在谈论多线程时,线程似乎通常被视为平等——与主线程相同,但在它旁边运行。
然而,在某些新处理器上,例如Apple M1芯片和即将推出的 Intel Alder Lake系列,并非所有线程的性能都与这些芯片的性能相同,因为这些芯片具有独立的高性能内核和高效、速度较慢的内核。
这并不是说还没有诸如超线程之类的东西,但这似乎对性能有更大的影响。
有没有办法查询std::thread的属性并强制它们在 C++ 中运行的核心?
似乎有两种类型的 C++。实用C++和语言律师C++。在某些情况下,能够将一种类型的位模式解释为一种不同的类型会很有用。浮点技巧就是一个显着的例子。让我们取著名的快速平方根反比(取自Wikipedia,又取自此处):
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); …Run Code Online (Sandbox Code Playgroud) 据我所知,标题中提到的所有技术都是看起来非常相似的渲染算法。所有基于光线的技术似乎都围绕着通过图像的每个像素投射光线来表示真实光线。这允许渲染非常逼真的图像。
\n事实上,我正在制作一个简单的程序,根据光线追踪在一个周末自行渲染此类图像。
\n现在的问题是我想以某种方式命名这个程序。我使用了术语 \xe2\x80\x9cray Tracer\xe2\x80\x9d,因为这是书中使用的术语。
\n然而,我听过很多不同的术语,我有兴趣知道光线追踪、光线匹配、光线投射、路径追踪和任何其他常见的光线相关算法之间到底有什么区别。我能够在网上找到这些技术的一些比较,但它们都只比较了其中的两种,并且一些定义重叠,所以我想问这个关于所有四种技术的问题。
\nOpenGL 和 Vulkan 都允许分别使用glMapBuffer和获取指向部分 GPU 内存的指针vkMapMemory。他们都给void*映射的内存一个。要将其内容解释为某些数据,必须将其强制转换为适当的类型。最简单的示例可能是转换为 afloat*以将内存解释为浮点数或向量或类似数组。
似乎任何类型的内存映射在 C++ 中都是未定义的行为,因为它没有内存映射的概念。但是,这并不是真正的问题,因为该主题超出了 C++ 标准的范围。但是,仍然存在一个问题volatile。
在链接的问题中,指针被额外标记为volatile因为它指向的内存内容可以以编译器在编译期间无法预料的方式进行修改。这似乎是合理的,尽管我很少看到人们volatile在这种情况下使用(更广泛地说,这个关键字现在似乎很少使用)。
同时在这个问题中,答案似乎是使用volatile是不必要的。这是因为他们所说的内存是映射使用的mmap,然后msync可以被视为修改内存,这类似于在 Vulkan 或 OpenGL 中显式刷新它。恐怕这不适用于 OpenGL 和 Vulkan。
如果内存被映射为未映射GL_MAP_FLUSH_EXPLICIT_BIT或根本VK_MEMORY_PROPERTY_HOST_COHERENT_BIT不需要刷新,则内存内容会自动更新。即使通过使用手动刷新内存,vkFlushMappedMemoryRanges或者glFlushMappedBufferRange这些函数实际上都没有将映射指针作为参数,因此编译器也不可能知道它们修改了映射内存的内容。
因此,是否有必要将指向映射 GPU 内存的指针标记为volatile?我知道从技术上讲这都是未定义的行为,但我问的是在实际硬件中实际需要什么。
顺便说一下,无论是Vulkan 规范还是OpenGL 规范都没有提到volatile限定符。
编辑:将内存标记为volatile会导致性能开销吗?
我想知道当前处理器上的二级缓存大小是多少。幸运的是,有一个名为的库cpu_features可以实现这一点,甚至更多。在内部,它使用cpuid指令来获取所有这些信息。
我想知道,如果我尝试在混合架构(例如 Alder Lake)上使用它会发生什么?其效率和性能核心具有不同的特点。我假设该cpuid指令返回有关当前线程运行的核心的信息。所以基本上,如果它被安排在性能核心上运行,它会描述性能核心。
这样做的问题是操作系统可以决定稍后将线程移动到效率核心,在这种情况下,从中接收到的任何信息都cpuid不再正确。并且该程序甚至不知道它已被迁移。这可以通过手动将线程“固定”到核心并设置其亲和力来解决。为了区分性能和效率核心,我可以按顺序将线程固定到每个核心,并比较cpuid.
不过,我想知道cpuid结果是否确实依赖于核心,以及是否有更好的方法来查询有关处理器的信息,而无需循环遍历所有核心,只需将它们分类为其中一个两类。
Linux 4.18 引入了rseq(2)系统调用。我在SO上发现只有一个问题提到了rseq,而且网上关于它的信息相对较少,所以我决定问一下。什么是可重新启动序列以及程序员如何使用它们?
我必须寻找restartable sequences: fast user-space percpu critical sections才能获得任何有意义的结果。我能够找到向内核添加相关功能的提交。进一步的研究让我看到了2013 年的演讲,我认为这是第一次介绍这个概念。许多工作是由 EfficiOS 公司的团队完成的。他们描述了向 Linux 内核贡献此功能的意图。
看起来这个功能很少有人知道,但显然它是用来优化TCMalloc 分配器的性能的。一般来说,看起来是某种并发优化。
尽管列出的来源提供了背景信息,但尚未对 SO 上提供的 RSEQ 进行解释。了解它们在实践中的其他用途以及如何使用会很有用。
假设我正在创建一个 C++ 作业系统。其中一部分是无锁多生产者单消费者队列。如何将rseq(2)系统调用的使用引入到我的代码中以潜在地提高其性能?
class mpsc_list_node
{
mpsc_list_node* _next;
template<typename T>
requires std::derived_from<T, mpsc_list_node>
friend class mpsc_list;
};
template<typename T>
requires std::derived_from<T, mpsc_list_node>
class mpsc_list
{
private:
std::atomic<T*> head{ nullptr };
private: …Run Code Online (Sandbox Code Playgroud) 似乎在 C++20 中引入了一种叫做“预期析构函数”的东西。在C++17 [class.dtor] 中:
- 在析构函数的声明中,声明符是以下形式的函数声明符 (11.3.5)
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier(opt) 属性说明符-seq(opt)
在C ++ 20这得到了改变,以这样:
- 其 declarator-id 具有以 ~ 开头的非限定 id 的声明声明了一个预期的析构函数;其声明符应为以下形式的函数声明符 ([dcl.fct])
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier(opt) 属性说明符-seq(opt)
那么这个“潜在的破坏者”是什么?那么标准似乎没有澄清,至少对我来说:
- 在类定义结束时,在该类中声明的预期析构函数之间执行重载解析,并带有空参数列表,以选择该类的析构函数,也称为选定的析构函数。如果重载解析失败,则程序格式错误。
引入“预期析构函数”这个新概念的原因是什么?它甚至意味着什么?它如何更改代码?它允许做什么?
我认为这可能是为了用于模板元编程,或者可能与 SFINAE 有什么关系,但这些只是猜测。
通常说一个static变量初始化被包装在一个中if以防止它被多次初始化。
对于这种情况和其他一次性条件,在第一次通过自我修改后让代码删除条件会更有效。
C++ 编译器是否允许生成这样的代码,如果不允许,为什么?我听说它可能会对缓存产生负面影响,但我不知道详细信息。
这个相关问题展示了如何使用指定数量的核心构建 CMake 项目。例如,如果我想使用 10 个核心,我可以像这样调用 CMake:
cmake --build . -j 10
Run Code Online (Sandbox Code Playgroud)
我的问题是:如何使用所有可用的核心进行构建。我实际上希望 CMake 自动检测我的核心数量并使用所有这些。
关于这个主题已经有一些问题(1、2、3 )。问题是,似乎没有一个明确的答案。有些答案建议( 1 , 2 ),有些答案建议( 1 , 2 )。其他选项包括、、或使用容器或成员类型。size_tptrdiff_tintuint32_tautodecltype.size()size_type
这个问题似乎不适合基于观点,但我认为事实并非如此。仅仅因为尚未就使用哪种类型达成共识,并不意味着不存在客观的答案。这是因为不同的选择不仅美观,而且实际上可以影响代码的行为。
例如,在循环条件中使用符号不匹配的索引变量类型将导致编译器警告,如下所示。此外,使用范围太小的类型可能会导致溢出,在有符号类型的情况下会导致 UB。同时,在某些情况下改变循环计数器类型可能会导致“疯狂的性能偏差”。
我还想找出最流行的(尽管不一定是最好的)创建 for 循环的方法,因此我使用 GitHub *搜索来查找。结果如下:
| 循环型 | GitHub 上的代码结果计数(平均值;“手动”循环 + 基于范围) |
|---|---|
for (int |
15.8m |
for (size_t |
11.6m |
for (auto |
7.5m |
for (uint32_t |
2.3m |
std::for_each |
501k |
for (ptrdiff_t |
98.7k |
for (decltype |
77.5k |
不同循环类型之间的出现次数肯定存在很大差异,但是,似乎没有明显的杰出领导者。
因此,我发布这个问题询问,在 C++ 的 for 循环中索引变量使用的最佳类型是什么,或者应该选择这种类型的规则或条件是什么?
*:GitHub 搜索工具每次都会为“代码结果”(计数)生成不同的结果,因此我对 26 个值进行了平均。由于搜索是基于文本的,因此它包括表单for (int i = 0; i < n; ++i)和的结果for …
c++ ×8
optimization ×2
performance ×2
algorithm ×1
apple-m1 ×1
assembly ×1
build ×1
c++20 ×1
casting ×1
cmake ×1
concurrency ×1
cpuid ×1
destructor ×1
for-loop ×1
gpu ×1
intel ×1
linux ×1
opengl ×1
raii ×1
raytracing ×1
rendering ×1
system-calls ×1
terminology ×1
type-punning ×1
types ×1
volatile ×1
vulkan ×1
x86 ×1