跳转至

Tutorials

Conda

conda

Anaconda和Miniconda都是针对数据科学和机器学习领域的Python发行版本,它们包含了许多常用的数据科学包和工具,使得安装和管理这些包变得更加简单。

解决了几个痛点:

  1. 不同python环境的切换(类似VirtualEnv)
  2. 高效的包管理工具(类似pip,特别是在Windows上好用)

anaconda

Anaconda是一个全功能的Python发行版本,由Anaconda, Inc.(前称Continuum Analytics)提供。

  • 它包含了Python解释器以及大量常用的数据科学、机器学习和科学计算的第三方库和工具,如NumPy、Pandas、Matplotlib、SciPy等。
  • Anaconda还包含一个名为Conda的包管理器,用于安装、更新和管理这些库及其依赖项。
  • Anaconda发行版通常较大(500MB),因为它预装了许多常用的包,适用于不希望从头开始搭建环境的用户。

Miniconda

Miniconda是Anaconda的轻量级版本(50MB),它也由Anaconda, Inc.提供。

  • 与Anaconda不同,Miniconda只包含了Python解释器和Conda包管理器,没有预装任何其他包。这意味着用户可以根据自己的需求手动选择要安装的包,从而实现一个精简而高度定制化的Python环境。
  • 对于希望从零开始构建数据科学环境或需要更细粒度控制的用户,Miniconda是一个很好的选择。

Install miniconda

According to the official website,

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
# choose local path to install, maybe ~/.local
# init = yes, will auto modified the .zshrc to add the miniconda to PATH

# If you'd prefer that conda's base environment not be activated on startup,
#    set the auto_activate_base parameter to false:
conda config --set auto_activate_base false

you need to close all terminal(all windows in one section including all split windows), and reopen a terminal will take effect;

Python on windows

ref

创建虚拟环境

使用以下命令创建一个名为"myenv"的虚拟环境(您可以将"myenv"替换为您喜欢的环境名称):

conda create --name myenv python=3.8

# list existed env
conda env list

激活环境

conda activate name

环境包的生成和使用

conda list -e > requirements.txt
conda install --yes --file requirements.txt

pyproject.toml

$ pip install .
ERROR: Directory '.' is not installable. Neither 'setup.py' nor 'pyproject.toml' found.

在conda命令无效时使用pip命令来代替

while read requirement; do conda install --yes $requirement || pip install $requirement; done < requirements.txt

The double pipe (“||”) is a control operator that represents the logical OR operation. It is used to execute a command or series of commands only if the previous command or pipeline has failed or has returned a non-zero status code.

保存和复制conda环境的配置

conda env export > freeze.yml
conda env create -f freeze.yml

复制(fork)已有的环境

conda create -n 新环境名称 --clone 原环境名称 --copy

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

https://blog.csdn.net/Mao_Jonah/article/details/89502380

Linux Terminal

导言

对程序员来说,一个好用、易用的terminal,就是和军人手上有把顺手的好枪一样。

基础知识

用户的环境变量和配置文件

在Linux系统中,用户的环境变量和配置文件可以在不同的节点生效。以下是这些文件的功能和它们生效的时机:

  1. /etc/environment:

    • 功能: 设置系统范围的环境变量。
    • 生效时机: 在用户登录时读取,但不会执行shell命令。它主要用于设置变量,如PATH、LANG等。
  2. /etc/profile:

    • 功能: 为系统的每个用户设置环境信息。
    • 生效时机: 当用户登录时,会读取并执行该文件中的配置。它是针对登录shell(例如,通过终端登录或ssh登录)的。
  3. /etc/profile.d/:

    • 功能: 存放多个脚本,这些脚本会被/etc/profile读取和执行。
    • 生效时机: 与/etc/profile相同,登录shell时执行。它使得系统管理员可以将不同的配置分散到多个文件中管理。
  4. /etc/bash.bashrc:

    • 功能: 为所有用户设置bash shell的配置。
    • 生效时机: 对于非登录shell(例如,打开一个新的终端窗口)时会读取并执行。
  5. ~/.profile:

    • 功能: 为单个用户设置环境信息。
    • 生效时机: 用户登录时读取并执行,主要针对登录shell。
  6. ~/.bashrc:

    • 功能: 为单个用户配置bash shell的设置。
    • 生效时机: 用户打开一个新的bash shell(非登录shell)时读取并执行。

总结

  • /etc/environment/etc/profile 主要用于系统范围的环境变量设置,前者不会执行shell命令,后者会执行。
  • /etc/profile.d/ 中的脚本作为 /etc/profile 的扩展,用于更灵活的管理配置。
  • /etc/bash.bashrc 适用于所有用户的bash配置,但只针对非登录shell。
  • ~/.profile~/.bashrc 适用于单个用户,前者用于登录shell,后者用于非登录shell。

通过这些文件,系统和用户可以灵活地设置和管理环境变量和shell配置,以满足不同的需求和使用场景。

\n \r 回车 换行
符号 ASCII码 意义
\n 10 换行NL: 本义是光标往下一行(不一定到下一行行首),n的英文newline,控制字符可以写成LF,即Line Feed
\r 13 回车CR: 本义是光标重新回到本行开头,r的英文return,控制字符可以写成CR,即Carriage Return

在不同的操作系统这几个字符表现不同:

  1. 在WIN系统下,这两个字符就是表现的本义,
  2. 在UNIX类系统,换行\n就表现为光标下一行并回到行首,
  3. 在MAC上,\r就表现为回到本行开头并往下一行,至于ENTER键的定义是与操作系统有关的。通常用的Enter是两个加起来。
\n: UNIX 系统行末结束符
\n\r: window 系统行末结束符
\r: MAC OS 系统行末结束符

终端命令行代理

在任意层级的SHELL配置文件里添加

export http_proxy=http://yourproxy:port
export https_proxy=http://yourproxy:port

写成bashrc的脚本命令

#YJH proxy
export proxy_addr=localhost
export proxy_http_port=7890
export proxy_socks_port=7890
function set_proxy() {
   export http_proxy=http://$proxy_addr:$proxy_http_port #如果使用git 不行,这两个http和https改成socks5就行
   export https_proxy=http://$proxy_addr:$proxy_http_port
   export all_proxy=socks5://$proxy_addr:$proxy_socks_port
   export no_proxy=127.0.0.1,.huawei.com,localhost,local,.local 
}
function unset_proxy() {
   unset http_proxy
   unset https_proxy
   unset all_proxy
}
function test_proxy() {
   curl -v -x http://$proxy_addr:$proxy_http_port https://www.google.com | egrep 'HTTP/(2|1.1) 200'
   # socks5h://$proxy_addr:$proxy_socks_port
}
# set_proxy # 如果要登陆时默认启用代理则取消注释这句

常用命令

check process create time

ps -eo pid,lstart,cmd |grep bhive
date

kill all process by name

 sudo ps -ef | grep 'bhive-re' | grep -v grep | awk '{print $2}' | sudo xargs -r kill -9

常见问题

鼠标滚轮输出乱码

滚轮乱码,是tmux set mouse on的原因

进入tmux后退出,并运行reset即可

sudo后找不到命令

当你使用sudo去执行一个程序时,处于安全的考虑,这个程序将在一个新的、最小化的环境中执行,也就是说,诸如PATH这样的环境变量,在sudo命令下已经被重置成默认状态了。

添加所需要的路径(如 /usr/local/bin)到/etc/sudoers文件"secure_path"下

Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
在用python使用curses写多进程进度条的时候,混乱输出

解决办法如下:

stdscr = curses.initscr() # 不要设置为全局变量
# 而且 使用set_win unset_win 保持区域换行的行为

参考文献

Zsim-tlb: bug

bug

zsim-tlb simulate in icarus0

