我有一个在支持AVX-512的Intel机器上运行的进程,但是这个进程不直接使用任何AVX-512指令(asm或内在函数)并且编译时-mno-avx512f使编译器不插入任何AVX-512指令.
然而,它在减少的AVX turbo频率下无限运行.毫无疑问,有一个AVX-512指令在某个地方偷偷摸摸,通过一个库,(非常不可能)系统调用或类似的东西.
而不是尝试"二进制搜索"AVX-512指令来自哪里,有没有什么方法可以立即找到它,例如,捕获这样的指令?
操作系统是Ubuntu 16.04.
AXV2没有任何整数乘法,其源大于32位.它提供32 x 32 - > 32乘法,以及32 x 32 - > 64乘以1,但没有64位源.
假设我需要一个输入大于32位但小于或等于52位的无符号乘法 - 我可以简单地使用浮点DP乘法或FMA指令,并且当整数输入和输出时输出将是位精确的结果可以用52或更少的比特表示(即,在[0,2 ^ 52-1]范围内)?
如果我想要产品的所有104位更一般的情况怎么样?或整数乘积超过52位的情况(即,产品在位索引中的非零值> 52) - 但我只想要低52位?在后一种情况下,它MUL会给我更高的位并舍去一些低位(也许这就是IFMA帮助的?).
编辑:事实上,根据这个答案,也许它可以做任何高达2 ^ 53的事情- 我忘记了1在尾数之前隐含的领先有效地给了你一点.
1有趣的是,正如Mysticial 在评论中所解释的那样,64位产品PMULDQ操作的延迟是32位PMULLD版本的一半,吞吐量是32位版本的两倍.
考虑以下类似于构建器的类,它最终允许我构造一个具有成员变量的某些(运行时)值的对象,以及嵌入由多个(编译时)类型携带的某些行为.
相同的构建允许更新成员变量(通常的构建器模式),以及更改与构建器的类型携带状态相关联的模板类型参数(仅显示有几个模板类型参数和成员,但在实践中,会有更多):
template <typename T1 = DefaultT1, typename T2 = DefaultT2>
class Builder {
int param1, param2;
Builder(int param1, int param2) : param1{param1}, param2{param2} {}
public:
Builder() : Builder(default1, default2) {}
// methods to change param1 and param2 not shown
/* return a new Builder with T1 changed to the given T1_NEW */
template <typename T1_NEW>
Builder<T1_NEW, T2 > withT1() { return {param1, param2}; }
template <typename T2_NEW>
Builder<T1 , T2_NEW> withT2() { return {param1, param2}; …Run Code Online (Sandbox Code Playgroud) 请考虑以下简单代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <err.h>
int cpu_ms() {
return (int)(clock() * 1000 / CLOCKS_PER_SEC);
}
int main(int argc, char** argv) {
if (argc < 2) errx(EXIT_FAILURE, "provide the array size in KB on the command line");
size_t size = atol(argv[1]) * 1024;
unsigned char *p = malloc(size);
if (!p) errx(EXIT_FAILURE, "malloc of %zu bytes failed", size);
int fill = argv[2] ? argv[2][0] : 'x';
memset(p, fill, size);
int startms = cpu_ms();
printf("allocated %zu bytes …Run Code Online (Sandbox Code Playgroud) 我有兴趣知道是否有任何可行的方法来连续存储多态对象数组,这样virtual可以合法地调用公共基础上的方法(并将调度到子类中正确的重写方法).
例如,考虑以下类:
struct B {
int common;
int getCommon() { return common; }
virtual int getVirtual() const = 0;
}
struct D1 : B {
virtual int getVirtual final const { return 5 };
}
struct D2 : B {
int d2int;
virtual int getVirtual final const { return d2int };
}
Run Code Online (Sandbox Code Playgroud)
我想分配一个连续的D1和D2对象数组,并将它们视为B对象,包括调用getVirtual()哪些将根据对象类型委托给适当的方法.从概念上讲,这似乎是可能的:每个对象通常通过嵌入的vtable指针知道它的类型,因此您可以想象,将n个对象存储在数组中,并使用放置和初始化对象,并将指针转换为.不过,我很确定演员不合法.n * max(sizeof(D1), sizeof(D2)) unsigned charnewdeleteunsigned charB*
人们还可以想象创建一个联盟,如:
union Both { …Run Code Online (Sandbox Code Playgroud) 的方式std::variant分派给不同的访问者方法时std::visit被称为是非常合理的,当变异的替代品是完全不同的类型.本质上,特定vtable于访问者的特定是在编译时构建的,并且在一些错误检查1之后,通过基于当前索引表来查看适当的访问者函数,该当前index()解析为大多数平台上的间接跳转.
但是,如果备选方案共享一个公共基类,则调用(非虚拟)成员函数或使用访问者访问基类上的状态在概念上要简单得多:您总是调用相同的方法并且通常使用相同的指针2来基类.
尽管如此,实施结果同样缓慢.例如:
#include <variant>
struct Base {
int m_base;
int getBaseMember() { return m_base; }
};
struct Foo : public Base {
int m_foo;
};
struct Bar : public Base {
int m_bar;
};
using Foobar = std::variant<Foo,Bar>;
int getBaseMemVariant(Foobar& v) {
return std::visit([](auto&& e){ return e.getBaseMember(); }, v);
}
Run Code Online (Sandbox Code Playgroud)
为最新版本的在x86生成的代码gcc和clang类似3(示出铛):
getBaseMemVariant(std::__1::variant<Foo, Bar>&): # @getBaseMemVariant(std::__1::variant<Foo, Bar>&)
sub …Run Code Online (Sandbox Code Playgroud) 在C ++中,存在一个别名漏洞,该漏洞允许通过某些字符类型的指针来读取或写入任何对象的对象表示。
这仅适用于char和unsigned char还是适用于signed char?
英特尔PMU可用于测量每核读/写内存带宽使用情况吗?这里"存储器"意味着DRAM(即,不在任何高速缓存级别中命中).
我可以在编译时检测"函数参数" 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()等.
考虑以下几乎叶函数:
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)