- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我真的不知道问题的根源是否真的与 POD 类型有关,但至少我可以看出那里的区别。我想要实现的是,我的模板 Pod
类在性能方面表现得像一个简单的内置类型(例如 float)。
所以我目前拥有的是一个带有标签和值类型的模板类:
template<typename TTag, typename TValue>
class Pod
{
public:
Pod() = default;
Pod( TValue value ) : m_value{ value } {};
TValue& value() noexcept
{
return m_value;
}
TValue const& value() const noexcept
{
return m_value;
}
Pod& operator+=( Pod const& src ) noexcept
{
m_value += src.m_value;
return *this;
}
Pod& operator*=( TValue const& src ) noexcept
{
m_value *= src;
return *this;
}
private:
TValue m_value{};
};
template<typename TTag, typename TValue>
Pod<TTag, TValue>
operator+( Pod<TTag, TValue> lhs, Pod<TTag, TValue> const& rhs ) noexcept
{
lhs += rhs;
return lhs;
}
template<typename TTag, typename TValue>
Pod<TTag, TValue> operator*( Pod<TTag, TValue> lhs, TValue const& rhs ) noexcept
{
lhs *= rhs;
return lhs;
}
现在让我们进行一个简单的测试操作,例如:(A)
std::vector<Pod<A_tag, float>> lhs( size );
std::vector<Pod<A_tag, float>> rhs( size );
std::vector<Pod<A_tag, float>> res( size );
for ( std::size_t i = 0; i < size; ++i )
{
res[ i ] = lhs[ i ] + rhs[ i ];
}
并将其与:(B) 进行比较
std::vector<float> lhs( size );
std::vector<float> rhs( size );
std::vector<float> res( size );
for ( std::size_t i = 0; i < size; ++i )
{
res[ i ] = lhs[ i ] + rhs[ i ];
}
我在 googlebenchmark
测试用例中注意到,(B) 比 (A) 快大约 3 倍。如果我将 (A) 更改为: (C)
std::vector<Pod<A_tag, float>> lhs( size );
std::vector<Pod<A_tag, float>> rhs( size );
std::vector<Pod<A_tag, float>> res( size );
auto plhs = &( lhs[ 0 ].value() );
auto prhs = &( rhs[ 0 ].value() );
for ( std::size_t i = 0; i < size; ++i )
{
res[ i ].value() = plhs[ i ] + prhs[ i ];
}
我可以为 (C) 实现与 (B) 类似的性能。
现在我想到了两个问题:
Pod
,使其在性能方面与底层值类型相似。如果有帮助,我可以使用 C++17。我使用 Visual Studio 2019 Release 模式/O2 测试了所有内容。
感谢您的帮助!
最佳答案
Is variant (C) legal regarding standard or is it actually UB or something similar.
变体 (C) 在技术上是 UB - 对任何 i
的严格别名违规>= 1. 您正在遍历指向 float
的指针住在里面Pod
就好像它本身就在一个数组中一样。但是Pod<...>
和 float
是不兼容的类型。
此外,它不可移植 - 在 VC++ 中可能没问题,但技术上可以在 Pod
末尾进行填充,所以不能保证 float
s 将被排列成没有间隙。
Is there any way to define the template class
Pod
that it behaves similar to the underlying value type in regards of performance.
是的,您可以在第一个版本中禁用自动矢量化,然后所有版本的性能都相似:)
#pragma loop( no_vector )
基本上 MSVC 目前只能自动向量化非常简单的循环。我们可以看到here它自动矢量化了第一个版本(如果我们可以这样调用它的话):
$LL25@Baseline:
movss xmm0, DWORD PTR [rax+r9*4]
addss xmm0, DWORD PTR [r10+r9*4]
movss DWORD PTR [rdx+r9*4], xmm0
movss xmm1, DWORD PTR [r10+r9*4+4]
addss xmm1, DWORD PTR [rax+r9*4+4]
movss DWORD PTR [rdx+r9*4+4], xmm1
movss xmm0, DWORD PTR [rax+r9*4+8]
addss xmm0, DWORD PTR [r10+r9*4+8]
movss DWORD PTR [rdx+r9*4+8], xmm0
movss xmm1, DWORD PTR [rax+r9*4+12]
addss xmm1, DWORD PTR [r10+r9*4+12]
movss DWORD PTR [rdx+r9*4+12], xmm1
add r9, 4
cmp r9, 9997 ; 0000270dH
jb SHORT $LL25@Baseline
cmp r9, 10000 ; 00002710H
jae $LN23@Baseline
但只是围绕着 Pod
发疯版本,不幸的是:
$LL4@PodVec:
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax-12]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax-12]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax-12], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax-8]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax-8]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax-8], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax-4]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax-4]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax-4], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax+4]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax+4]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax+4], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax+8]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax+8]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax+8], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax+12]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax+12]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax+12], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax+16]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax+16]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax+16], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax+20]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax+20]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax+20], xmm0
mov rax, QWORD PTR [rdx]
movss xmm0, DWORD PTR [r9+rax+24]
mov rax, QWORD PTR [rcx]
addss xmm0, DWORD PTR [r9+rax+24]
mov rax, QWORD PTR [r8]
movss DWORD PTR [r9+rax+24], xmm0
add r9, 40 ; 00000028H
cmp r9, 40012 ; 00009c4cH
jb $LL4@PodVec
我试图以各种合法的方式帮助它解决问题,方法是简化代码,使其最容易优化:
#include <vector>
#include <benchmark/benchmark.h>
template<typename TValue>
struct Pod {
public:
Pod() noexcept {}
Pod(TValue value) noexcept : m_value{ value } {}
Pod operator+ (Pod src) const noexcept {
return m_value + src.m_value;
}
private:
TValue m_value{};
};
constexpr std::size_t size = 10000;
inline void Baseline(std::vector<float>& lhs, std::vector<float>& rhs, std::vector<float>& res) {
for (std::size_t i = 0; i < size; ++i)
{
res[i] = lhs[i] + rhs[i];
}
}
inline void PodVec(std::vector<Pod<float>>& lhs, std::vector<Pod<float>>& rhs, std::vector<Pod<float>>& res) {
for (std::size_t i = 0; i < size; ++i)
{
res[i] = lhs[i] + rhs[i];
}
}
inline void PodPtr(Pod<float>* lhs, Pod<float>* rhs, Pod<float>* res) {
for (std::size_t i = 0; i < size; ++i)
{
res[i] = lhs[i] + rhs[i];
}
}
using Iter = std::vector<Pod<float>>::iterator;
inline void PodIter(Iter lhs, Iter rhs, Iter res, Iter endres) {
for (; res != endres; lhs++, rhs++, res++)
{
*res = *lhs + *rhs;
}
}
void BaselineTest(benchmark::State& state) {
std::vector<float> lhs(size), rhs(size), res(size);
for (auto _ : state) {
Baseline(lhs, rhs, res);
benchmark::DoNotOptimize(res);
}
}
BENCHMARK(BaselineTest);
void PodVecTest(benchmark::State& state) {
std::vector<Pod<float>> lhs(size), rhs(size), res(size);
for (auto _ : state) {
PodVec(lhs, rhs, res);
benchmark::DoNotOptimize(res);
}
}
BENCHMARK(PodVecTest);
void PodPtrTest(benchmark::State& state) {
std::vector<Pod<float>> lhs(size), rhs(size), res(size);
for (auto _ : state) {
PodPtr(&lhs[0], &rhs[0], &res[0]);
benchmark::DoNotOptimize(res);
}
}
BENCHMARK(PodPtrTest);
void PodIterTest(benchmark::State& state) {
std::vector<Pod<float>> lhs(size), rhs(size), res(size);
for (auto _ : state) {
PodIter(begin(lhs), begin(rhs), begin(res), end(res));
benchmark::DoNotOptimize(res);
}
}
BENCHMARK(PodIterTest);
但不幸的是,运气不佳(奇怪的是,每个版本的性能都不同):
-------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------
BaselineTest 1824 ns 1842 ns 373333
PodVecTest 6166 ns 6278 ns 112000
PodPtrTest 5185 ns 5162 ns 112000
PodIterTest 4663 ns 4604 ns 149333
同时GCC 10无论哪个版本都没有问题:
----------------------------------------------------
Benchmark Time CPU Iterations
----------------------------------------------------
BaselineTest 1469 ns 1469 ns 422409
PodVecTest 1464 ns 1464 ns 484971
PodPtrTest 1424 ns 1424 ns 491200
PodIterTest 1420 ns 1420 ns 495973
这就是 GCC 循环程序集的样子:
.L6:
movss xmm0, DWORD PTR [rcx+rax*4]
addss xmm0, DWORD PTR [rsi+rax*4]
movss DWORD PTR [rdx+rax*4], xmm0
add rax, 1
cmp rax, 10000
jne .L6
有趣的是,Clang 11 在处理 vector<Pod>
时遇到了一些困难。版本也是 ( link ),这是出乎意料的。
故事的寓意:there are no zero-cost abstractions .在此示例中,如果您想从矢量化中受益,我认为除了使用“原始”vector<float>
别无他法。 s(或切换到 GCC)。
关于c++ - 与普通 POD 类型相比,如何克服仅包含一个 POD 成员的简单类的性能下降?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67359445/
我正在努力实现以下目标, 假设我有字符串: ( z ) ( A ( z ) ( A ( z ) ( A ( z ) ( A ( z ) ( A ) ) ) ) ) 我想编写一个正则
给定: 1 2 3 4 5 6
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
大家好,我卡颂。 Svelte问世很久了,一直想写一篇好懂的原理分析文章,拖了这么久终于写了。 本文会围绕一张流程图和两个Demo讲解,正确的食用方式是用电脑打开本文,跟着流程图、Demo一
身份证为15位或者18位,15位的全为数字,18位的前17位为数字,最后一位为数字或者大写字母”X“。 与之匹配的正则表达式: ?
我们先来最简单的,网页的登录窗口; 不过开始之前,大家先下载jquery的插件 本人习惯用了vs2008来做网页了,先添加一个空白页 这是最简单的的做法。。。先在body里面插入 <
1、MySQL自带的压力测试工具 Mysqlslap mysqlslap是mysql自带的基准测试工具,该工具查询数据,语法简单,灵活容易使用.该工具可以模拟多个客户端同时并发的向服务器发出
前言 今天大姚给大家分享一款.NET开源(MIT License)、免费、简单、实用的数据库文档(字典)生成工具,该工具支持CHM、Word、Excel、PDF、Html、XML、Markdown等
Go语言语法类似于C语言,因此熟悉C语言及其派生语言( C++、 C#、Objective-C 等)的人都会迅速熟悉这门语言。 C语言的有些语法会让代码可读性降低甚至发生歧义。Go语言在C语言的
我正在使用快速将 mkv 转换为 mp4 ffmpeg 命令 ffmpeg -i test.mkv -vcodec copy -acodec copy new.mp4 但不适用于任何 mkv 文件,当
我想计算我的工作簿中的工作表数量,然后从总数中减去特定的工作表。我错过了什么?这给了我一个对象错误: wsCount = ThisWorkbook.Sheets.Count - ThisWorkboo
我有一个 perl 文件,用于查看文件夹中是否存在 ini。如果是,它会从中读取,如果不是,它会根据我为它制作的模板创建一个。 我在 ini 部分使用 Config::Simple。 我的问题是,如果
尝试让一个 ViewController 通过标准 Cocoa 通知与另一个 ViewController 进行通信。 编写了一个简单的测试用例。在我最初的 VC 中,我将以下内容添加到 viewDi
我正在绘制高程剖面图,显示沿路径的高程增益/损失,类似于下面的: Sample Elevation Profile with hand-placed labels http://img38.image
嗨,所以我需要做的是最终让 regStart 和 regPage 根据点击事件交替可见性,我不太担心编写 JavaScript 函数,但我根本无法让我的 regPage 首先隐藏。这是我的代码。请简单
我有一个非常简单的程序来测量一个函数花费了多少时间。 #include #include #include struct Foo { void addSample(uint64_t s)
我需要为 JavaScript 制作简单的 C# BitConverter。我做了一个简单的BitConverter class BitConverter{ constructor(){} GetBy
已关闭。这个问题是 not reproducible or was caused by typos 。目前不接受答案。 这个问题是由拼写错误或无法再重现的问题引起的。虽然类似的问题可能是 on-top
我是 Simple.Data 的新手。但我很难找到如何进行“分组依据”。 我想要的是非常基本的。 表格看起来像: +________+ | cards | +________+ | id |
我现在正在开发一个 JS UDF,它看起来遵循编码。 通常情况下,由于循环计数为 2,Alert Msg 会出现两次。我想要的是即使循环计数为 3,Alert Msg 也只会出现一次。任何想法都
我是一名优秀的程序员,十分优秀!