pinbin: build/opt/zsim.cpp:816: LEVEL_BASE::VOID VdsoCallPoint(LEVEL_VM::THREADID): Assertion `vdsoPatchData[tid].level' failed.
Pin app terminated abnormally due to signal 6.

locate error

VOID VdsoCallPoint(THREADID tid) {
    //level=0,invalid
    assert(vdsoPatchData[tid].level);
    vdsoPatchData[tid].level++;
    // info("vDSO internal callpoint, now level %d", vdsoPatchData[tid].level); //common
}
  • vDSO (virtual dynamic shared object) is a kernel machanism for exporting a carefully set kernel space routines (eg. not secret api, gettid() and gettimeofday()) to user spapce to eliminate the performance penalty of user-kernel mode switch according to wiki. vDSO
  • You can use some __vdso_getcpu() C library, and kernel will auto move it to user-space
  • vDSO overcome vsyscall(first linux-kernel machanism to accelerate syscall) drawback.
  • In zsim, vDSO have only four function enum VdsoFunc {VF_CLOCK_GETTIME, VF_GETTIMEOFDAY, VF_TIME, VF_GETCPU};

vDSO simulate part

// Instrumentation function, called for EVERY instruction
VOID VdsoInstrument(INS ins) {
    ADDRINT insAddr = INS_Address(ins); //get ins addr
    if (unlikely(insAddr >= vdsoStart && insAddr < vdsoEnd)) {
        //INS is vdso syscall
        if (vdsoEntryMap.find(insAddr) != vdsoEntryMap.end()) {
            VdsoFunc func = vdsoEntryMap[insAddr];
            //call VdsoEntryPoint function
            //argv are: tid ,func(IARG_UINT32),arg0(LEVEL_BASE::REG_RDI),arg1(LEVEL_BASE::REG_RSI) 
            INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR) VdsoEntryPoint, IARG_THREAD_ID, IARG_UINT32, (uint32_t)func, IARG_REG_VALUE, LEVEL_BASE::REG_RDI, IARG_REG_VALUE, LEVEL_BASE::REG_RSI, IARG_END);
        } else if (INS_IsCall(ins)) {   //call instruction
            INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR) VdsoCallPoint, IARG_THREAD_ID, IARG_END);
        } else if (INS_IsRet(ins)) {    //Ret instruction
            INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR) VdsoRetPoint, IARG_THREAD_ID, IARG_REG_REFERENCE, LEVEL_BASE::REG_RAX /* return val */, IARG_END);
        }
    }

    //Warn on the first vsyscall code translation
    if (unlikely(insAddr >= vsyscallStart && insAddr < vsyscallEnd && !vsyscallWarned)) {
        warn("Instrumenting vsyscall page code --- this process executes vsyscalls, which zsim does not virtualize!");
        vsyscallWarned = true;
    }
}

INS_Address is from pin-kit, but INS_InsertCall is pin api.

try:

.level is just show the level of nested vsyscall. I think comment the assert which trigerd when callfunc before entryfunc is just fun.

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

Perf

perf 介绍

Perf是Linux下的一个性能分析工具(profiler)。基于Linux 内核perf_event子系统实现。

Perf不仅能够分析 PMU 事件(硬件事件),也能分析各种软件事件,如进程切换、pagefault、网络、文件IO等。

常用perf命令

间隔选项

-I 100 以100ms为间隔打印数据

性能事件的属性

硬件性能事件由处理器中的PMU提供支持。由于现代处理器的主频非常高,再加上深度流水线机制,从性能事件被触发,到处理器响应 PMI中断,流水线上可能已处理过数百条指令。

那么PMI中断采到的指令地址就不再是触发性能事件的那条指令的地址了,而且可能具有非常严重的偏差。

为了解决这个问题,Intel处理器通过PEBS机制实现了高精度事件采样。PEBS通过硬件在计数器溢出时将处理器现场直接保存到内存(而不是在响应中断时才保存寄存器现场),从而使得 perf能够采到真正触发性能事件的那条指令的地址,提高了采样精度。

在默认条件下,perf不使用PEBS机制。用户如果想要使用高精度采样,需要在指定性能事件时,在事件名后添加后缀”:p”或”:pp”。Perf在采样精度上定义了4个级别,如下表所示。

级别描述

  0 无精度保证
  1 采样指令与触发性能事件的指令之间的偏差为常数(:p)
  2 需要尽量保证采样指令与触发性能事件的指令之间的偏差为0(:pp)
  3 保证采样指令与触发性能事件的指令之间的偏差必须为0(:ppp)
  性能事件的精度级别目前的X86处理器,包括Intel处理器与AMD处理器均仅能实现前 3 个精度级别。

除了精度级别以外,性能事件还具有其它几个属性,均可以通过”event: X”的方式予以指定。

标志属性

  u 仅统计用户空间程序触发的性能事件
  k 仅统计内核触发的性能事件,有些根据寄存器计数器获得的数值,无法区分内核态还是用户态产生。
  uk 测量两者的合
  h 仅统计Hypervisor触发的性能事件
  G 在KVM虚拟机中,仅统计Guest系统触发的性能事件
  H 仅统计 Host 系统触发的性能事件
  p 精度级别

perf list

列出可采集事件(to be used in -e).

常用的集合事件 Metric Groups: perf list metricgroup

perf stat

可以列出程序运行的基本分析数据,或者特殊指明事件

$ perf stat ./bin/pivot "../run/uniformvector-2dim-5h.txt"
dim = 2, n = 500, k = 2
Using time : 236.736000 ms
max : 143 351 58880.823709
min : 83 226 21884.924801

 Performance counter stats for './bin/pivot ../run/uniformvector-2dim-5h.txt':

          7,445.60 msec task-clock                #   30.814 CPUs utilized
               188      context-switches          #    0.025 K/sec
                33      cpu-migrations            #    0.004 K/sec
               678      page-faults               #    0.091 K/sec
    14,181,698,360      cycles                    #    1.905 GHz                      (75.63%)
    46,455,227,542      instructions              #    3.28  insn per cycle           (74.37%)
     2,008,507,493      branches                  #  269.758 M/sec                    (74.18%)
        13,872,537      branch-misses             #    0.69% of all branches          (75.82%)

       0.241629599 seconds time elapsed

       7.448593000 seconds user
       0.000000000 seconds sys

Using Metric Groups: perf stat -M Summary,TLB --metric-only ./exe

--metric-only will just print calculated metric without raw data.

perf record

Run a command and record its profile into perf.data

must add perf record -g xxx to generate perf report topdown graph

不指定 -e 默认 -e cycles:u 。要统计全部的周期使用 -e cycles:uk

注意周期和指令统计的是多核的。选项-a可以指定所有核。

cycles和task-clock基本差不多

在测试时加上 uppp 的p提高精度,防止采样偏移到其他指令。

perf record -g -e branch-misses:uppp ./bin/pivot "../run/uniformvector-2dim-5h.txt"
perf record -g -e task-clock:uppp ./bin/pivot "../run/uniformvector-2dim-5h.txt"

可以看出分支失败在for循环这里

echo 0 > /proc/sys/kernel/kptr_restrict is needed.

perf report

Read perf.data (created by perf record) and display the profile

可交互的命令行。不仅可以显示出汇编和原代码的对应关系,还可以jump自动跳转

perf annotate

  1. program compiled in -g flag
  2. perf annotate can show each line execution cycles percentage

percentage at the end of each line

When you select many metric using -e option, but there is limited PMU hardware to use. So perf using Event multiplexing to measure parttime data to estimate the full data. the % is the parttime / fulltime

pmc-tools

pmc-tools

实践

Because of perf estimation, when numbers bigger than 10^9, the math relationship is more convincible.

Integer Operations per seconds (IntOps)

No integer-ralated state But float in perf list
  1. perf is hard to measure int
  2. Check raw hardware event codes (to be used with -rNNN) also difficult choice

Roofline: PMU & Perf

maybe we should further research Integer PMU events. Or just use VTune

1

TLB miss rate

reference inspire me in snode6

  • Total number of memory references ( X ) = mem_uops_retired.all_loads + mem_uops_retired.all_stores
  • Total number of memory references that missed in TLB ( Y ) = mem_uops_retired.stlb_miss_loads + mem_uops_retired.stlb_miss_stores
  • TLB miss rate = Y/X
perf stat \
    -e mem_uops_retired.all_loads \
    -e mem_uops_retired.all_stores \
    -e mem_uops_retired.stlb_miss_loads \
    -e mem_uops_retired.stlb_miss_stores xxx

according to the following experience, mem_uops_retired.stlb_miss_stores is equal to dtlb_store_misses.miss_causes_a_walk.

$ perf stat -e mem_uops_retired.all_loads -e mem_uops_retired.all_stores -e mem_uops_retired.stlb_miss_loads -e mem_uops_retired.stlb_miss_stores -e dtlb_load_misses.miss_causes_a_walk\
    -e dtlb_store_misses.miss_causes_a_walk \
    -e itlb_misses.miss_causes_a_walk  \
    -e dtlb_load_misses.walk_duration \
    -e dtlb_store_misses.walk_duration \
    -e itlb_misses.walk_duration \
    -e instructions:uk \
    -e cycles:uk ./bigJump.exe 1 10 500
Number read from command line: 1 10 (N,J should not big, [0,5] is best.)
result 0
 Performance counter stats for './bigJump.exe 1 10 500':

           3253636      mem_uops_retired.all_loads                                     (41.53%)
         529570049      mem_uops_retired.all_stores                                     (41.62%)
             59111      mem_uops_retired.stlb_miss_loads                                     (41.71%)
         471688964      mem_uops_retired.stlb_miss_stores                                     (33.50%)
            101474      dtlb_load_misses.miss_causes_a_walk                                     (33.56%)
         477591045      dtlb_store_misses.miss_causes_a_walk                                     (33.47%)
             61667      itlb_misses.miss_causes_a_walk                                     (33.37%)
           5591102      dtlb_load_misses.walk_duration                                     (33.28%)
       16489869334      dtlb_store_misses.walk_duration                                     (33.22%)
           2202174      itlb_misses.walk_duration                                     (33.22%)
        3712587926      instructions:uk           #    0.34  insn per cycle           (41.52%)
       10791067051      cycles:uk                                                     (41.52%)

perf测量 page walk 时间占比

脚本如下:

# Intel(R) Xeon(R) CPU E5-2695 v4 @ 2.10GHz
USER="/staff/qcjiang/codes/PIA_workspace"
APP="$USER/workloads/pagerank/cpp/pagerank $USER/workloads/pagerank/test/barabasi-100000-pr-p.txt"
perf stat \
    -e dtlb_load_misses.miss_causes_a_walk\
    -e dtlb_store_misses.miss_causes_a_walk \
    -e itlb_misses.miss_causes_a_walk  \
    -e dtlb_load_misses.walk_duration \
    -e dtlb_store_misses.walk_duration \
    -e itlb_misses.walk_duration \
    -e instructions:uk \
    -e cycles:uk \
    $APP

# dtlb_load_misses.walk_duration
# [Cycles when PMH(Page Miss Handling) is busy with page walks Spec update: BDM69]

# Intel(R) Xeon(R) Platinum 8358 CPU @ 2.60GH
perf stat\
    -e dtlb_load_misses.walk_completed\
    -e dtlb_load_misses.walk_active\
    -e dtlb_store_misses.walk_completed\
    -e dtlb_store_misses.walk_active\
    -e itlb_misses.walk_completed\
    -e itlb_misses.walk_active\


# different perf on hades0 AMD cpu
# AMD do not have the page walk time record
perf stat\
 -e ls_l1_d_tlb_miss.all\
 -e l1_dtlb_misses\
 -e l2_dtlb_misses\
 -e l2_itlb_misses\
 -e bp_l1_tlb_miss_l2_tlb_miss\
 -e bp_l1_tlb_miss_l2_tlb_miss.if2m\
 -e bp_l1_tlb_miss_l2_tlb_miss.if4k\
 -e bp_l1_tlb_miss_l2_tlb_miss.if1g\
 -e ls_tablewalker.dc_type0\
 -e ls_tablewalker.dc_type1\
 -e ls_tablewalker.dside\
 -e ls_tablewalker.ic_type0\
 -e ls_tablewalker.ic_type1\
 -e ls_tablewalker.iside -e instructions:uk -e cycles:uk /staff/shaojiemike/github/DAMOV/workloads/gemm/gemm 2000

统计TLB miss的性能事件可以帮助分析程序是否存在地址翻译方面的性能瓶颈。STLB miss通常表示存储指令无法利用局部性,访问了过多随机地址。优化程序的数据访问模式可以减少TLB miss。

-I 100 以100ms为间隔打印数据

$ ./investigation/pagewalk/tlbstat -c '/staff/shaojiemike/github/sniper_PIMProf/PIMProf/gapbs/sssp.inj -f /staff/shaojiemike/github/sniper_PIMProf/PIMProf/gapbs/benchmark/kron-20.wsg -n1'
command is /staff/shaojiemike/github/sniper_PIMProf/PIMProf/gapbs/sssp.inj -f /staff/shaojiemike/github/sniper_PIMProf/PIMProf/gapbs/benchmark/kron-20.wsg -n1
K_CYCLES   K_INSTR      IPC DTLB_WALKS ITLB_WALKS K_DTLBCYC  K_ITLBCYC  DTLB% ITLB%
186001     83244       0.45 753584     63         27841      1          14.97  0.00
229121     89734       0.39 1009100    213        31103      10         13.58  0.00
6233833    3629907     0.58 1227653    316159     45877      8347        0.74  0.13
16579329   8756681     0.53 10860225   524414     264655     15348       1.60  0.09
  • obs1: 前期读取数据部分DTLB大
  • obs2: cycle数前面是单核,后面是32核。而且读取时与计算时的core动态频率也不同。
  • 根据lscpu的结果。最高3.3GHz, min 1.2GHz. 2*32*33/12 = 176 ~ 165。符合预期。
  • obs3: 可以不考虑kernel。内核操作(malloc空间)的浮动很大。

为什么强调是Retired

在Intel处理器中,不是所有的存储指令(store uops)都会退休(retire)执行。具体来说,有以下几种情况:

  1. 存储指令被重排序或规避了,没有真正执行,所以不会退休。
  2. 存储指令执行期间遇到异常,被取消了,同样不会退休。
  3. 优化后的微操作 fusion 可能会 cancel 掉冗余的存储指令,这些指令也不会退休。
  4. 硬件优化会将多个存储指令合并(coalescing)为一个存储指令执行,其它冗余指令不会退休。

综上所述,不是所有的存储指令都会 retirement,其中有一部分指令因为各种原因被取消或合并了。

所以这个事件“Retired store uops” 特别强调统计的是执行完整并退休的存储指令。只统计退休的存储指令,可以更准确地反映程序实际进行了存储操作的次数。如果包含被取消的指令,会引入噪声,影响分析结果。

此外,TLB miss只发生在指令真正要执行时,那时已经可以确定指令确实会退休,不会被取消。

进阶应用:

改变数据规模好方式,统计该函数花费的cycle,斜率即CPE(cycles per element)

Perf stat例子: 通过CPE估计L1 Cache latency

改变链表的长度n,统计该函数花费的cycle,斜率即CPE(cycles per element)

Perf stat例子: 通过CPE估计分支预测失败惩罚

实际例子

$ perf record -g ./bin/pivot "../run/uniformvector-2dim-5h.txt"
WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,
check /proc/sys/kernel/kptr_restrict and /proc/sys/kernel/perf_event_paranoid.

Samples in kernel functions may not be resolved if a suitable vmlinux
file is not found in the buildid cache or in the vmlinux path.

Samples in kernel modules won't be resolved at all.

If some relocation was applied (e.g. kexec) symbols may be misresolved
even with a suitable vmlinux or kallsyms file.

Couldn't record kernel reference relocation symbol
Symbol resolution may be skewed if relocation was used (e.g. kexec).
Check /proc/kallsyms permission or run as root.
dim = 2, n = 500, k = 2
Using time : 240.525000 ms
max : 143 351 58880.823709
min : 83 226 21884.924801
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 2.794 MB perf.data (30470 samples) ]

在不改变 /proc/sys/kernel 的时候

$ sudo perf record ./bin/pivot "../run/uniformvector-2dim-5h.txt"
dim = 2, n = 500, k = 2
Using time : 389.349000 ms
max : 143 351 58880.823709
min : 83 226 21884.924801
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 1.909 MB perf.data (49424 samples) ]

超算使用perf分析

有时候由于架构不同超算不能使用vtune,

srun

srun -p IPCC -N 1 -n 1 -c 64 -t 1  perf record -g ../build/bin/pivot uniformvector-4dim-1h.txt

没有生成perf.data文件。终端末尾输出一堆乱码,不懂(后面知道了乱码是因为没有指定 -o 输出,默认竟然输出到命令行)

srun -p IPCC -N 1 -n 1 -c 64 -t 1  /usr/bin/bash

申请bash然后运行一样结果

salloc

这样可以

ipcc22_0029@ln121 ~/github/MarchZnver1/IPCC2022-preliminary/run (main*) [07:38:46]           
> salloc -p IPCC -N 1 -t 10:00                                            
salloc: Granted job allocation 2175282                                       
salloc: Waiting for resource configuration                                         
salloc: Nodes fb0101 are ready for job                                     
bash-4.2$ ls                                                             
check.py  manual.log  refer-2dim-5h.txt  refer-4dim-1h.txt  result.txt  run.2022-08-10.log  run_case1.sh  run-ipcc-mpi.sh  run-ipcc.sh  run-mpi.sh  run.sh  uniformvector-2dim-5h.txt  uniformvector-4dim-1h.txt   
bash-4.2$ perf record -g ../build/bin/pivot uniformvector-4dim-1h.txt

但实际在登录节点跑的,有ssh不上去

sbatch

#!/bin/bash
#SBATCH -p IPCC
#SBATCH -t 3:00
#SBATCH --nodes=1
#SBATCH --exclude=
#SBATCH --cpus-per-task=64
#SBATCH --mail-type=FAIL
#SBATCH [email protected]

source /public1/soft/modules/module.sh
module purge

module load gcc/8.1.0
module load mpich/3.1.4-gcc8.1.0

logname=vtune
export OMP_PROC_BIND=close; export OMP_PLACES=cores
perf record -g -e task-clock:uppp /public1/home/ipcc22_0029/shaojiemike/github/IPCC2022-preliminary/build/bin/pivot /public1/home/ipcc22_0029/shaojiemike/slurm/uniformvector-4dim-1h.txt |tee ./$logname

返回

ipcc22_0029@ln121 ~/slurm  [11:18:06]
> cat slurm-2180072.out 
Error:
task-clock:uppp: PMU Hardware doesn't support sampling/overflow-interrupts. Try 'perf stat'

修改指令为 perf record -g -o perfData -e task-clock:upp 支持 pp

分析CVT

感觉寄存器才用到10,还可以展开一次。

分析CVT变sub

为了形成add流水,编译器把sub都改成add了。

vmovaps 0xf98(%rip),%ymm9 # 81a0 <blockSize+0x20> 应该是从静态变量里读的。

分析再展开一次float

结果已经不对了,而且也没快(还是和资源流水有关吧,寄存器也吃紧了,sub都没在一起)

但是说明 unroll_SumDistance(j) 这种写法是能在汇编层面实现展开的

对比分析再展开一次double

由于寄存器更加紧张,load都不能再一起, ymm9 寄存器不够。

第一条红色的vadd感觉可以放下来。可能add和sub是公用同一个流水?

perf stat

单节点

 Performance counter stats for '/public1/home/ipcc22_0029/shaojiemike/github/IPCC2022-preliminary/build/bin/pivot /public1/home/ipcc22_0029/shaojiemike/slurm/case2/uniformvector-4dim-1h.txt':

         73,637.63 msec task-clock:u              #   54.327 CPUs utilized          
                 0      context-switches:u        #    0.000 K/sec                  
                 0      cpu-migrations:u          #    0.000 K/sec                  
             9,433      page-faults:u             #    0.128 K/sec                  
   192,614,020,152      cycles:u                  #    2.616 GHz                      (83.34%)
     4,530,181,367      stalled-cycles-frontend:u #    2.35% frontend cycles idle     (83.32%)
    25,154,915,770      stalled-cycles-backend:u  #   13.06% backend cycles idle      (83.33%)
   698,720,546,628      instructions:u            #    3.63  insn per cycle         
                                                  #    0.04  stalled cycles per insn  (83.34%)
    27,780,261,977      branches:u                #  377.256 M/sec                    (83.33%)
        11,900,773      branch-misses:u           #    0.04% of all branches          (83.33%)

       1.355446229 seconds time elapsed

      73.465281000 seconds user
       0.181156000 seconds sys

两个节点的perf数据感觉有问题, perf record 结果也很奇怪

Performance counter stats for 'mpirun -n 2 /public1/home/ipcc22_0029/shaojiemike/github/IPCC2022-preliminary/build/bin/pivot /public1/home/ipcc22_0029/shaojiemike/slurm/case2/uniformvector-4dim-1h.txt':

             51.37 msec task-clock:u              #    0.060 CPUs utilized          
                 0      context-switches:u        #    0.000 K/sec                  
                 0      cpu-migrations:u          #    0.000 K/sec                  
             2,278      page-faults:u             #    0.044 M/sec                  
        39,972,793      cycles:u                  #    0.778 GHz                      (84.56%)
         2,747,434      stalled-cycles-frontend:u #    6.87% frontend cycles idle     (85.16%)
        10,620,259      stalled-cycles-backend:u  #   26.57% backend cycles idle      (88.39%)
        58,479,982      instructions:u            #    1.46  insn per cycle         
                                                  #    0.18  stalled cycles per insn  (89.18%)
        14,068,620      branches:u                #  273.884 M/sec                    (77.31%)
           365,530      branch-misses:u           #    2.60% of all branches          (75.40%)

       0.850258803 seconds time elapsed

       0.015115000 seconds user
       0.038139000 seconds sys

具体分析例子

对于精心展开的手动向量化代码(理应用满了avx2的16个YMM*寄存器)

但是 -march=znver1 还是有1/3的加速。

可以发现变快的代码几点区别(可能和相同类型指令在一起执行,没什么关系) 1. load的形式变了,没有采用了insertf128。(0.71 下降到 0.06) 2. load的次数变少了(vmaxpd 地址元素实现,减少指令数) 3. 指令没有被拆分的那么散,更紧凑了。

下面是核心展开两次的代码: 快速的代码 1. load统一load快?而且在循环外围load,,无需load(vmax地址元素实现) 2. 从sub指令可以看出,是编译器是很想一起load,但是只有16个.(ymm0是总和,ymm9是掩码

综合来看指令数减少了许多(看截图,快的36条指令, 慢的54条指令)。性能瓶颈在指令退休?? 可以perf stat来验证 可以看出IPC基本没变。

这样就可以解释为什么数据重用加上 -march=znver1 变快了,但是原本的没变快。因为数据重用多了n*n的许多的数据读取,然后该选项合并消减了许多指令(直接隐含到vmaxpd里了),所以有加速。但是原本的实现基本没有数据读取,自然没得优化,所以基本没加速。

机器的IPC影响因素

可以看出优化了有些许变化,主要原因是不同指令的Throughput不同(每个周期能执行的指令数)。

核数竟然就是 userTime/elapsedTime 这也太粗暴了吧。

运行不同的例子,执行的程序内代码的权重和类型不同,IPC有所不同。(小例子有400/600的时间都在MPI_Init, 自然IPC会低一些)

常见问题

限制选项

perf_event_paranoid setting is 4:
  -1: Allow use of (almost) all events by all users
      Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK
>= 0: Disallow raw and ftrace function tracepoint access
>= 1: Disallow CPU event access
>= 2: Disallow kernel profiling

编辑 /etc/sysctl.conf 文件,加入 kernel.perf_event_paranoid = -1 保存文件并退出。然后,你可以通过重新加载 sysctl 配置来使设置生效: sudo sysctl -p

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

实验室同学袁福焱组会汇报

https://zhuanlan.zhihu.com/p/141694060

Git Submodule: Data & Code Repository Separate

Just follow my mind

  1. parent repository ignore the sub directory in .gitignore
  2. Normal git usage in child repository, eg. init commit push
  3. submodule for what?
    1. for auto upload and init multi-subgit to target git commit

submodule

submodule is hard to use when it will be blocked under big sub-srepository.

I recommend to use .gitignore or just use ln -s xxx yyy

chatgpt balabala

Adding a Git repository inside another Git repository, often referred to as a "submodule," is a way to manage a separate repository as a part of your main project. This is particularly useful when you want to include another project within your project and keep them separate.

Here's how you can add a submodule and set up .gitignore for the outer repository:

  1. Add a Submodule:

    Navigate to the root of your outer Git repository and run the following command:

    git submodule add <repository_url> <submodule_path>
    

    Replace <repository_url> with the URL of the repository you want to add as a submodule, and <submodule_path> with the relative path within your outer repository where you want to place the submodule.

  2. Update and Initialize Submodule:

    After adding the submodule, you need to update and initialize it:

    git submodule update --init --recursive
    

    This command fetches the submodule's content and initializes it.

  3. Set Up .gitignore:

    To ignore the submodule's content and avoid committing it to the outer repository, you need to add the submodule path to the .gitignore file of the outer repository. For example, if your submodule is located in a directory called "submodule_folder," add the following line to the .gitignore file:

    submodule_folder/
    
  4. Working with Submodules:

    When you clone the outer repository on another system or share it with others, they will need to run git submodule update --init --recursive to fetch and initialize the submodule's content.

    To make changes to the submodule, navigate into the submodule directory and work as you would in a regular Git repository. When you commit changes in the submodule, you will need to push them to its remote repository. After that, commit the updated submodule reference in the outer repository.

    To update the submodule to the latest version available, you can navigate into the submodule directory and run git pull origin master (or the appropriate branch) to update its content. Then, commit the updated reference in the outer repository.

Remember that submodules have their own independent version control, so you need to manage them separately. Submodules provide a way to incorporate external projects into your main project while keeping their development and versioning separate.

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。

Make

简介

make 和 makefile 是 Linux 系统下 C/C++ 工程的编译工具,它们用来自动化配置实验环境,编译项目文件。makefile 文件描述了项目文件之间的依赖关系和编译规则,make 命令根据 makefile 文件的内容执行编译任务123。相对于 cmake 等其他的编译工具,make 和 makefile 有以下几个特点:

  • make 和 makefile 是 GNU 的标准工具,可以在多种平台上使用。
  • make 和 makefile 可以根据文件时间戳自动发现更新过的文件而减少编译的工作量。
  • make 和 makefile 可以灵活地定义变量、函数和条件判断等,实现复杂的编译逻辑

对于复杂的项目,能通过makefile文件将

  • 实验环境Build,(提前设置相关路径参数)
  • wget/git 下载相关文件到对应目录
  • apt install
  • 样例测试test,
  • 整体运行/Benchmark运行run,
  • 环境清理clean 统一起来

Makefile 执行流程

  • make的默认目标是makefile中的第一个目标,而其它目标一般是由这个目标连带出来的。

常用命令

# 不实际运行,但是打印会运行的命令
make -f makefile.llvm -n
# -W 假设某个文件是最新的,不需要重新编译。常见结合 -n 使用
make inj -f makefile.llvm -n -W svm.inj

打印debug信息,makefile如何选择决策

  • --debug=v 输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
  • --debug=i implicit,输出使用的隐含规则过程。
  • --debug=m 输出make读取makefile,更新makefile,执行makefile的信息。

Makefile 隐含规则

  • “隐含规则”也就是一种惯例,make会按照这种“惯例”心照不喧地来运行,那怕我们的Makefile中没有书写这样的规则。
  • 例如,把[.c]文件编译成[.o]文件这一规则,你根本就不用写出来,make会自动推导出这种规则,并生成我们需要的[.o]文件。
  • 将头文件变成.gch precompiled headers
  • 许多的隐含规则都是使用了“后缀规则”来定义的
  • 变量.SUFFIXES存储了默认的依赖目标,可以修改。默认的后缀列表是:.out,.a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el
  • 要让make知道一些特定的后缀,我们可以使用伪目标".SUFFIXES"来定义或是删除,如:

  • 把后缀.hack和.win加入后缀列表中的末尾。

SUFFIXES: .hack .win
  1. 先删除默认后缀,后定义自己的后缀列表。
.SUFFIXES: # 删除默认的后缀
.SUFFIXES: .c .o .h # 定义自己的后缀

模式规则

  • 常见使用模式规则来定义一个隐含规则
  • 至少在规则的目标定义中要包含"%"
%.o : %.c ; <command ......>

老式风格的"后缀规则"

  • 后缀规则是一个比较老式的定义隐含规则的方法。
  • 后缀规则会被模式规则逐步地取代。因为模式规则更强更清晰。为了和老版本的Makefile兼容,GNU make同样兼容于这些东西。
  • 后缀规则有两种方式:"双后缀"和"单后缀"。
  • 双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如".c.o"相当于"%o : %c"。
  • 单后缀规则只定义一个后缀,也就是源文件的后缀。如".c"相当于"% : %.c"。
.cpp.o:
  $(CPPCOMPILE) -c $(COMPILEOPTION) $(INCLUDEDIR) $<

.c.o:
 $(CCOMPILE) -c $(COMPILEOPTION) $(INCLUDEDIR) $<

Makefile编写技巧

Makefile框架

SIM_ROOT ?= $(shell readlink -f "$(CURDIR)") #当前工作目录的绝对路径。
CLEAN=$(findstring clean,$(MAKECMDGOALS)) #???

include common/Makefile.common

$(STANDALONE): $(LIB_CARBON) $(LIB_SIFT) $(LIB_DECODER)
 @$(MAKE) $(MAKE_QUIET) -C $(SIM_ROOT)/standalone #进入standalone目录,执行Makefile文件

Makefile.common文件中的内容如下:

include $(SIM_ROOT)/Makefile.config

DIRECTORIES := ${shell find $(SIM_ROOT)/common -type d -print} #查找common目录下的所有子目录
LIBCARBON_SOURCES = $(foreach dir,$(DIRECTORIES),$(wildcard $(dir)/*.cc)) \
 $(wildcard $(SIM_ROOT)/common/config/*.cpp) # 把变量DIRECTORIES中的每个目录下的所有.cc文件和$(SIM_ROOT)/common/config目录下的所有.cpp文件赋值给变量LIBCARBON_SOURCES

# FLAGS
ifeq ($(SNIPER_TARGET_ARCH),ia32)
  # Add -march=i686 to enable some extra instructions that allow for implementation of 64-bit atomic adds
  CXXFLAGS += -m32 -march=i686 -DTARGET_IA32 # Include paths
  LD_FLAGS += -m32
endif
ifeq ($(SNIPER_TARGET_ARCH),intel64)
  CXXFLAGS += -fPIC -DTARGET_INTEL64
  LD_FLAGS +=
endif

# Build rules for dependency generation
%.d: %.cpp
 $(_MSG) '[DEP   ]' $(subst $(shell readlink -f $(SIM_ROOT))/,,$(shell readlink -f $@)) #把$@的绝对路径中的$(SIM_ROOT)的绝对路径部分替换为空字符串,便于打印
 $(_CMD) $(CXX) -MM -MG $(CPPFLAGS) $(CXXFLAGS) $< | sed -n "H;$$ {g;s@.*:\(.*\)@$*.o $@: \$$\(wildcard\1\)@;p}" >$@
 # 用$(CXX)编译器对$<文件进行依赖分析,并把结果通过sed命令进行处理,然后输出到$@文件

Makefile.config 如下

# 架构判断
ARCH_QUERY=$(shell uname -m)
ifeq ($(ARCH_QUERY),i686)
SNIPER_TARGET_ARCH = ia32
else
ifeq ($(ARCH_QUERY),x86_64)
SNIPER_TARGET_ARCH ?= intel64
#SNIPER_TARGET_ARCH = ia32
else
$(error Unknown target arch: $(ARCH_QUERY))
endif
endif

# 全局的路径
PIN_HOME ?= xxx
PIN_ROOT := $(PIN_HOME)

# 编译器
CC ?= gcc
CXX ?= g++

# DEBUG输出
ifneq ($(DEBUG_SHOW_COMPILE),)  # 如果变量DEBUG_SHOW_COMPILE不为空,如make DEBUG_SHOW_COMPILE=1 使用
  SHOW_COMPILE=1
  MAKE_QUIET=     # 使用情形:$(MAKE) $(MAKE_QUIET) -C pin clean
  _MSG=@echo >/dev/null   # 使用情形:$(_MSG) '[CLEAN ] pin'
  _CMD=       # 使用情形:$(_CMD) git clone --quiet xxx
else
  SHOW_COMPILE=
  MAKE_QUIET=--quiet
  _MSG=@echo
  _CMD=@
endif

打印make的详细信息make DEBUG_SHOW_COMPILE=1 DEBUG=1 -j 16|tee make.log |my_hl

打印make时每项依赖文件的情况

# Sums up number of passes and fails
# cut 命令用于从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段写至标准输出。其中 -d (--delimiter)参数指定分隔符(默认TAB),-f (--fields)参数指定要显示的字段。因此,cut -d\  -f 2 的意思是以空格为分隔符,显示第二个字段。
# uniq -c 统计不重复词(PASS\|FAIL)的出现
test-score: test-all
 @$(MAKE) test-all | cut -d\  -f 2 | grep 'PASS\|FAIL' | sort | uniq -c

# Result output strings
PASS = \033[92mPASS\033[0m
FAIL = \033[91mFAIL\033[0m

# Need to be able to build kernels, if this fails rest not run
test-build: all
 @echo " $(PASS) Build"

实现在Makefile里下载

根据是否存在文件来执行makefile语句

ROAD_URL = http://www.dis.uniroma1.it/challenge9/data/USA-road-d/USA-road-d.USA.gr.gz
$(RAW_GRAPH_DIR)/USA-road-d.USA.gr.gz:
 wget -P $(RAW_GRAPH_DIR) $(ROAD_URL)

$(RAW_GRAPH_DIR)/USA-road-d.USA.gr: $(RAW_GRAPH_DIR)/USA-road-d.USA.gr.gz
 cd $(RAW_GRAPH_DIR)
 gunzip < $< > $@

$(GRAPH_DIR)/road.sg: $(RAW_GRAPH_DIR)/USA-road-d.USA.gr converter
 ./converter -f $< -b $@

目录下所有源文件编译成单独的可执行文件

#before run, mkdir build && cd build

CFLAGS = -g -O0

#source files dir
SRC_DIR = ../
#Makefile dir, generally build
BUILD_DIR = ./

#get ../example.c 不能寻找子文件夹的文件,需要shell find
SRCS = $(wildcard $(SRC_DIR)*.c)

#delete .c, then become ../example
# :.c = .o的意思是C文件對應相應的.o文件
FILENAME = $(SRCS:.c=)

#replace src dir with dest dir, then become ./example
PROGS = $(addprefix $(BUILD_DIR),$(notdir $(FILENAME)))

.PHONY: all
all: $(PROGS)
    @echo "-- build all .c files" # makefile中命令前加一个@的作用是不让make显示出要执行的命令
 @echo Building for x86 architecture # echo 无需“”

$(BUILD_DIR)%: $(SRC_DIR)%.c
    gcc $(CFLAGS) -o $@ $<

另一种写法

CC=mpicc
CXX=mpicxx
OPENMP=-fopenmp
SOURCES:=$(shell find $(.) -name '*.c')
SOURCESCXX:=$(shell find $(.) -name '*.cpp')
# SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s)
LIB=-lm
OBJS=$(SOURCES:%.c=%)
OBJS+=$(SOURCESCXX:%.cpp=%)
DEBUG=-g

SRC_DIR_1 = ./Lab1
SRCS_1 = $(shell find $(.) -name '*.c')
FILENAME_1 = $(SRCS_1:.c=)

# lab1 : $(FILENAME_1)
#  @echo $(SRCS_1)
#  @echo "lab1编译完成"
#  if [ ! -d "build" ]; then mkdir build; fi
#  mv $(FILENAME_1) build

all : $(OBJS)
 @echo $(SOURCES) $(SOURCESCXX)
 @echo "编译完成"
 @echo $(OBJS)
 if [ ! -d "build" ]; then mkdir build; fi
 mv $(OBJS) build

%: %.c
 $(CC) $(DEBUG) $(OPENMP) $< $(LIB) -o $@

%: %.cpp
 $(CXX) $(DEBUG) $(OPENMP) $< $(LIB) -o $@

.PHONY: clean showVariable

showVariable:
 @echo $(SOURCES)

clean: 
 rm -rf build

多个文件变一个可执行文件

MPICC = mpicc

LIB = -lm 
C_FLAGS= -O3 -mavx2 -fopenmp $(LIB)

SRC_DIR = src
BUILD_DIR = build/bin
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJ = $(SRCS:.c=.o)

pivot: ${SRCS}
 echo "compiling $(SRC_DIR) ${FILENAME}"
 $(MPICC) $^ $(C_FLAGS) -o $(BUILD_DIR)/pivot

运行结果

$ make pivot -C ..
make: 进入目录“/home/shaojiemike/github/IPCC2022-preliminary”
echo "compiling src src/Combination src/heapSort src/SunDistance src/MPI src/pivot"
compiling src src/Combination src/heapSort src/SunDistance src/MPI src/pivot
mpicc src/Combination.c src/heapSort.c src/SunDistance.c src/MPI.c src/pivot.c -O3 -mavx2 -fopenmp -lm  -o build/bin/pivot

更复杂

保留.o文件,区分Flag与lib

CC = g++
MPICC = mpicc

C_FLAGS= -O3 -fopenmp 
LIB = -lgomp

INCLUDEPATH = feGRASS
SRC_DIR = feGRASS
BUILD_DIR = build/bin

SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJ = $(SRCS:.cpp=.o)
DEBUG_OBJ = $(SRCS:.cpp=_debug.o)
TIME_OBJ = $(SRCS:.cpp=_time.o)
FILENAME = $(SRCS:.cpp=)

.DEFAULT_GOAL := all
all : ${OBJ}
 echo "compiling $(SRC_DIR) ${FILENAME}"
 $(CC) $^ $(LIB) -o $(BUILD_DIR)/main

debugPrint: ${DEBUG_OBJ}
 echo "compiling $(SRC_DIR) ${FILENAME}"
 $(CC) $^ $(LIB) -o $(BUILD_DIR)/main

timePrint: ${TIME_OBJ}
 echo "compiling $(SRC_DIR) ${FILENAME}"
 $(CC) $^ $(LIB) -o $(BUILD_DIR)/main

%.o: %.cpp
 $(CC) $(C_FLAGS) -c $< -o $@ 
%_debug.o: %.cpp
 $(CC) -DDEBUG -DTIME $(C_FLAGS) -c $< -o $@ 
%_time.o: %.cpp
 $(CC) -DTIME $(C_FLAGS) -c $< -o $@ 


checkdirs: $(BUILD_DIR)
$(BUILD_DIR):
 @mkdir -p $@

.PHONY: clean
clean:
 rm -rf $(BUILD_DIR)/main ${SRC_DIR}/*.o

将obj与src分离

CC = g++
MPICC = mpicc

C_FLAGS= -O3 -fopenmp 
LIB = -lgomp
# C_FLAGS= -fopenmp $(LIB) $(debugFlag)
# C_FLAGS= -O3 -march=znver1 -mavx2 -fopenmp $(LIB) $(debugFlag)

INCLUDEPATH = feGRASS
SRC_DIR = feGRASS
BUILD_DIR = build/bin
OBJ_DIR = build/obj

SRCS = $(wildcard $(SRC_DIR)/*.cpp)
TMP = $(patsubst %.cpp,${OBJ_DIR}/%.cpp,$(notdir ${SRCS}))
# example: $(patsubst %.cpp,%.d,$(patsubst %.c,%.d,$(patsubst %.cc,%.d,$(LIBCARBON_SOURCES)))) 
# 意思是把$(LIBCARBON_SOURCES)变量中的所有.cpp,.c,.cc文件名替换为.d文件名
HEADERS = $(wildcard $(SRC_DIR)/*.h)
OBJ = $(TMP:.cpp=.o)
DEBUG_OBJ = $(TMP:.cpp=_debug.o)
TIME_OBJ = $(TMP:.cpp=_time.o)
FILENAME = $(TMP:.cpp=)

$(info    SRCS is: $(SRCS))
$(info    OBJ is: $(OBJ))
$(info    HEADERS is: $(HEADERS))

.DEFAULT_GOAL := main
main : $(OBJ)
 $(CC) $(OBJ) $(LIB) -o $(BUILD_DIR)/main

debugPrint: $(DEBUG_OBJ)
 $(CC) $(DEBUG_OBJ) $(LIB) -o $(BUILD_DIR)/main

timePrint: $(TIME_OBJ)
 $(CC) $(TIME_OBJ) $(LIB) -o $(BUILD_DIR)/main

mpi: $(SRCS)
 $(MPICC) $^ $(C_FLAGS) -o $(BUILD_DIR)/main

debugMpi: $(SRCS)
 $(MPICC) -DDEBUG -DTIME $^ $(C_FLAGS) -o $(BUILD_DIR)/main

timeMpi: $(SRCS)
 $(MPICC) -DTIME $^ $(C_FLAGS) -o $(BUILD_DIR)/main

${OBJ_DIR}/%.o: ${SRC_DIR}/%.cpp $(HEADERS)
 $(CC) $(C_FLAGS) -c $< -o $@ 
${OBJ_DIR}/%_debug.o: ${SRC_DIR}/%.cpp $(HEADERS)
 $(CC) -DDEBUG -DTIME $(C_FLAGS) -c $< -o $@ 
${OBJ_DIR}/%_time.o: ${SRC_DIR}/%.cpp $(HEADERS)
 $(CC) -DTIME $(C_FLAGS) -c $< -o $@ 


checkdirs: $(BUILD_DIR)
$(BUILD_DIR):
 @mkdir -p $(BUILD_DIR)
 @mkdir -p $(OBJ_DIR)


.PHONY: clean
clean:
 rm -rf $(BUILD_DIR)/main $(OBJ_DIR)/*.o

说明

  1. $(foreach var,list,text)的语法来对list中的每个元素执行text,并用var来引用当前元素1。你也可以用
  2. $(wildcard pattern)的语法来匹配指定模式的文件,并返回其列表
  3. $(subst from,to,text)的语法来把text中的from替换为to
  4. $(patsubst pattern,replacement,text)的语法来把text中匹配pattern的部分替换为replacement
  5. include <filenames> ,make 在处理程序的时候,文件列表中的任意一个文件不存在的时候或者是没有规则去创建这个文件的时候,make 程序将会提示错误并保存退出;
  6. -include <filenames>,当包含的文件不存在或者是没有规则去创建它的时候,make 将会继续执行程序,只有真正由于不能完成终极目标重建的时候我们的程序才会提示错误保存退出;
  7. addprefix的功能增加前缀,例如$(addprefix -I,./Inc)执行后为 -I ./Inc
  8. OUTPUT_FILES = $(addsuffix .out, $(addprefix $(OUTPUT_DIR)/, $(BENCH_MAINNAME)))
  9. 文件名处理函数 dir, notdir, suffix, basename (网站basename例子写错了)
  10. https://www.zhaixue.cc/makefile/makefile-filename-function.html
  11. .PHONY: clean是为了有clean名称的文件时,防止把make clean理解成生成clean文件,而不是使用clean规则。
  12. 一个Makefile文件里通常会有多个目标,一般会选择第一个作为默认目标。所以一般第一个写all
  13. 赋值符号 = 是最基本的赋值 := 是覆盖之前的值 ?= 是如果没有被赋值过就赋予等号后面的值 += 是添加等号后面的值
  14. makefile中命令前加一个@的作用是不让make显示出要执行的命令
  15. $@ is the name of the target being generated, and$< the first prerequisite (usually a source file). You can find a list of all these special variables in the GNU Make manual.

For example, consider the following declaration:

all: library.cpp main.cpp

In this case:

$@ evaluates to all

$< evaluates to library.cpp

$^ evaluates to library.cpp main.cpp

常见问题

Makefile:22: *** missing separator.  Stop.

命令开头要用Tab,不是空格。别用vscode,用vim写

复杂项目的例子

Victima模拟器(sniper拓展地址翻译的模拟器)

分析复杂的Makefile

# Victima/sniper/Makefile
STANDALONE=$(SIM_ROOT)/lib/sniper
$(STANDALONE): $(LIB_CARBON) $(LIB_SIFT) $(LIB_DECODER)
    @$(MAKE) $(MAKE_QUIET) -C $(SIM_ROOT)/standalone

# vscode-remote://ssh-remote%2Bicarus3/staff/shaojiemike/test/Victima/src/Victima/sniper/standalone/Makefile
LD_LIBS += -lcarbon_sim -lpthread -ldw
SOURCES = $(shell ls $(SIM_ROOT)/standalone/*.cc)
OBJECTS = $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCES)))
## build rules
TARGET = $(SIM_ROOT)/lib/sniper
$(TARGET): $(SIM_ROOT)/lib/libcarbon_sim.a $(SIM_ROOT)/sift/libsift.a $(SIM_ROOT)/decoder_lib/libdecoder.a
$(TARGET): $(OBJECTS)
    $(_MSG) '[LD    ]' $(subst $(shell readlink -f $(SIM_ROOT))/,,$(shell readlink -f $@))
    $(_CMD) $(CXX) $(LD_FLAGS) -o $@ $(OBJECTS) $(LD_LIBS) $(OPT_CFLAGS) -std=c++0x

解释

第一个规则:$(TARGET): $(SIM_ROOT)/lib/libcarbon_sim.a $(SIM_ROOT)/sift/libsift.a $(SIM_ROOT)/decoder_lib/libdecoder.a 这行定义了 $(TARGET)(即 $(SIM_ROOT)/lib/sniper)依赖于三个库文件。这意味着在构建 $(TARGET) 之前,必须首先存在或构建这些库文件。

第二个规则:$(TARGET): $(OBJECTS) 这行则说明 $(TARGET) 还依赖于由 $(OBJECTS) 定义的一系列对象文件。这是实际编译生成可执行文件所需要的对象文件。

参考文献

https://blog.csdn.net/ET_Endeavoring/article/details/98989066

LinuxCommand: system info

硬盘/挂载空间

df -h .

各个文件夹空间

deep为1

du -h -d .

进程查看与kill

# thi
ps aux | grep 1499321
ps -auxf | grep -nB 10 -E 'python[3]?|PID'
kill -9 

ps aux linux command whill show no zero cpu usage when the process is sleeping beacuse of its snapshots mechanism

apt-get problems

dpkg: 处理归档 /var/cache/apt/archives/bat_0.12.1-1build1_arm64.deb (--unpack)时出错:
 正试图覆盖 /usr/.crates2.json,它同时被包含于软件包 ripgrep 11.0.2-1build1
dpkg-deb: 错误: 粘贴 子进程被信号(断开的管道) 终止了
在处理时有错误发生:
 /var/cache/apt/archives/bat_0.12.1-1build1_arm64.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)
sudo apt-get purge -h
# jsonfile conflict purge - Remove packages and config files

tree

deep size

tree -L DepthSIze Folder_Path

so文件分析

#分析symbols 
nm -gDC intel64/gcc4.8/*
  • -g:显示全局符号表。
  • -D:显示动态符号表。
  • -C:将 C++ 符号名还原成源代码中的名称。

综合来看,使用 nm -gDC <filename> 命令可以查看一个二进制可执行文件或者共享库中的全局符号表和动态符号表,并将包含其中的 C++ 符号名还原成源代码中的名称。

shaojiemike@snode6 ~/github/gem5  [10:49:56]
> nm /usr/local/lib/libprotobuf.a |c++filt|grep google::protobuf::MessageFactory::InternalRegisterGeneratedFile
                 U google::protobuf::MessageFactory::InternalRegisterGeneratedFile(char const*, void (*)(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&))
0000000000000e00 T google::protobuf::MessageFactory::InternalRegisterGeneratedFile(char const*, void (*)(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&))

#分析子so
ldd .so

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

Cpu Time

Situation

It is all started with two confusing situation.

  • I found out using ps aux | grep -v process_name, the process is in Sl+ state. But the cpu usage is not zero.
  • watch "ps aux |grep 3496617" always show the same cpu usage percentage, which is very confusing beacause htop always show up-down value. and pidstat -p 3516617 show cpu% less than 100%.

ParallelIntroduction

战略层

并行算法设计(详细见陈国良教材)

PCAM

设计并发程序的四个阶段(PCAM设计方法学):

  1. 划分(Partitioning):分解成小的任务,开拓并发性
  2. 通讯(Communication):确定诸任务间的数据交换,监测划分的合理性;
  3. 组合(Agglomeration):依据任务的局部性,组合成更大的任务;
  4. 映射(Mapping):将每个任务分配到处理器上,提高算法的性能。

三种基本方法

串改并:现有串行算法改成并行
  1. 从问题开始全新设计并行算法
  2. 前缀和改成线性方程组的问题来并行
  3. 有向环的k-着色并行算法
    1. 将coreId作为颜色,进行二进制处理来颜色压缩。压缩到0-5之后再单独消除颜色
借用法:借用已有算法
  1. 最短路径动态规划转换成矩阵乘法
其他基础方法
  1. 平衡树
    1. 求n个最大值,先串行求部分最大,再用树,成本(处理器个数*时间)最低
    2. 访问存储次数/成本也不是最低的
  2. 倍增技术,指针跳跃
  3. 分治策略
  4. 划分原理(以两个有序数列到归并排序为例)
    1. 均匀划分
    2. 对数划分
    3. 方根划分
    4. 功能划分:基于硬件的Batcher实现,奇偶归并排序,双调序列的实现可以简化网络
  5. 流水线技术
    1. 脉动阵列
  6. 加速级联策略
    1. 先采用最快的方法将问题规模先减小到一个阈值,然后用其余最优的算法求出原问题的解。
    2. 思想其实类似机器学习里的变学习率。例子有平衡树的
  7. 破对称技术
    1. 打破数据的对称,便于分类

指令级并行 ILP

编译器和硬件级别的,一般不会引起程序员的注意。1

挑战: 数据相关

  • 真数据相关
  • 名称相关:两条指令使用了相同的寄存器或者存储器位置,但实际并没有数据流动。寄存器重命名处理
  • 控制相关:主要指指令的执行与分支指令存在先后关系。

解决方案(CPI to 1):“硬件推测”(Hardware Speculation)

硬件推测是一种技术,通过它,处理器可以在不完全确定某些操作结果的情况下,提前执行后续指令。这种技术主要用于提高处理器的性能和执行效率。以下是硬件推测的几个关键方面:

  1. 分支预测(Branch Prediction):处理器使用分支预测来猜测条件跳转指令的结果(即跳转或不跳转)。如果预测正确,提前执行的指令就可以直接使用,从而避免等待分支决策的延迟。
  2. 数据依赖性推测(Data Dependency Speculation):处理器可能会提前执行依赖于尚未计算完成的数据的指令。例如,即使前一条指令的结果尚未确定,它也会继续执行依赖于该结果的后续指令。
  3. 乱序执行(Out-of-Order Execution):这是硬件推测的另一种形式。在这里,处理器根据资源的可用性而不是指令在程序中的顺序来调度指令的执行。
  4. 内存访问推测(Memory Access Speculation):处理器可能会在所有必要的内存访问权限检查完成之前开始执行依赖于特定内存操作的指令。

这些推测性技术的共同目标是减少因等待数据依赖、分支决策或其他延迟而导致的空闲处理器周期。如果推测正确,这可以显著提高执行速度。然而,如果推测错误,处理器必须“倒回”并重新执行正确的指令路径,这可能导致性能损失。因此,现代处理器设计的一个关键方面是优化这些推测机制以最大限度地减少错误预测的影响。

编译器技术:基本流水线与循环展开
分支预测器
动态调度/乱序执行(out-of-order execution)
  • 有多个功能单元和流水化单元。使得流水线能同时执行多个指令
  • 乱序执行, 循序提交: 记分板 -> Tomasulo算法

  • 重排缓存区ROB:保存已经执行完成,但是还没有提交的指令结果。是乱序执行处理器的核心组件之一。

  • 三点作用:
    1. 指令顺序的维护:尽管指令被乱序执行(以利用处理器资源并提高性能),但最终的结果需要按照程序的原始顺序提交。ROB跟踪每条指令的执行状态,并确保它们在最终提交时是按顺序的。
    2. 分支与异常的支持处理: 区分已提交指令,保证分支预测失败时正常回退,和异常和中断时程序的一致性。
    3. 资源管理:ROB还参与动态地管理处理器资源,如识别哪些指令可以并行执行,以及在资源有限时优先执行哪些指令。

升级方案(CPI lower 1):多发射架构(Multiple Issue)

与前面的区分

前面的技术是为了消除数据与控制停顿,使得CPI达到理想值1,如果我们想CPI小于一,每个时钟周期就需要发射多条指令。

多发射是一个更广泛的术语,指的是在一个时钟周期内发射(开始执行)多条指令的能力。

主要有三类:

  1. In order superscalar processor
  2. out-of-order superscalar processor
  3. VLIW
超标量架构(Superscalar)

超标量处理器通常具有多个执行单元,如多个整数、浮点和其他专用执行单元,以及复杂的调度和分支预测机制来支持同时处理多条指令。

VLIW

VLIW(Very Long Instruction Word)是一种处理器架构设计,其特点是使用非常长的指令字来编码多个操作,这些操作可以在单个处理器周期内并行执行。VLIW架构的关键特征如下:

  1. 长指令字:VLIW架构的指令字长度远超常规处理器。这些长指令字包含了多个操作(如算术、逻辑操作),这些操作在一个时钟周期内同时执行。
  2. 编译器优化:在VLIW架构中,指令的并行性是在编译时确定的,而不是在运行时。这意味着编译器负责识别可以并行执行的操作,并将它们组合成单个长指令字。
  3. 硬件简化:由于指令级并行性是在编译时处理的,VLIW处理器的硬件可以相对简化,因为它们不需要复杂的运行时指令调度和分支预测机制。这使得VLIW处理器在设计上更简单,功耗更低。
  4. 应用依赖:VLIW架构的效率高度依赖于编译器的优化能力和应用程序代码的特性。在指令流中并行性高的应用中,VLIW架构可以实现很高的性能。

VLIW架构在某些特定的应用场景(如数字信号处理DSP)中效果显著,但在通用计算领域的适用性受到限制,主要是因为编译器在处理普通程序时面临更大的挑战来有效地利用指令级并行性。

实例(超标量处理器能在一个时钟周期内同时发射和执行多条指令来实现指令级并行性。)

  • ARM Cortex-A8 Core 双发射、静态调度(In-order)超标量处理器
  • Intel Core i7 四发射、动态调度(out-of-order execution)超标量处理器
    • 微指令融合。性能更强,但是能耗比显著降低。

数据级并行 DLP

  • 向量张量、SIMD、以及GPU的结构
  • 区别和MLP 概念的不同:MLP(访存并行)是一种通过同时处理多个内存访问来实现并行性的概念。MLP的目标是提高对存储器系统的效率,减少内存访问的延迟时间。它可以通过预取、缓存和内存操作的重叠等技术来实现。

线程级并行 TLP

  • 单机多核系统
  • 运行一组紧密耦合的线程,协同完成任务
  • 缓存一致性协议

超算或者仓库级计算机(WSC Warehouse-Scale Computers) 请求级并行 RLP

  • 请求级并行:由一个或者多个用户发起的多个相对独立的进程
  • 环境: 云计算。
  • 关注成本与收益

并行分类

微处理器器中的并行

ILP 指令级并行

TLP 线程级并行

SMT 同步多线程(Simultaneous Multi-Threading,SMT)是一种在一个CPU 的时钟周期内能够执行来自多个线程的指令的硬件多线程技术。

CMP 单芯片多处理器(Chip multiprocessors)

常用的四种并行模式(这样分感觉不是很对)

  1. 共享内存模式(The shared memory model)
  2. 多线程模式(The multithread model)
  3. 分布式内存/消息传递模式(The distributed memory/message passing model)
  4. 数据并行模式(The data parallel model)

实际的经验

  1. IPCC Preliminary SLIC Optimization 4: EnforceLabelConnectivity

并行常见名词

SM : shared Memory

LM : Local Memory

DM :distribute memory

并行计算模型

并行计算访存模型(强调时间)

均匀访存模型(UMA)、非均匀访存模型(NUMA)、全高速缓存访存模型(COMA)、一致性高速缓存非均匀存储访问模型(CC-NUMA)和非远程存储访问模型(NORMA)。

UMA(Uniform Memory Access)均匀存储訪问:物理存储器被全部处理器均匀共享,全部处理器对全部SM訪存时间相同,每台处理器可带有快速私有缓存,外围设备共享。

NUMA非均匀存储訪问:共享的SM是由物理分布式的LM逻辑构成,处理器訪存时间不一样,訪问LM或CSM(群内共享存储器)内存储器比訪问GSM(群间共享存储器)快

COMA(Cache-Only MA)全快速缓存存储訪问:NUMA的特例、全快速缓存实现

CC-NUMA(Coherent-Cache NUMA)快速缓存一致性NUMA:NUMA+快速缓存一致性协议。实际是分布共享的DSM机器

NORMA(No-Remote MA)非远程存储訪问:无SM,全部LM私有。通过消息传递通信

NUMA

NUMA : NUMA (non-uniform memory access) is a method of configuring a cluster of microprocessor in a multiprocessing system so that they can share memory locally, improving performance and the ability of the system to be expanded. NUMA is used in a symmetric multiprocessing ( SMP ) system.

在NUMA下,處理器存取它自己的本地記憶體的速度比非本地記憶體快一些。 非統一記憶體存取架構的特點是:被共享的記憶體物理上是分散式的,所有這些記憶體的集合就是全域位址空間。

RDMA

Remote Direct Memory Access (RDMA) is an extension of the Direct Memory Access (DMA) technology, which is the ability to access host memory directly without CPU intervention. RDMA allows for accessing memory data from one host to another.

远程直接内存访问(英语:Remote Direct Memory Access,RDMA)是一种从一台计算机的内存到另一台计算机的内存的直接内存访问,而不涉及任何一台计算机的操作系统。这允许高吞吐量、低延迟联网,这在大规模并行计算机集群中特别有用。

重点是zero-copy, 不再需要机器缓存,然后拷贝传递信息。 InfiniBand网络默认支持,另一种就是RoCE

relationship between RDMA and NUMA

Most high performance computing clusters are nowadays composed of large multicore machines that expose Non-Uniform Memory Access (NUMA), and they are interconnected using modern communication paradigms, such as Remote Direct Memory Access (RDMA).

结构类型

  • SISD:单指令流单数据流计算机(冯诺依曼机)
  • SIMD:单指令流多数据流计算机
  • MISD:多指令流单数据流计算机, 实际不存在
  • MIMD:多指令流多数据流计算机
  • SIMD-SM

    PRAM(Parallel Random Access Machine)模型是单指令流多数据流(SIMD)并行机中的一种具有共享存储的模型。

    它假设有对其容量大小没有限制的一个共享存储器,并且有多个功能相同的处理器,在任意时刻处理器可以访问共享存储单元。根据是否可以同时读写,它又分为以下三类:PRAM-EREW,PRAM-CREW,PRAM-CRCW(其中C代表Concurrent,意为允许并发操作,E-代表Exclusive,意味排斥并发操作)。在PRAM中有一个同步时钟,所有的操作都是同步进行的。

    具有局部存储器的PRAM模型称作LPRAM模型,具有异步时钟的PRAM模型称作APRAM模型。

    在《并行算法的设计和分析》的第二十章-并行计算理论有额外的定义:

    • 允许任意处理器自由读写的 SIMD-SM。简记为 APRAM-CRCW
    • 只允许所有处理器并发写同一数的SIMD-SM。简记为CPRAM-CRCW
    • 只允许最小号码处理器优先写的SIMD-SM。称作优先PRAM-CRCW。简记为PPRAM-CRCW
    • 一个具有p个处理器的优先PRAM-CRCW模型。称作p-处理器的PPRAM-CRCW。

    几种MIMD

  • PVP并行向量处理机:多VP(向量处理器)通过交叉开关和多个SM(共享内存)相连
  • SMP对称多处理机:多P/C(商品微处理器)通过交叉开关/总线和多个SM(共享内存)相连
  • MPP大规模并行处理机:处理节点有商品微处理器+LM(分布式本地内存)。节点间通过高带宽低延迟定制网络互联,异步MIMD,多个进程有自己的地址空间,通过消息传递机制通信
  • COW工作站机群:节点是完整操作系统的工作站,且有磁盘
  • DSM分布共享存储处理机:快速缓存文件夹DIR确保缓存一致性。将物理分布式LM组成逻辑共享SM从而提供统一地址的编程空间
  • 需要进一步的研究学习

    暂无

    遇到的问题

    暂无

    参考文献


    1. 计算机体系结构 - 量化研究方法第5 版