小编Bee*_*ope的帖子

英特尔失去了周期?rdtsc和CPU_CLK_UNHALTED.REF_TSC之间的不一致

在最近的CPU上(至少在过去十年左右),除了各种可配置的性能计数器之外,英特尔还提供了三个固定功能硬件性能计数器.三个固定柜台是:

INST_RETIRED.ANY
CPU_CLK_UNHALTED.THREAD
CPU_CLK_UNHALTED.REF_TSC
Run Code Online (Sandbox Code Playgroud)

第一个计算退役指令,第二个计算实际周期,最后一个是我们感兴趣的."英特尔软件开发人员手册"第3卷的描述如下:

当核心未处于暂停状态而不处于TM停止时钟状态时,此事件计算TSC速率下的参考周期数.核心在运行HLT指令或MWAIT指令时进入暂停状态.此事件不受核心频率变化(例如,P状态)的影响,但计数与时间戳计数器的频率相同.当核心未处于暂停状态而不处于TM stopclock状态时,此事件可以估计经过的时间.

因此,对于CPU绑定循环,我希望该值与从中读取的自由运行TSC值相同rdstc,因为它们应该仅针对暂停的循环指令或"TM stopclock state"是什么发散.

我使用以下循环测试它(整个独立演示在github上可用):

for (int i = 0; i < 100; i++) {
    PFC_CNT cnt[7] = {};

    int64_t start = nanos();
    PFCSTART(cnt);
    int64_t tsc =__rdtsc();
    busy_loop(CALIBRATION_LOOPS);
    PFCEND(cnt);
    int64_t tsc_delta   = __rdtsc() - tsc;
    int64_t nanos_delta = nanos() - start;

    printf(CPU_W "d" REF_W ".2f" TSC_W ".2f" MHZ_W ".2f" RAT_W ".6f\n",
            sched_getcpu(),
            1000.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC] / nanos_delta,
            1000.0 * tsc_delta / nanos_delta,
            1000.0 * CALIBRATION_LOOPS / nanos_delta,
            1.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/tsc_delta); …
Run Code Online (Sandbox Code Playgroud)

performance x86 x86-64 cpu-architecture rdtsc

27
推荐指数
1
解决办法
1300
查看次数

%对Python中的字符串做了什么?

我没有找到运算符%的文档,因为它在Python中的字符串上使用.有人知道文档的位置吗?

python string documentation operators

25
推荐指数
4
解决办法
4万
查看次数

有效地将无符号值除以2的幂,四舍五入

我想实现的无符号整数除法通过两个任意功率,围捕,高效.所以我想要的,数学上是ceiling(p/q)0.在C中,不利用受限域的strawman实现q可能类似于以下函数1:

/** q must be a power of 2, although this version works for any q */
uint64_t divide(uint64_t p, uint64_t q) {
  uint64_t res = p / q;
  return p % q == 0 ? res : res + 1;
} 
Run Code Online (Sandbox Code Playgroud)

...当然,我实际上并不想在机器级别使用除法或mod,因为即使在现代硬件上也需要很多周期.我正在寻找使用轮班和/或其他一些廉价操作的力量减少 - 利用q2的力量这一事实.

You can assume we have an efficient lg(unsigned int x) function, which returns the base-2 log of x …

c optimization x86 bit-manipulation division

25
推荐指数
7
解决办法
1033
查看次数

英特尔Skylake上的商店循环出乎意料的糟糕和奇怪的双峰性能

我看到一个简单的存储循环出乎意料地表现不佳,这个存储循环有两个存储:一个具有16字节的正向步长,另一个总是位于同一位置1,如下所示:

volatile uint32_t value;

void weirdo_cpp(size_t iters, uint32_t* output) {

    uint32_t x = value;
    uint32_t          *rdx = output;
    volatile uint32_t *rsi = output;
    do {
        *rdx    = x;
        *rsi = x;

        rdx += 4;  // 16 byte stride
    } while (--iters > 0);
}
Run Code Online (Sandbox Code Playgroud)

