程序完成后分支预测器条目失效?

2023-11-27

我试图了解分支预测器条目何时无效。

以下是我所做的实验:

Code1:

start_measure_branch_mispred()
while(X times):
 if(something something):
  do_useless()
 endif
endwhile
end_measurement()
store_difference()

因此,我多次运行此代码。我可以看到,第一次运行后,错误预测率降低了。分支预测器学习如何正确预测。但是,如果我一次又一次地运行这个实验(即通过写./experiment到终端),所有第一次迭代都是从高误预测率开始的。因此,在每次执行时,这些分支预测单元conditional branches均作废。我在用nokaslr我已经禁用了ASLR。我还在一个独立的核心上运行了这个实验。我已经运行了这个实验几次,以确保这是这种行为(即不是因为噪音)。

我的问题是:程序停止执行后CPU是否会使分支预测单元失效?或者说这是什么原因造成的呢?

我做的第二个实验是:

Code 2:

do:
    start_measure_branch_mispred()
    while(X times):
      if(something something):
        do_useless()
      endif
    endwhile
    end_measurement()
    store_difference()
while(cpu core == 1)

在这个实验中,我从两个不同的终端运行不同的进程。第一个固定到core 1这样它将在核心 1 上运行,并且它将执行此实验,直到我停止它(通过杀死它)。然后,我从另一个终端运行第二个进程,并将该进程固定到不同的内核。由于该进程位于不同的核心中,因此它只会执行 do-while 循环 1 次。如果第二个进程固定到第一个进程的同级核心(相同的物理核心),我发现在第一次迭代中,第二个进程的猜测几乎正确。如果我将第二个进程固定到不是第一个进程同级的另一个核心,那么第二个进程的第一次迭代会产生更高的错误预测。这是预期的结果,因为同一物理核心上的虚拟核心共享相同的分支预测单元(这是我的假设)。因此,第二个过程有利于经过训练的分支预测单元,因为它们具有相同的虚拟地址并映射到相同的分支预测单元条目。

据我了解,由于 CPU 尚未完成第一个进程(执行繁忙循环的核心 1 进程),因此分支预测条目仍然存在,第二个进程可以从中受益。但是,在第一个实验中,从一次又一次的实验中,我得到了更高的错误预测。

编辑:由于其他用户要求提供代码,就在这里。您需要下载性能事件头代码从这里

编译:$(CXX) -std=c++11 -O0 main.cpp -lpthread -o experiment

代码:

#include "linux-perf-events.h"

#include <algorithm>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <vector>

// some array
int arr8[8] = {1,1,0,0,0,1,0,1};

int pin_thread_to_core(int core_id){            
    int retval;     
    int num_cores = sysconf(_SC_NPROCESSORS_ONLN);      
    if (core_id < 0 || core_id >= num_cores)            
        retval = EINVAL;                                
    cpu_set_t cpuset;                                   
    CPU_ZERO(&cpuset);                                  
    CPU_SET(core_id, &cpuset);                          
    retval = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
    return retval;
}

void measurement(int cpuid, uint64_t howmany, int* branch_misses){

    int retval = pin_thread_to_core(cpuid);
    if(retval){
        printf("Affinity error: %s\n", strerror(errno));
        return;
    }

    std::vector<int> evts;
    evts.push_back(PERF_COUNT_HW_BRANCH_MISSES); // You might have a different performance event!

    LinuxEvents<PERF_TYPE_HARDWARE> unified(evts, cpuid); // You need to change the constructor in the performance counter so that it will count the events in the given cpuid

    uint64_t *buffer = new uint64_t[howmany + 1];
    uint64_t *buffer_org; // for restoring
    buffer_org = buffer;
    uint64_t howmany_org = howmany; // for restoring

    std::vector<unsigned long long> results;
    results.resize(evts.size());

    do{
        for(size_t trial = 0; trial < 10; trial++) {

            unified.start();
            // the while loop will be executed innerloop times
            int res;
            while(howmany){
                res = arr8[howmany & 0x7]; // do the sequence howmany/8 times
                if(res){
                    *buffer++ = res;
                }       
                howmany--;
            }
            unified.end(results);
            // store misses
            branch_misses[trial] = results[0];
            // restore for next iteration
            buffer = buffer_org;
            howmany = howmany_org;
        }
    }while(cpuid == 5); // the core that does busy loop

    // get rid of optimization
    howmany = (howmany + 1) * buffer[3];
    branch_misses[10] = howmany; // last entry is reserved for this dummy operation

    delete[] buffer;

}
void usage(){
    printf("Run with ./experiment X \t where X is the core number\n");
}
int main(int argc, char *argv[]) {
    // as I have 11th core isolated, set affinity to that
    if(argc == 1){
        usage();
        return 1;
    }

    int exp = 16; // howmany

    int results[11];
    int cpuid = atoi(argv[1]); 

    measurement(cpuid, exp, results);

    printf("%d measurements\n", exp);

    printf("Trial\t\t\tBranchMiss\n");
    for (size_t trial = 0; trial < 10; trial++)
    {
        printf("%zu\t\t\t%d\n", trial, results[trial]);
    }
    return 0;
}

