日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

地址:https://my.oschina.net/u/2475751/blog/1823736

背景

此前工作中,筆者使用perf測(cè)過(guò)CPU的CPI[1],cache miss, 內(nèi)存帶寬等性能指標(biāo)。另外,還移植過(guò)perf uncore[2]相關(guān)的補(bǔ)丁。這些讓我很好奇:perf大概是怎么工作的? 帶著這個(gè)問(wèn)題,筆者謹(jǐn)希望把自己的一點(diǎn)經(jīng)驗(yàn)分享出來(lái)。

perf-list

perf list列出的event有這幾類:1. hardware,如cache-misses; 2. software, 如context switches; 3. cache, 如L1-dcache-loads;4. tracepoint; 5. pmu。 但是,perf list僅僅把有符號(hào)名稱的事件列出來(lái)了,而缺了很多硬件相關(guān)的事件。這些硬件相關(guān)事件叫作Raw Hardware Event, man perf-list有介紹。

舉個(gè)例子,PMU是一組監(jiān)控CPU各種性能的硬件,包括各種core, offcore和uncore事件。單說(shuō)perf uncore, Intel處理器就提供了各種的性能監(jiān)控單元,如內(nèi)存控制器(IMC), 電源控制(PCU)等等,詳見(jiàn)《Intel® Xeon® Processor E5 and E7 v4 Product Families Uncore Performance Monitoring Reference Manual》[3]。這些uncore的PMU設(shè)備,注冊(cè)在MSR space或PCICFG space[4],可以通過(guò)下面命令看到(抹掉同類別設(shè)備):

$ls /sys/devices/ | grep uncore
uncore_cbox_0
uncore_ha_0
uncore_imc_0
uncore_pcu
uncore_qpi_0
uncore_r2pcie
uncore_r3qpi_0
uncore_ubox

但是,使用perf list只能顯示IMC相關(guān)事件:

$perf list|grep uncore
 uncore_imc_0/cas_count_read/ [Kernel PMU event]
 uncore_imc_0/cas_count_write/ [Kernel PMU event]
 uncore_imc_0/clockticks/ [Kernel PMU event]
 ... 
 uncore_imc_3/cas_count_read/ [Kernel PMU event]
 uncore_imc_3/cas_count_write/ [Kernel PMU event]
 uncore_imc_3/clockticks/ [Kernel PMU event]

為什么perf list沒(méi)有顯示其他uncore事件呢?從代碼分析來(lái)看,perf list會(huì)通過(guò)sysfs去讀取uncore設(shè)備所支持的event, 見(jiàn)linux/tools/perf/util/pmu.c:pmu_aliases():

/*
 * Reading the pmu event aliases definition, which should be located at:
 * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
 */
 static int pmu_aliases(const char *name, struct list_head *head)

再看perf uncore的驅(qū)動(dòng)代碼,發(fā)現(xiàn)只有iMC uncore設(shè)備注冊(cè)了events相關(guān)屬性, 見(jiàn)arch/x86/events/intel/uncore_snbep.c:hswep_uncore_imc_events:

static struct uncore_event_desc hswep_uncore_imc_events[] = {
 INTEL_UNCORE_EVENT_DESC(clockticks, "event=0x00,umask=0x00"),
 INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"),
 INTEL_UNCORE_EVENT_DESC(cas_count_read.scale, "6.103515625e-5"),
 INTEL_UNCORE_EVENT_DESC(cas_count_read.unit, "MiB"),
 INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"),
 INTEL_UNCORE_EVENT_DESC(cas_count_write.scale, "6.103515625e-5"),
 INTEL_UNCORE_EVENT_DESC(cas_count_write.unit, "MiB"),
 { /* end: all zeroes */ },
};

從實(shí)用性看,在所有uncore設(shè)備中,系統(tǒng)工程師可能最常用的就是iMC提供的內(nèi)存帶寬監(jiān)測(cè)。其它不常用到的uncore PMU事件,可以通過(guò)Raw Hardware Event的方式,查看Intel Uncore手冊(cè)[5]來(lái)指定。