在汇编这个循环可能3看起来像:

weirdo_cpp:

...

align 16
.top:
    mov    [rdx], eax  ; stride 16
    mov    [rsi], eax  ; never changes

    add    rdx, 16

    dec    rdi
    jne    .top

    ret
Run Code Online (Sandbox Code Playgroud)

当访问的存储区域在L2中时,我希望每次迭代运行少于3个周期.第二个商店只是一直在同一个位置,应该添加一个周期.第一个商店意味着从L2引入一条线,因此每4次迭代也会驱逐一条线.我不确定你如何评估L2成本,但即使你保守估计L1只能在每个周期中执行以下操作之一:(a)提交商店或(b)从L2接收一行或(c)将一条线驱逐到L2,对于stride-16商店流,你会得到1 + 0.25 + …

optimization performance x86 assembly x86-64

25
推荐指数
2
解决办法
1629
查看次数

C中重叠对象的语义是什么?

考虑以下结构:

struct s {
  int a, b;
};
Run Code Online (Sandbox Code Playgroud)

通常为1,此结构的大小为 8,对齐方式为 4。

如果我们创建两个struct s对象(更准确地说,我们将两个这样的对象写入分配的存储空间),并且第二个对象与第一个对象重叠会怎样?

char *storage = malloc(3 * sizeof(struct s));
struct s *o1 = (struct s *)storage; // offset 0
struct s *o2 = (struct s *)(storage + alignof(struct s)); // offset 4

// now, o2 points half way into o1
*o1 = (struct s){1, 2};
*o2 = (struct s){3, 4};

printf("o2.a=%d\n", o2->a);
printf("o2.b=%d\n", o2->b);
printf("o1.a=%d\n", o1->a);
printf("o1.b=%d\n", o1->b);
Run Code Online (Sandbox Code Playgroud)

这个程序的未定义行为有什么问题吗?如果是这样,它在哪里变得未定义?如果不是UB,是否保证始终打印以下内容:

o2.a=3
o2.b=4
o1.a=1
o1.b=3
Run Code Online (Sandbox Code Playgroud)

特别是,我想知道o1 …

c struct pointers language-lawyer c11

25
推荐指数
1
解决办法
931
查看次数

为什么Arrays.equals(char [],char [])比其他所有版本快8倍?

短篇故事

根据我与几个不同的Oracle和OpenJDK的实现测试,似乎Arrays.equals(char[], char[])是某种大约快8倍比其他类型的所有其他变体.

图1

如果您的应用程序的性能在很大程度上相关0与平等比较阵列,这意味着你很可能要强制所有的数据到char[],只是为了得到这个神奇的性能提升.

很长的故事

最近我写了一些高性能代码,用于Arrays.equals(...)比较用于索引到结构的键.密钥可能很长,并且通常仅在后面的字节中有所不同,因此这种方法的性能非常重要.

在一个点我用类型的键char[],但作为推广服务的一部分,并且避免从底层的来源一些副本byte[]ByteBuffer,我改变这对byte[].突然2,许多基本操作的表现下降了约3倍.我将其追溯到上述事实:Arrays.equals(char[], char[])似乎在所有其他Arrays.equals()版本中享有特殊状态,包括在short[]语义上相同的一个版本(并且可以使用相同的底层代码实现,因为签名不会影响equals的行为).

所以我写了一个JMH基准测试的所有原始变种Arrays.equals(...)1char[]变异击碎所有其它的,如上图所示.

现在,~8x变种的这种优势并没有扩大到更小或更大的阵列 - 但它仍然更快.

对于小型阵列,似乎常数因素开始占主导地位,对于较大的阵列,L2/L3或主内存带宽开始起作用(您可以在前面的图中清楚地看到后者的影响,其中int[]尤其是long[]阵列开始降低大尺寸的性能).下面是相同的测试,但是有一个较小的小数组和较大的大数组:

图2

在这里,char[]仍然踢屁股,只是没有像以前那么多.小数组(仅16个元素)的每个元素时间大约是标准时间的两倍,可能是由于函数开销:在大约0.5 ns /元素时,char[]整个调用的变量仍然只需要大约7.2纳秒,或大约19纳秒在我的机器上循环 - 所以少量的方法开销会大量削减运行时间(同样,基准开销本身就是几个循环).

在大端,缓存和/或内存带宽是一个驱动因素 - long[]变体几乎是变体的2 倍int[].的short[],尤其是byte[]变种都不是很有效的(他们的工作设置仍然适用于L3在我的机器).

char[]与其他所有应用程序之间的差异非常大,以至于对于依赖于阵列比较的应用程序(对于某些特定域而言,这实际上并不常见),尝试将所有数据char[]用于利用是值得的.呵呵.

是什么赋予了?是否char得到特殊待遇,因为它是某些String …

java arrays optimization performance

24
推荐指数
2
解决办法
1958
查看次数

函数指针强制指令管道清除吗?

现代CPU具有广泛的流水线操作,也就是说,它们在实际执行指令之前很久就会加载必要的指令和数据.

有时,加载到管道中的数据会失效,必须清除管道并重新加载新数据.重新填充管道所需的时间可能相当长,并导致性能下降.

如果我在C中调用一个函数指针,那么管道是否足够智能以实现管道中的指针是一个函数指针,并且它应该跟随该指针用于下一个指令?或者是否有一个函数指针导致管道清除并降低性能?

我在C中工作,但我想这在C++中更为重要,因为许多函数调用都是通过v-tables进行的.


编辑

@JensGustedt写道:

要成为函数调用的真正性能,您调用的函数必须非常简短.如果您通过测量代码来观察这一点,那么您最终应该重新审视您的设计以允许内联调用

不幸的是,这可能是我陷入的陷阱.

出于性能原因,我编写了小而快的目标函数.

但是它被函数指针引用,因此它可以很容易地被其他函数替换(只需使指针引用一个不同的函数!).因为我通过函数指针引用它,所以我认为它不能内联.

所以,我有一个非常简短,没有内联的功能.

c performance x86 pipeline function-pointers

23
推荐指数
2
解决办法
3249
查看次数

mfence和asm volatile之间的差异("":::"memory")

据我所知,mfence硬件内存屏障asm volatile ("" : : : "memory")是一个编译器障碍.但是,可以asm volatile ("" : : : "memory")用来代替mfence.

我迷惑的原因是这个链接

x86 gcc memory-barriers

23
推荐指数
3
解决办法
2万
查看次数

每个c ++成员函数是否隐含地将"this"作为输入?

当我们在c ++中为类创建一个成员函数时,它有一个隐式的额外参数,它是一个指向调用对象的指针 - 被称为this.

对于任何函数都是如此,即使它不使用this指针也是如此.例如,给定班级

class foo
{
private:
    int bar;
public:
    int get_one()
    {
      return 1;  // Not using `this`
    }
    int get_bar()
    {
        return this->bar;  // Using `this`
    }
}
Run Code Online (Sandbox Code Playgroud)

两个函数(get_oneget_bar)都将this作为隐式参数,即使其中只有一个实际使用它吗?
这样做似乎有点浪费.

注意:我理解正确的做法是做get_one()静态,答案可能取决于实现,但我只是好奇.

c++ performance this member-functions language-lawyer

22
推荐指数
2
解决办法
1268
查看次数

是否保证C++标准库容器调用可替换的新函数?

如果我替换所有operator new我可以签名的签名,至少在我测试的实现上,我看到标准容器调用我的替换版本来分配内存.

这是否由标准保证?也就是说,实现使用优化版本是不合法的,该版本没有将我的替换函数称为标准容器下的内存?

c++ language-lawyer c++11

22
推荐指数
2
解决办法
1935
查看次数