我可以在编译时检测"函数参数" 1是否是编译时常量?
例如,一个函数print(int i)可以"constant 5"在被调用时打印,print(5)但"non-constant 5"如果被调用为print(i)where,i则是一些非常量变量.特别是,在"is constant"分支中,我应该能够将其i视为constexpr,包括将其用于模板参数等.
宏技巧,模板元编程和SFINAE技巧都可以.理想情况下它是可移植的,但是编译器特定的解决方案总比没有好.
如果存在"错误否定"则可以 - 即,如果常量值有时被检测为非常数(例如,禁用某些优化时).
如果解决方案可以检测到常量值何时间接传递给函数(例如,当一个常量值传递给调用的中间函数print并且随后内联将常量暴露给print)时,可以获得奖励积分.最后一种行为显然取决于优化.
如果它自然延伸到多个参数,则可获得双倍奖励
如果一个人可以使用和不带constexpr参数重载函数的版本,这可能是直截了当的,但你不能.
1我在这里引用"函数参数",因为解决方案并不严格要求在函数内(或在具有特殊参数的调用者/被调用者边界)检测此状态 - 它只需要像函数一样出现给调用者但是可以使用宏或其他技巧,如静态对象operator()等.
较新的Linux内核具有sysfs可调参数/proc/sys/kernel/perf_event_paranoid,允许用户调整perf_events非root用户的可用功能,更高的数字更安全(提供相应更少的功能):
从内核文档中,我们对各种值有以下行为:
perf_event_paranoid:
控制非特权用户对性能事件系统的使用(无CAP_SYS_ADMIN).默认值为2.
-1:允许所有用户使用(几乎)所有事件在没有CAP_IPC_LOCK的perf_event_mlock_kb之后忽略mlock限制
> = 0:没有CAP_SYS_ADMIN的用户不允许使用ftrace函数跟踪点不允许没有CAP_SYS_ADMIN的用户访问原始跟踪点
> = 1:禁止没有CAP_SYS_ADMIN的用户访问CPU事件
> = 2:禁止没有CAP_SYS_ADMIN的用户进行内核分析
我1在我的perf_event_paranoid文件中应该"禁止CPU事件访问" - 但这究竟是什么意思?
普通读数意味着无法访问CPU性能计数器事件(例如Intel PMU事件),但似乎我可以访问那些就好了.例如:
$ perf stat sleep 1
Performance counter stats for 'sleep 1':
0.408734 task-clock (msec) # 0.000 CPUs utilized
1 context-switches # 0.002 M/sec
0 cpu-migrations # 0.000 K/sec
57 page-faults # 0.139 M/sec
1,050,362 cycles # 2.570 GHz
769,135 instructions # 0.73 insn per cycle
152,661 branches # 373.497 M/sec
6,942 …Run Code Online (Sandbox Code Playgroud) 是否允许以下内容:
const int const_array[] = { 42 };
int maybe_inc(bool write, int* array) {
if (write) array[0]++;
return array[0];
}
int main() {
return maybe_inc(false, const_cast<int *>(const_array));
}
Run Code Online (Sandbox Code Playgroud)
特别地,它是确定以铸远的常量性const_array,将其定义为const,只要对象是不实际修改,如在实施例?
考虑以下三个方面struct:
class blub {
int i;
char c;
blub(const blub&) {}
};
class blob {
char s;
blob(const blob&) {}
};
struct bla {
blub b0;
blob b1;
};
Run Code Online (Sandbox Code Playgroud)
在int4 字节的典型平台上,大小、对齐方式和总填充1如下:
struct size alignment padding
-------- ------ ----------- ---------
blub 8 4 3
blob 1 1 0
bla 12 4 6
Run Code Online (Sandbox Code Playgroud)
blub和blob成员的存储之间没有重叠,即使大小 1blob原则上可以“适合” blub.
C++20 引入了no_unique_address属性,它允许相邻的空成员共享相同的地址。它还明确允许使用一个成员的填充来存储另一个成员的上述场景。来自cppreference(强调我的):
指示此数据成员不需要具有与其类的所有其他非静态数据成员不同的地址。这意味着如果成员有一个空类型(例如无状态分配器),编译器可能会优化它以不占用空间,就像它是一个空基一样。如果该成员不为空,则其中的任何尾部填充也可以重新用于存储其他数据成员。
事实上,如果我们在 上使用这个属性blub b0,大小会bla …
考虑以下几乎叶函数:
int almost_leaf(int* x) {
if (__builtin_expect(*x >= 0, true)) {
return *x;
}
return x_was_negative() + 1;
}
Run Code Online (Sandbox Code Playgroud)
它几乎是叶子,因为它不是严格意义上的叶子函数(它可能调用x_was_negativeis x 为负数,但__builtin_expect提示编译器return *x通常采用分支,这不涉及任何调用。
clang-16 像这样编译它:
almost_leaf(int*): # @almost_leaf(int*)
push rax
mov eax, dword ptr [rdi]
test eax, eax
js .LBB0_1
pop rcx
ret
.LBB0_1:
call x_was_negative()
inc eax
pop rcx
ret
Run Code Online (Sandbox Code Playgroud)
快速(预期)路径上的and (直到第一个的部分)在这里push是完全不必要的:堆栈未使用,并且不会进行需要“由于 ABI”而对齐的堆栈的调用。popret
最好将堆栈对齐到x_was_negative()调用的慢速路径上,就像 gcc 那样:
almost_leaf(int*):
mov eax, DWORD PTR [rdi] …Run Code Online (Sandbox Code Playgroud) AVX2有很多好东西.例如,它有很多指令,它们比它们的前体更加强大.Take VPERMD:它允许您从一个256位长的32位值向量中完全任意地广播/混洗/置换到另一个,并且在运行时1可以选择置换.在功能上,它废除了大量现有的旧解包,广播,置换,随机和移位指令3.
凉豆.
那么在哪里VPERMB?即,相同的指令,但在字节大小的元素上工作.或者,就此而言,VPERMW对于16位元素,在哪里?已经涉足x86程序集已经有一段时间了,很明显SSE PSHUFB指令几乎是有史以来最有用的指令之一.它可以进行任何可能的排列,广播或逐字节混洗.此外,它还可用于执行16个并行4位 - > 8位表查找2.
不幸的是,PSHUFB在AVX2中没有延伸到跨车道,所以它仅限于车道内行为.该VPERM指令能够做到跨洗牌(事实上,"烫发"和"SHUF"似乎是在指令助记符同义词?) -但被省略了8位和16位版本?
甚至似乎没有一种好的方法来模拟这个指令,而你可以轻松地模拟宽度较小的shuffles(通常,它甚至是免费的:你只需要一个不同的掩码).
我毫不怀疑英特尔已经意识到它的广泛使用PSHUFB,因此自然会出现为什么在AVX2中省略字节变体的问题.操作本质上难以在硬件中实现吗?是否有编码限制迫使其遗漏?
1通过在运行时选择,我的意思是定义混洗行为的掩码来自寄存器.这使得指令比采用立即随机掩码的早期变体更灵活,其方式与add更灵活的inc变换相比,或者变量比立即变换更灵活.
2或AVX2中的32个此类查找.
3较旧的指令偶尔会有用,如果它们的编码较短,或者避免从内存中加载掩码,但在功能上它们会被取代.
我是否可以编写一个带有参数T的模板函数,foo如果它存在T,则调用成员函数,如果它不调用自由函数foo(T)(如果两者都不存在则无法编译)?
就像是:
template<typename T>
int call_foo(T t) {
// if T::foo() exists
return t.foo();
// else return foo(t);
}
Run Code Online (Sandbox Code Playgroud)
相反的情况怎么样:foo在成员函数之前更喜欢自由函数?我不能使用C++ 11之后引入的任何功能.
AVX指令集引入了VPERMILPS,它似乎是SHUFPS的简化版本(对于两个输入寄存器相同的情况).
例如,以下说明:
c5 f0 c6 c1 00 vshufps xmm0,xmm1,xmm1,0x0
Run Code Online (Sandbox Code Playgroud)
可以替换为:
c4 e3 79 04 c1 00 vpermilps xmm0,xmm1,0x0
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,VPERMILPS版本需要额外增加一个字节并执行相同的操作.根据指令表,两条指令占用1个CPU周期并具有相同的吞吐量.
引入这种指令有什么意义?我错过了什么吗?
编辑: 有是这两个指令的区别之一.VPERMILPS将上部通道设置为零,而SHUFPS使它们不受影响.
也就是说,将其constexpr std::array<int,2>{1,2}传递给将吐出类型的某些函数或帮助器类std::integer_sequence<int, 1, 2>?
It seems easy to jump from the type world to the "constexpr value" world (e.g., to do the reverse conversion), but hard or impossible to do the reverse.
我经常发现自己想为a创建一个比较器对象,struct或者class只是提取一个类的成员并对其进行常规<比较.
例如:
struct student {
int id;
std::string name;
};
// sort by ID
std::sort(students.begin(), students.end(), [](const student& l, const student& r){ return l.id < r.id; });
Run Code Online (Sandbox Code Playgroud)
那里有很多样板,特别是因为我们必须重复声明l和r.标准库中是否有一种方法可以基于"提取器"函数创建比较器,该函数返回要比较的对象?
就像是:
std::sort(students.begin(), students.end(), compare_on([](const student& s){ return s.id; });
Run Code Online (Sandbox Code Playgroud)
我使用C++ 11的,但也有兴趣,如果有在以后的标准解决方案,不适用于C++ 11(所以我可以补充一下我的"的理由升级"列表).
我在这里要求使用单个成员作为合并,以及默认比较"小于",但是对于易于构成的技术的奖励点,例如允许您在字典顺序中使用两个字段,或者更改比较运算符.
c++ ×7
c++11 ×3
x86 ×3
assembly ×2
optimization ×2
avx ×1
avx2 ×1
c++14 ×1
c++20 ×1
clang ×1
comparison ×1
const ×1
const-cast ×1
constexpr ×1
intel ×1
intel-pmu ×1
linux-kernel ×1
perf ×1
performance ×1
profiling ×1
sfinae ×1
sse ×1
templates ×1
x86-64 ×1