在使用過(guò)程中,發(fā)現(xiàn)一個(gè)perf list存在的bug,iMC channel的編號(hào)不正確,發(fā)了個(gè)補(bǔ)丁得到了Intel工程師review,upstream還沒(méi)有merge,見(jiàn)perf/x86/intel/uncore: allocate pmu index for pci device dynamically[6]。這是一個(gè)很明顯的問(wèn)題,剛開(kāi)始我不相信上游或Intel會(huì)允許這樣明顯的問(wèn)題存在,雖然問(wèn)題不大,通過(guò)解決這個(gè)問(wèn)題的感受是perf可能隱藏一些問(wèn)題,需要在測(cè)試中提高警惕,最好能通過(guò)其他測(cè)量方式進(jìn)行粗略的對(duì)比驗(yàn)證。

perf-stat

perf-stat是最常用到的命令,用man手冊(cè)的原話就是Run a command and gathers performance counter statistics from it。perf-record命令可看做是perf-stat的一種包裝,核心代碼路徑與perf-stat一樣,加上周期性采樣,用一種可被perf-report解析的格式將結(jié)果輸出到文件。因此,很好奇perf-stat是如何工作的。

perf是由用戶態(tài)的perf tool命令和內(nèi)核態(tài)perf驅(qū)動(dòng)兩部分,加上一個(gè)連通用戶態(tài)和內(nèi)核態(tài)的系統(tǒng)調(diào)用sys_perf_event_open組成。

最簡(jiǎn)單的perf stat示例

perf工具是隨內(nèi)核tree一起維護(hù)的,構(gòu)建和調(diào)試都非常方便:

$cd linux/tools/perf
$make
...
$./perf stat ls
...
 Performance counter stats for 'ls':
 1.011337 task-clock:u (msec) # 0.769 CPUs utilized
 0 context-switches:u # 0.000 K/sec
 0 cpu-migrations:u # 0.000 K/sec
 105 page-faults:u # 0.104 M/sec
 1,105,427 cycles:u # 1.093 GHz
 1,406,263 instructions:u # 1.27 insn per cycle
 282,440 branches:u # 279.274 M/sec
 9,686 branch-misses:u # 3.43% of all branches
 0.001314310 seconds time elapsed

以上是一個(gè)非常簡(jiǎn)單的perf-stat命令,運(yùn)行了ls命令,在沒(méi)有指定event的情況下,輸出了幾種默認(rèn)的性能指標(biāo)。下面,我們以這個(gè)簡(jiǎn)單的perf-stat命令為例分析其工作過(guò)程。

用戶態(tài)工作流

如果perf-stat命令沒(méi)有通過(guò)-e參數(shù)指定任何event,函數(shù)add_default_attributes()會(huì)默認(rèn)添加8個(gè)events。 event是perf工具的核心對(duì)象,各種命令都是圍繞著event工作。perf-stat命令可以同時(shí)指定多個(gè)events,由一個(gè)核心全局變量struct perf_evlist *evsel_list組織起來(lái),以下僅列出幾個(gè)很重要的成員:

struct perf_evlist {
 struct list_head entries;
 bool enabled;
 struct {
 int cork_fd;
 pid_t pid;
 } workload;
 struct fdarray pollfd;
 struct thread_map *threads;
 struct cpu_map *cpus;
 struct events_stats stats;
 ...
} 
  • entries: 所有events列表, 即struct perf_evsel對(duì)象;
  • pid: 運(yùn)行cmd的進(jìn)程pid, 即運(yùn)行l(wèi)s命令的進(jìn)程pid;
  • pollfd: 保存sys_perf_event_open()返回的fd;
  • threads: perf-stat可以通過(guò)-t參數(shù)指定多個(gè)線程,僅在這些線程運(yùn)行時(shí)進(jìn)行計(jì)數(shù);
  • cpus: perf-stat能通過(guò)-C參數(shù)指定多個(gè)cpu, 僅當(dāng)程序運(yùn)行在這些cpu上時(shí)才會(huì)計(jì)數(shù);
  • stats: 計(jì)數(shù)統(tǒng)計(jì)結(jié)果,perf-stat從mmap內(nèi)存區(qū)讀取counter值后,還要做一些數(shù)值轉(zhuǎn)換或聚合等處理

perf_evlist::entries是一個(gè)event鏈表,鏈接的對(duì)象是一個(gè)個(gè)event,由struct perf_evsel表示,其中非常重要的成員如下:

struct perf_evsel {
char *name;
struct perf_event_attr attr;
struct perf_counts *counts;
struct xyarray *fd;
struct cpu_map *cpus;
struct thread_map *threads;
}
  • name: event的名稱;
  • attr: event的屬性,傳遞給perf系統(tǒng)調(diào)用非常重要的參數(shù);
  • cpus, threads, fd: perf-stat可以指定一些對(duì)event計(jì)數(shù)的限制條件,只統(tǒng)計(jì)哪些task或哪些cpu, 其實(shí)就是一個(gè)由struct xyarray表示的二維表格,最終的計(jì)數(shù)值被分解成cpus*threads個(gè)小的counter,sys_perf_event_open()請(qǐng)求perf驅(qū)動(dòng)為每個(gè)分量值創(chuàng)建一個(gè)子counter,并分別返回一個(gè)fd;
  • counts: perf_counts::values保存每個(gè)分量計(jì)數(shù)值,perf_counts::aggr保存最終所有分量的聚合值。

perf的性能計(jì)數(shù)器本質(zhì)上是一些特殊的硬件寄存器,perf對(duì)這樣的硬件能力進(jìn)行抽象,提供針對(duì)event的per-CPU和per-thread的64位虛機(jī)計(jì)數(shù)器("virtual" 64-bit counters)。當(dāng)perf-stat不指定任何thread或cpu時(shí),這樣的一個(gè)二維表格就變成一個(gè)點(diǎn),即一個(gè)event對(duì)應(yīng)一個(gè)counter,對(duì)應(yīng)一個(gè)fd。

簡(jiǎn)單介紹了核心數(shù)據(jù)結(jié)構(gòu),終于可以繼續(xù)看看perf-stat的工作流了。perf-stat的工作邏輯主要在__run_perf_stat()中,大致是這樣: a. fork一個(gè)子進(jìn)程,準(zhǔn)備用來(lái)運(yùn)行cmd,即示例中的ls命令;b. 為每一個(gè)event事件,通過(guò)sys_perf_event_open()系統(tǒng)調(diào)用,創(chuàng)建一個(gè)counter; c. 通過(guò)管道給子進(jìn)程發(fā)消息,exec命令, 即運(yùn)行示例中的ls命令, 并立即enable計(jì)數(shù)器; d. 當(dāng)程序運(yùn)行結(jié)束后,disable計(jì)數(shù)器,并讀取counter。 用戶態(tài)的工作流大致如下:

__run_perf_stat()
 perf_evlist__prepare_workload()
 create_perf_stat_counter()
 sys_perf_event_open()
 enable_counters()
 perf_evsel__run_ioctl(evsel, ncpus, nthreads, PERF_EVENT_IOC_DISABLE)
 ioctl(fd, ioc, arg)
 wait()
 disable_counters()
 perf_evsel__run_ioctl(evsel, ncpus, nthreads, PERF_EVENT_IOC_ENABLE)
 read_counters()
 perf_evsel__read(evsel, cpu, thread, count)
 readn(fd, count, size)

用戶態(tài)工作流比較清晰,最終都可以很方便通過(guò)ioctl()控制計(jì)數(shù)器,通過(guò)read()讀取計(jì)數(shù)器的值。而這樣方便的條件都是perf系統(tǒng)調(diào)sys_perf_event_open()用創(chuàng)造出來(lái)的,已經(jīng)迫不及待想看看這個(gè)系統(tǒng)調(diào)用做了些什么。

perf系統(tǒng)調(diào)用

perf系統(tǒng)調(diào)用會(huì)為一個(gè)虛機(jī)計(jì)數(shù)器(virtual counter)打開(kāi)一個(gè)fd,然后perf-stat就通過(guò)這個(gè)fd向perf內(nèi)核驅(qū)動(dòng)發(fā)請(qǐng)求。perf系統(tǒng)調(diào)用定義如下(linux/kernel/events/core.c):

/**
 * sys_perf_event_open - open a performance event, associate it to a task/cpu
 *
 * @attr_uptr: event_id type attributes for monitoring/sampling
 * @pid: target pid
 * @cpu: target cpu
 * @group_fd: group leader event fd
 */
SYSCALL_DEFINE5(perf_event_open,
 struct perf_event_attr __user *, attr_uptr,
 pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)