如果您想尝试第一个代码,只需运行./experiment 1两次。它将与第一个代码执行相同的操作。

如果您想尝试第二个代码,请打开两个终端,然后运行./experiment X在第一个中,然后运行./experiment Y在第二个中,X 和 Y 是 cpuid。

请注意,您可能没有相同的性能事件计数器。另请注意,您可能需要更改 busyloop 中的 cpuid。


因此,我进行了更多的实验来减少噪音的影响(无论是来自_start until main()函数或来自syscalls and interrupts这可能发生在两个程序执行之间(系统调用和中断),这可能会破坏分支预测器。

这是修改后的实验的伪代码:

int main(int arg){ // arg is the iteration
   pin_thread_to_isolated_core()
   for i=0 to arg:
     measurement()
     std::this_thread::sleep_for(std::chrono::milliseconds(1)); // I put this as it is
   endfor
   printresults() // print after all measurements are completed
}

void measurement(){
   initialization()
   for i=0 to 10:
      start_measurement()
      while(X times) // for the results below, X is 32
        a = arr8[an element] //sequence of 8,
        if(a is odd)
           do_sth()
        endif
      endwhile
      end_measurement()
      store_difference()
   endfor
}

并且,这些是结果:

例如,我将迭代次数指定为 3

Trial           BranchMiss
RUN:1
    0           16
    1           28
    2           3
    3           1
    ....  continues as 1
RUN:2
    0           16   // CPU forgets the sequence
    1           30
    2           2
    3           1
    ....  continues as 1
RUN:3
    0           16
    1           27
    2           4
    3           1
    ....  continues as 1

因此,即使是毫秒的睡眠也会干扰分支预测单元。为什么会这样?如果我不在这些测量之间设置睡眠,CPU 可以正确猜测,即 Run2 和 Run3 将如下所示:

RUN:2
    0           1   
    1           1
    ....  continues as 1
RUN:3
    0           1
    1           1
    ....  continues as 1

我相信我减少了分支处决_start到测量点。尽管如此,CPU 仍然忘记了经过训练的东西。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

程序完成后分支预测器条目失效? 的相关文章

  • 为什么我应该使用内联代码? [复制]

    这个问题在这里已经有答案了 我是一名 C C 开发人员 这里有几个始终困扰我的问题 常规 代码和内联代码之间有很大区别吗 主要区别是什么 内联代码只是宏的一种 形式 吗 选择内联代码时必须进行什么样的权衡 Thanks 表现 正如之前的答案
  • 将 ARGB 拆分为字节值

    我有一个 ARGB 值存储为 int 类型 它是通过调用 ToArgb 来存储的 我现在想要来自 int 值的各个颜色通道的字节值 例如 int mycolor 16744448 byte r g b a GetBytesFromColor
  • 如何创建语法突出显示文本框[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何使用 C Net 创建语法突出显示文本框 Take 闪烁网 http scintillanet codeplex com 并采取其
  • 为什么使用数组索引循环数组比指针访问慢?

    我正在读Kochan的书 Programming in C 在第 14 页的 指针和数组 部分中 264 他说 一般来说 索引数组的过程比执行索引过程花费更多的时间 访问指针内容的过程 其实这也是主要原因之一 为什么使用指针来访问数组的元素
  • 如何转发声明要在 unique_ptr 的标准容器中使用的类

    在智能指针的标准容器中使用它时 是否可以避免完整的类定义可见 例如 我无法编译以下内容 include
  • C 中的 '\0' 和 printf()

    在 C 入门课程中 我了解到在存储字符串时存储空字符 0在它的最后 但是如果我想打印一个字符串怎么办 printf hello 虽然我发现它并没有结束 0通过以下声明 printf d printf hello Output 5 但这似乎不
  • C 链表销毁函数

    我正在尝试学习 C 和很多人一样 我对指针有点困惑 无论如何 我创建了一个递归函数来销毁我的链表 但是正如我调试的那样 当我从函数返回时 列表的头部不应该为空 所以我猜这是对指针的一些基本误解 这是函数 void destroy struc
  • 为什么 ObservableCollection 有两个集合构造函数?

    The 可观察集合 T https msdn microsoft com en us library ms668604类有两个构造函数 可以在其中传递项目集合 一个构造函数接受一个IEnumerable T 另一个List T 鉴于List
  • UI 线程正在阻塞调用 COM 对象的后台线程

    我正在开发一个通过第三方 COM 库与外部设备通信的应用程序 我试图让与设备的所有通信都通过后台线程 以防止通信问题搞砸我的应用程序 并消除在 UI 线程中进行通信所引入的一些其他复杂性 问题是 每当发生导致主 UI 线程阻塞的情况 即调用
  • 微软怎么能说WinAPI中一个字的大小是16位呢?

    我刚刚开始学习WinAPI 在MSDN中 对WORD数据类型提供了以下解释 WORD16 位无符号整数 范围是十进制 0 到 65535 该类型在 WinDef h 中声明如下 typedef 无符号短 WORD 很简单 而且它与我一直在使
  • 将视频上传/保存到数据库或文件系统

    我以前从未尝试过保存视频 所以我对此了解不多 我知道如果视频很小 我可以转换为字节数组并保存到数据库 但是为了提高效率 我想了解如何将任何上传的视频保存到我的服务器文件中 然后只保存该文件的文件路径我的数据库表中的视频 我完全不知道如何开始
  • ef core 在更新数据库期间不使用 ASPNETCORE_ENVIRONMENT

    我使用 Visual Studio 通过一定的迁移来更新我的所有环境 使用下面的命令效果很好 update database Migration initMigrationProduct c ProductContext Environme
  • 为什么最小的 int -2147483648 的类型为“long”? [复制]

    这个问题在这里已经有答案了 对于一个学校项目 我必须编写 C 函数 printf 的代码 一切进展顺利 但有一个问题我找不到好的答案 所以我来了 printf PRINTF d t d n 2147483648 告诉我 gcc Werror
  • 链接到ntdll.lib并调用ntdll.dll内部的函数

    我最近正在对私有 API 进行一些研究 我尝试调用诸如NtOpenFile在 ntdll dll 中LoadLibrary and GetProcAddress在运行时 幸运的是 它成功了 今天早上我在电脑上进行了文件搜索 发现ntdll
  • 使用“const cv::Mat &”、“cv::Mat &”、“cv::Mat”或“const cv::Mat”作为函数参数的区别?

    我已经彻底搜索过 但没有找到一个简单的答案 传递 opencv 矩阵 cv Mat 作为函数的参数 我们传递一个智能指针 我们对函数内部的输入矩阵所做的任何更改也会改变函数范围之外的矩阵 我读到 通过将矩阵作为 const 引用传递 它不会
  • 在链表程序中使用模板时重载 C++ 中的 << 运算符

    我正在尝试实现一个链接列表 但是当我尝试重载 include
  • 使用 DataGridViewCheckboxCell 真正禁用 DataGridView 中的复选框

    有谁知道如何使用 DataGridViewCheckboxCell 禁用 DataGridView 中的复选框 我可以将其设置为只读 并设置背景颜色 但我无法让复选框本身显示为禁用状态 有什么想法吗 Guess 你必须自己画 http so
  • 如何使用 .NET 捕获我的桌面视频?

    我想知道是否有任何方法可以使用 NET 捕获我的桌面的视频 截屏视频 我并不是在寻找截屏软件 而只是在寻找一种可以让我自己生成桌面视频的技术 我想过拍摄多个屏幕截图 但我不确定如何以编程方式生成带有图像序列的视频 有人有主意吗 Thanks
  • 打印任何类型的数组和列表的通用方法[重复]

    这个问题在这里已经有答案了 每当我调试一段涉及整数 双精度 字符串等数组或列表的代码时 有时我更喜欢打印它们 我为此所做的是为不同类型编写重载的 printArray printList 方法 for e g 我可能有这 3 种方法来打印各
  • 推断“x => { throw .. }”的 Lambda 与重载方法中的 Func 匹配吗?

    我不明白为什么 C 最终在以下 LINQPad 代码中执行不正确的扩展方法 void Main Actual Sync Action Expected Sync Action Run x gt x Dump Actual Async Tas

随机推荐

  • Array.Find 和 IndexOf 用于查找完全相同对象的多个元素

    我无法获取完全相同对象的多个元素的当前元素的索引 b A D B D C E D F b contains D 替代版本 b A D B D C E D F Array FindAll b Predicate String args 0 c
  • BPMS 还是只是简单的编程?

    在实现业务流程时 您更喜欢什么 从开发人员的角度来看 业务流程管理系统 BPMS 还是您最喜欢的带有所需工具和框架 例如报告工具 的 IDE 从您的角度来看 与具有您个人工具和框架的 IDE 相比 BPMS 的最大好处是什么 好的 也许我应
  • Python Pandas,应用函数

    我正在尝试使用 apply 来避免iterrows 函数中的迭代器 然而 pandas 方法的记录很少 我找不到如何使用它的示例 除了蹩脚的 apply sq rt 在文档中 没有关于如何使用参数等的示例 不管怎样 这里有一个关于我尝试做的
  • 如何将 git-replace 推送到远程仓库?

    我使用 git Replace 来替换分支 没有共同祖先 22b2b25 来替换 master 中的提交 我希望这种改变是永久性的 我对以前的历史不感兴趣 在下面的输出中 前 5 个提交来自原始 master 分支 后 2 个来自不同的分支
  • appengine-maven-plugin 配置选项,例如 jvm 标志

    从1 7 4版本开始 Google App Engine 的官方 appengine maven plugin 是由 Google 发布的 它有一个任务 appengine devserver 来启动本地开发服务器 这个插件似乎没有任何 M
  • 带有 Kubernetes 客户端插件的 Jenkins - NoSuchMethodError

    将 jenkins 插件 Kubernetes Client 升级到版本 1 30 3 也适用于 1 31 1 后 当我开始构建时 我在 jenkins 日志中收到以下异常 Timer task org csanchez jenkins p
  • 删除流星身份验证的登录令牌

    我不确定以前是否有人问过这个问题 但是loginTokens变得非常大并且没有任何清理会使我的数据库大小增加 对此我们正在做什么 其他人正在做什么来管理这个问题 我指的是默认的Meteor users services其中有一个loginT
  • 使用Cgroups限制cpu使用

    我正在尝试使用 cgroups 来限制 CPU 使用率 我正在使用本指南https access redhat com documentation en US Red Hat Enterprise Linux 6 html Resource
  • 无法从使用本地类型参数化的另一个包中实现来自另一个包的泛型类型的特征

    此测试代码 playpen use std fmt Display Formatter Error struct MyLocalType type MyResult Result
  • 在java中绘制圆的切片?

    我想通过一个实心圆来表示一个计时器 该实心圆是在计时器的过程中逐段完全绘制的 IE 如果在 4 秒计时器内每 1 秒填充一次圆圈 第一个将显示四分之一圆 然后是半圆 然后是四分之三 最后是一个完整的圆 有没有办法在java中绘制这些圆的切片
  • 在 hbase shell 上执行 ValueFilter 或 ColumnFilter

    谁能告诉我如何从 hbase shell 命令行执行限定符过滤器或 ValueFilter 它与使用任何编程语言进行编码的方式非常相似 例如 import org apache hadoop hbase filter CompareFilt
  • Hadoop mapReduce 如何在 HDFS 中仅存储值

    我用它来删除重复的行 public class DLines public static class TokenCounterMapper extends Mapper
  • 字符串池行为

    我读了这个关于Java字符串池的问题并了解字符串池的基本概念 但仍然不了解其行为 第一 如果直接赋值并且 s1 和 s2 都引用池中的同一个对象 它就可以工作 String s1 a bc String s2 ab c System out
  • 阻止元素参与文本选择

    我有一些源代码 pre code 行号在单独的 div 选择文本后 行号会随之出现 并随后被复制 即使我选择源代码块上方和下方的元素 是否有任何方法可以防止行号成为选择的一部分 为了那些关闭 JavaScript 的人的利益 我想避免使用
  • System.arrayCopy 很慢

    我一直在尝试测量 System arrayCopy 与 Arrays copyOf 的性能 以便正确选择其中之一 只是为了基准测试 我还添加了手动复制 结果令我惊讶 显然我错过了一些非常重要的东西 你能告诉我它是什么吗 实现如下 见前4种方
  • 为什么长长联合成员的对齐比包含的联合/结构更大?它是否正确?

    From 这个问题人们可能会开始相信 一个联盟的联盟不小于其单个成员的最大联盟 但我有一个问题long long输入 gcc g 完整的例子可以找到here 但以下是我的问题的相关部分 union ull long long m struc
  • C# 加权随机数

    我需要游戏编程方面的帮助 你打开一个箱子 并以给定的概率找到一个物品 物品 机会 A 10 B 30 C 60 Random random new Random int x random Next 1 101 if x lt 11 Numb
  • Ruby 拒绝正确划分

    我只是想得到一个百分比 irb main 001 0 gt 25 50 100 gt 0 正如我的计算器所证实的那样 这绝对应该等于 50 将相同的方程复制并粘贴到 gcalc 中 为什么 Ruby 拒绝这样做 它正在做整数除法 基本上 2
  • LINQ 列表到句子格式(插入逗号和“and”)

    我有一个 linq 查询 它执行一些简单的操作 例如 var k people Select x gt new x ID x Name 然后我想要一个函数或 linq lambda 或者使用逗号和 and 以句子格式输出名称的东西 1 Jo
  • 程序完成后分支预测器条目失效?

    我试图了解分支预测器条目何时无效 以下是我所做的实验 Code1 start measure branch mispred while X times if something something do useless endif endw