特別提一下, struct perf_event_attr是一個(gè)信息量很大的結(jié)構(gòu)體,kernel中有文檔詳細(xì)介紹[7]。其它參數(shù)如何使用,man手冊(cè)有詳細(xì)的解釋,并且手冊(cè)最后還給出了用戶態(tài)編程例子,見(jiàn)man perf_event_open。

sys_perf_event_open()主要做了這幾件事情:

a. 根據(jù)struct perf_event_attr,創(chuàng)建和初始化struct perf_event, 它包含幾個(gè)重要的成員:

/**
 * struct perf_event - performance event kernel representation:
 */
struct perf_event {
	struct pmu *pmu; //硬件pmu抽象
	local64_t count; // 64-bit virtual counter
	u64 total_time_enabled;
	u64 total_time_running;
	struct perf_event_context *ctx; // 與task相關(guān)
...
}

b. 為這個(gè)event找到或創(chuàng)建一個(gè)struct perf_event_context, context和event是1:N的關(guān)系,一個(gè)context會(huì)與一個(gè)進(jìn)程的task_struct關(guān)聯(lián),perf_event_count::event_list表示所有對(duì)這個(gè)進(jìn)程感興趣的事件, 它包括幾個(gè)重要成員:

struct perf_event_context {
 struct pmu *pmu;
 struct list_head event_list;
 struct task_struct *task;
 ...
}

c. 把event與一個(gè)context進(jìn)行關(guān)聯(lián),見(jiàn)perf_install_in_context();

d. 最后,把fd和perf_fops進(jìn)行綁定:

static const struct file_operations perf_fops = {
 .llseek = no_llseek,
 .release = perf_release,
 .read = perf_read,
 .poll = perf_poll,
 .unlocked_ioctl = perf_ioctl,
 .compat_ioctl = perf_compat_ioctl,
 .mmap = perf_mmap,
 .fasync = perf_fasync,
};

perf系統(tǒng)調(diào)用大致的調(diào)用鏈如下:

sys_perf_event_open()
	get_unused_fd_flags()
 	perf_event_alloc()
 	find_get_context()
 		alloc_perf_context()
 	anon_inode_getfile()
 	perf_install_in_context()
 		add_event_to_ctx()
 	fd_install(event_fd, event_file)

內(nèi)核態(tài)工作流

perf event有兩種方式:計(jì)數(shù)(counting)和采樣(sampled)。計(jì)數(shù)方式會(huì)對(duì)發(fā)生在所有指定cpu和指定進(jìn)程的事件次數(shù)進(jìn)行求和,對(duì)事件數(shù)值通過(guò)read()獲得。而采樣方式會(huì)周期性地把計(jì)數(shù)結(jié)果放在由mmap()創(chuàng)建的ring buffer中。回到開(kāi)始的簡(jiǎn)單perf-stat示例,用的是計(jì)數(shù)(counting)方式。

接下來(lái),我們主要了解這幾個(gè)問(wèn)題:

  1. 怎么enable和disable計(jì)數(shù)器?
  2. 進(jìn)行計(jì)數(shù)的時(shí)機(jī)在哪里?
  3. 如何讀取計(jì)數(shù)結(jié)果?

回答這些問(wèn)題的入口,基本都在perf實(shí)現(xiàn)的文件操作集中:

static const struct file_operations perf_fops = {
 .read = perf_read,
 .unlocked_ioctl = perf_ioctl,
...

首先,我們看一下怎樣enable計(jì)數(shù)器的,主要步驟如下:

perf_ioctl()
	__perf_event_enable()
		ctx_sched_out() IF ctx->is_active
		ctx_resched()
			perf_pmu_disable()
			task_ctx_sched_out()
			cpu_ctx_sched_out()
			perf_event_sched_in()
				event_sched_in()
					event->pmu->add(event, PERF_EF_START)
			perf_pmu_enable()
				pmu->pmu_enable(pmu)

這個(gè)過(guò)程有很多調(diào)度相關(guān)的處理,使整個(gè)邏輯顯得復(fù)雜,我們暫且不關(guān)心太多調(diào)度細(xì)節(jié)。硬件的PMU資源是有限的,當(dāng)event數(shù)量多于可用的PMC時(shí),多個(gè)virtual counter就會(huì)復(fù)用硬件PMC。因此, PMU先把event添加到激活列表(pmu->add(event, PERF_EF_START)), 最后enable計(jì)數(shù)(pmu->pmu_enable(pmu) )。PMU是CPU體系結(jié)構(gòu)相關(guān)的,可以想象它有一套為event分配具體硬件PMC的邏輯,我們暫不深究。

我們繼續(xù)了解如何獲取計(jì)數(shù)器結(jié)果,大致的callchain如下:

perf_read()
	perf_read_one()
		perf_event_read_value()
			__perf_event_read()
				pmu->start_txn(pmu, PERF_PMU_TXN_READ)
				pmu->read(event)
				pmu->commit_txn(pmu)

PMU最終會(huì)通過(guò)rdpmcl(counter, val)獲得計(jì)數(shù)器的值,保存在perf_event::count中。關(guān)于PMU各種操作說(shuō)明,可以參考include/linux/perf_event.h:struct pmu{}。PMU操作的實(shí)現(xiàn)是體系結(jié)構(gòu)相關(guān)的,x86上的read()的實(shí)現(xiàn)是arch/x86/events/core.c:x86_pmu_read()。

event可以設(shè)置限定條件,僅當(dāng)指定的進(jìn)程運(yùn)行在指定的cpu上時(shí),才能進(jìn)行計(jì)數(shù),這就是上面提到的計(jì)數(shù)時(shí)機(jī)問(wèn)題。很容易想到,這樣的時(shí)機(jī)發(fā)生在進(jìn)程切換的時(shí)候。當(dāng)目標(biāo)進(jìn)程切換出目標(biāo)CPU時(shí),PMU停止計(jì)數(shù),并將硬件寄保存在內(nèi)存變量中,反之亦然,這個(gè)過(guò)程類似進(jìn)程切換時(shí)對(duì)硬件上下文的保護(hù)。在kernel/sched/core.c, 我們能看到這些計(jì)數(shù)時(shí)機(jī)。

在進(jìn)程切換前:

prepare_task_switch()
	perf_event_task_sched_out()
		__perf_event_task_sched_out() // stop each event and update the event value in event->count
			perf_pmu_sched_task()
				pmu->sched_task(cpuctx->task_ctx, sched_in)

進(jìn)程切換后:

finish_task_switch()
	perf_event_task_sched_in()
		perf_event_context_sched_in()
			perf_event_sched_in()

小結(jié)

通過(guò)對(duì)perf-list和perf-stat這兩個(gè)基本的perf命令進(jìn)行分析,引出了一些有意思的問(wèn)題,在嘗試回答這些問(wèn)題的過(guò)程中,基本上總結(jié)了目前我對(duì)perf這個(gè)工具的認(rèn)識(shí)。但是,本文僅對(duì)perf的工作原理做了很粗略的梳理,也沒(méi)有展開(kāi)對(duì)PMU層,perf uncore等硬件相關(guān)代碼進(jìn)行分析,希望以后能補(bǔ)上這部分內(nèi)容。

最后,能堅(jiān)持看到最后的親們都是希望更深了解性能測(cè)試的,作為福利給大家推薦本書(shū):《system performance: enterprise and the cloud》(https://pan.baidu.com/s/1yyPsJxi0XWSwIKOrAWm-Vg?errno=0&errmsg=Auth%20Login%20Sucess&&bduss=&ssnerror=0&traceid=) 書(shū)的作者是一位從事多年性能優(yōu)化工作的一線工程師,想必大家都聽(tīng)說(shuō)過(guò)他寫(xiě)的火焰圖程序: perf Examples【http://www.brendangregg.com/perf.html】

Cheers!

參考索引

  1. Cycles per instruction: https://en.wikipedia.org/wiki/Cycles_per_instruction
  2. uncore: https://en.wikipedia.org/wiki/Uncore
  3. 《Intel® Xeon® Processor E5 and E7 v4 Product Families Uncore Performance Monitoring Reference Manual》
  4. 《Linux設(shè)備驅(qū)動(dòng)程序》中第二章PCI驅(qū)動(dòng)程序
  5. https://patchwork.kernel.org/patch/10412883/
  6. linux/tools/perf/design.txt

分享到:
標(biāo)簽:linux perf
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定