一文读懂TSC时钟: (x86_64/arm64)实现介绍和编程使用

news2024/11/19 18:21:37

Linux(16)之Time Stamp Counter

Author:Once Day Date:2023年5月30日

参考文档:

  • 4. Environment Abstraction Layer — Data Plane Development Kit 23.03.0 documentation (dpdk.org)
  • DPDK: lib/eal/include/generic/rte_cycles.h File Reference
  • 测量CPU的利器 - TSC (Time Stamp Counter) - 知乎 (zhihu.com)
  • 正确使用cpu提供的TSC - 知乎 (zhihu.com)
  • x86 TSC使用的那些坑 - 爱你一万年123 - 博客园 (cnblogs.com)
  • Time Stamp Counter (TSC) 知识点 - 简书 (jianshu.com)
  • 细说RDTSC的坑 – Dreamer Thinker Doer (wangkaixuan.tech)
  • 调整开发板arm的cpu频率_armv8 cpuinfo pll_思而后行之的博客-CSDN博客
  • Pitfalls of TSC usage | Oliver Yang
  • timestamp - Getting TSC rate from x86 kernel - Stack Overflow
  • linux - rdtsc accuracy across CPU cores - Stack Overflow
  • Porting x86架构的rdtsc函数到ARM64架构的方法 - 极术社区 - 连接开发者与智能计算生态 (aijishu.com)
  • Arm Architecture Reference Manual for A-profile architecture
  • 华为鲲鹏云 KBengine arm64编译问题实践报告-云社区-华为云 (huaweicloud.com)

文章目录

    • Linux(16)之Time Stamp Counter
        • 1. 概述
        • 2. DPDK的两种时间戳计时器(TSC,HPET)
        • 3. 时间戳计数器(TSC,Time Stamp Counter)详细总结
        • 4. TSC发展历史
        • 5. TSC多核时钟深入分析
        • 6. ARM架构的“TSC”时钟
        • 7. 实际代码演示
          • 7.1 x86_64架构DPDK获取TSC频率和cycle代码
          • 7.2 ARM64架构获取TSC(System Counter)

1. 概述

DPDK(数据包开发处理包,Data Plane Development Kit)是一套用于快速处理数据包的库和驱动程序。TSC(时间戳计数器,Time Stamp Counter)是一个高精度计时器,用于在CPU内核上测量时间。TSC是一个64位的寄存器,每个CPU内核都有一个TSC,它在每个时钟周期内递增。

在不同的CPU核上,TSC的周期可能相同,也可能不同。这取决于以下几个因素:

  1. 同步TSC:现代处理器实现了TSC的同步,即在所有内核上同时启动和递增TSC。这意味着在这些处理器上,所有内核上的TSC周期应该是相同的。要确定处理器是否支持同步TSC,可以检查处理器的规格文档,或者查询CPUID指令的相关字段。

  2. 动态调节频率(如Intel SpeedStep,AMD Cool’n’Quiet等):某些处理器可以根据负载动态调整CPU频率。这可能导致不同内核上的TSC以不同的速度增加,因为每个内核的运行频率可能不同。在这种情况下,不同内核上的TSC周期可能不同。要解决这个问题,可以将处理器设置为固定频率运行,或者使用不受动态频率调整影响的计时器,如HPET(高精度事件计时器,High Precision Event Timer)。

  3. 多处理器系统(如多个物理CPU的服务器):在具有多个物理处理器的系统中,每个处理器都有自己的TSC。这可能导致不同处理器上的内核具有不同的TSC周期。可以尝试使用软件技术,如RDTSC(读取时间戳计数器指令,Read Time Stamp Counter instruction)或者使用更适合多处理器系统的计时器,如HPET来解决这个问题。

总之,不同CPU核上的TSC周期可能相同,也可能不同。确定TSC周期是否相同通常需要考虑处理器架构、动态频率调整技术和多处理器系统等因素。在需要精确计时的场景中,可以使用其他计时器,如HPET,以避免潜在的问题。

2. DPDK的两种时间戳计时器(TSC,HPET)

在DPDK中,时间戳计数器(TSC,Time Stamp Counter)和高精度事件计时器(HPET,High Precision Event Timer)是两种用于测量时间的方法。它们之间的使用关系可以从以下几个方面进行总结:

  1. 时间测量精度和性能

    • TSC:TSC是高精度的计时器,它在每个时钟周期内递增。由于TSC读取速度快,延迟低,因此在性能要求较高的场景中,DPDK可能优先使用TSC作为计时器。
    • HPET:HPET也是一种高精度计时器,但相对于TSC,它的读取速度较慢,延迟较高。然而,HPET在多处理器系统和动态频率调整场景下表现更加稳定,因此在这些情况下,DPDK可能会选择使用HPET作为计时器。
  2. TSC和HPET的选择

    • DPDK在启动时会自动检测并选择合适的计时器。首选TSC,因为它具有较高的性能。然而,如果检测到TSC在多核处理器、多处理器系统或动态频率调整场景下可能存在不稳定性,DPDK会退回到使用HPET作为计时器。
  3. 定时器API

    • DPDK提供了通用的定时器API,这些API抽象了底层的计时器实现(如TSC和HPET),使得DPDK应用程序可以在不关心底层计时器类型的情况下进行时间测量和调度。这意味着DPDK应用程序开发者不需要直接处理TSC和HPET之间的使用关系,而可以通过DPDK提供的API来实现所需的计时功能。

总之,在DPDK中,TSC和HPET是两种用于测量时间的方法。它们之间的使用关系主要取决于性能需求和特定场景下的稳定性。DPDK会自动选择合适的计时器,应用程序开发者可以通过DPDK提供的通用定时器API来实现计时功能,而无需直接处理TSC和HPET之间的关系。

3. 时间戳计数器(TSC,Time Stamp Counter)详细总结

(1)优点

  • 高精度:TSC是一个高精度计数器,每个CPU内核上的TSC寄存器在每个时钟周期内递增,因此可以提供非常精确的时间测量。
  • 低延迟:相比其他计时器(如HPET),TSC读取速度更快,延迟更低。
  • 广泛支持:绝大多数现代处理器支持TSC,使其成为一种通用的计时解决方案。

(2)缺点

  • 同步问题:在多核处理器和多处理器系统中,不同CPU核上的TSC可能不完全同步,导致时间测量不一致。
  • 动态频率调整:动态调节CPU频率(如Intel SpeedStep,AMD Cool’n’Quiet等)可能导致TSC以不同速度增加,影响精确度。
  • 虚拟化环境:在虚拟化环境中,TSC的行为可能受到虚拟机监视器(hypervisor)的影响,导致不准确的时间测量。

(3)使用方法

  • 读取TSC:可以通过执行RDTSC(读取时间戳计数器)指令来读取当前TSC值。在C/C++中,可以使用内联汇编或者使用编译器提供的内建函数(如__rdtsc())来读取TSC。
  • 计算时间差:通过在程序的不同点读取TSC,可以计算两个时间点之间的时钟周期数。然后,将时钟周期数除以CPU频率(单位为Hz),可以得到时间差(单位为秒)。

(4)注意事项

  • 确保同步:在使用TSC之前,应检查处理器是否支持同步TSC。可以查询CPUID指令的相关字段或处理器规格文档来获取这些信息。
  • 考虑动态频率调整:在受到动态频率调整影响的处理器上使用TSC时,应将处理器设置为固定频率运行,或使用不受动态频率调整影响的计时器(如HPET)。
  • 多处理器系统:在多处理器系统中,应使用适用于多处理器系统的计时器(如HPET),或使用软件技术来解决不同处理器上TSC不同步的问题。
  • 考虑到CPU乱序执行的问题,rdtsc需要配合cpuid或lfence指令,以保证计这一刻流水线已排空,即rdtsc要测量的指令已执行完。后来的CPU提供了rdtscp指令,相当于cpuid + rdtsc,但cpuid指令本身的执行周期有波动,而rdtscp指令的执行更稳定。
  • 多核系统:新的CPU支持了Invariant TSC特性,可以保证在默认情况下各核心看到的TSC是一致的,否则测量代码执行时不能调度至其它核心上。
  • 时序测量容易被干扰(线程调度、抢占、系统中断、虚拟化等),要求测量的指令序列尽量短,并且需要进行多次测量

(5)常见问题

  • TSC不同步:多核处理器和多处理器系统中,不同CPU核上的TSC可能不完全同步。可以尝试使用软件技术进行校正,或使用其他计时器(如HPET)。
  • 动态频率调整:动态调节CPU频率可能导致TSC以不同速度增加。可以将处理器设置为固定频率运行,或使用不受动态频率调整影响的计时器(如HPET)。
  • 虚拟化环境:虚拟机中的TSC可能受到虚拟机监视器(hypervisor)的影响。在虚拟化环境中,建议使用虚拟化友好的计时器,如虚拟机监视器提供的虚拟化时钟(如KVM中的kvm-clock)。

4. TSC发展历史

参考文档:

  • 正确使用cpu提供的TSC - 知乎 (zhihu.com)
  • Pitfalls of TSC usage | Oliver Yang

最早CPU提供的TSC有很多弊端:

  • 频率受cpu频率影响,进入C-state的某些深度级别甚至会停止工作(不再跳动)
  • SMP架构下core间不同步,意味着一个core上的tsc与其它core上不一样,并且跳变的频率也不同。

后来Intel进行了增强(在CPU特性标识里面可以查看):

  • constant_tsc:含义是以固定的频率跳动,与cpu当前的频率无关。
  • nonstop_tsc:进入C-State也不会停止跳动。

基于这2个特性组合,称为 invariant tsc,即tsc是以理想中的恒定频率跳动,符合对时钟的假设。

SMP架构下不同步的问题,有内核来进行判定:

  • Linux 内核启动时,探测tsc是否同步,采用尝试校准多个核心上的tsc以相同的频率和起始值启动运行。
  • 通过写入MSR寄存器值来设置tsc的特性,需要cpu支持,目前仅仅intel的cpu才可能被认为是多核同步的

TSC的频率有以下的获取方式:

  • 通过CPUID中的一些寄存器值来计算,较新的cpu可以。
  • 通过读取MSR寄存器的值来计算,需要跟进不同的CPU model来读取不同的寄存器。
  • 通过读取内核export的符号 tsc_khz

如下是读取内核符号:

bpftrace -e 'BEGIN { printf("%u\n", *kaddr("tsc_khz")); exit(); }'

此外,内核计算和调整后的tsc freqency和经过硬件寄存器计算出来的不一定相同,因为内核会进行calibrate。

5. TSC多核时钟深入分析

参考文档:

  • 细说RDTSC的坑 – Dreamer Thinker Doer (wangkaixuan.tech)
  • linux - rdtsc accuracy across CPU cores - Stack Overflow
  • Porting x86架构的rdtsc函数到ARM64架构的方法 - 极术社区 - 连接开发者与智能计算生态 (aijishu.com)
  • x86 TSC使用的那些坑 - 爱你一万年123 - 博客园 (cnblogs.com)

在同一处理器的多个核心之间,以及不同处理器的不同核心之间,rdtsc的结果是否是同步的呢?如果不同步,那么取时的结果就不能用来相互比较。

关于这点,Intel的官方手册没有明说,如下:

The time stamp counter in newer processors may support an enhancement, referred to as invariant TSC. Processor’s support for invariant TSC is indicated by CPUID.80000007H:EDX[8].
The invariant TSC will run at a constant rate in all ACPI P-, C-. and T-states. This is the architectural behavior moving forward. On processors with invariant TSC support, the OS may use the TSC for wall clock timer services (instead of ACPI or HPET timers). TSC reads are much more efficient and do not incur the overhead associated with a ring transition or access to a platform resource.

只是说TSC能够在CPU处于任何(电源)状态下都能保证以标称速率递增,并没有明确说明TSC能够在多核甚至多处理器的情况下保持同步

在Linux内核启动时,对TSC(时间戳计数器,Time Stamp Counter)时钟进行处理的过程可分为以下几个步骤

  1. 检测TSC特性:内核首先使用CPUID指令来检测CPU是否支持TSC。如果CPU支持TSC,内核将继续检查其他TSC相关特性,例如:

    • 是否支持不变TSC(Invariant TSC):不变TSC在所有内核和处理器之间同步,且不受CPU频率和电源管理事件影响。
    • 是否支持恒定TSC(Constant TSC):恒定TSC在所有内核和处理器之间同步,但可能受到CPU频率调整的影响。
  2. 校准TSC:为了将TSC时钟周期转换为实际时间,内核需要知道CPU的时钟频率。在启动期间,内核将校准TSC,以便将其与实际时间对齐。校准过程通常涉及在一定时间间隔内计算TSC增量,然后根据这些增量推断出CPU时钟频率。

  3. 选择时钟源:Linux内核支持多种时钟源,例如TSC、HPET(高精度事件计时器,High Precision Event Timer)和ACPI Power Management Timer。在启动时,内核将根据可用时钟源的精度和性能选择最佳时钟源。如果TSC具有恒定或不变特性,并且表现出良好的性能和精度,内核可能会将其设置为默认时钟源。

  4. 同步多处理器系统中的TSC:在多处理器系统中,内核需要确保所有处理器上的TSC是同步的。内核将使用特定的同步算法(如校准时钟偏移)来尽量确保不同处理器上的TSC值保持一致。然而,这种同步并非总是完美的,因此在多处理器系统中使用TSC时需要小心。

  5. 初始化调度时钟:在内核启动过程中,它还需要初始化调度时钟,该时钟用于内核调度器来决定何时运行进程和线程。如果TSC被选为默认时钟源,内核将使用TSC来初始化和维护调度时钟。

内核检查TSC是否同步代码如下(X86结构):

/*
 * Make an educated guess if the TSC is trustworthy and synchronized
 * over all CPUs.
 */
int unsynchronized_tsc(void)
{
	if (!boot_cpu_has(X86_FEATURE_TSC) || tsc_unstable)
		return 1;

#ifdef CONFIG_SMP
	if (apic_is_clustered_box())
		return 1;
#endif

	if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
		return 0;

	if (tsc_clocksource_reliable)
		return 0;
	/*
	 * Intel systems are normally all synchronized.
	 * Exceptions must mark TSC as unstable:
	 */
	if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) {
		/* assume multi socket systems are not synchronized: */
		if (num_possible_cpus() > 1)
			return 1;
	}

	return 0;
}

从这段代码可以获取下面的信息:

  • 如果你的cpuinfo里有constant_tsc的flag,那么无论在同一CPU不同核心之间,还是在不同CPU的不同核心之间,TSC都是同步的,可以随便用。
  • 如果你用的是Intel的CPU,但是cpuinfo里没有constant_tsc的flag,那么在同一处理器的不同核心之间,TSC仍然是同步的,但是不同CPU的不同核心之间不同步,尽量不要用。
  • 在Intel CPU下还有一个注释“assume multi socket systems are not synchronized”,即在多处理器系统上,不同CPU(处理器、socket、NUMA节点)之间的TSC是不同步的。

Non-intel x86 platform has different stories. Current Linux kernel treats all non-intel SMP system as non-sync TSC system. See unsynchronized_tsc code in tsc.c. LKML also has the AMD documents.

非英特尔x86平台有不同的情况。当前Linux内核将所有非intel SMP系统视为非同步TSC系统

6. ARM架构的“TSC”时钟

参考文档:

  • Porting x86架构的rdtsc函数到ARM64架构的方法 - 极术社区 - 连接开发者与智能计算生态 (aijishu.com)
  • Arm Architecture Reference Manual for A-profile architecture
  • 华为鲲鹏云 KBengine arm64编译问题实践报告-云社区-华为云 (huaweicloud.com)
  • 【ARMv8】通用定时器总结_arm system counter_从善若水的博客-CSDN博客

ARM64架构(也称为ARMv8-A架构)引入了一种称为系统计数器(System Counter)的新组件,用于提供一个单调递增的计时器,以便在ARM64系统中实现精确的时间测量和调度。系统计数器在ARM64架构中相当重要,因为它为操作系统和应用程序提供了一个稳定、可靠的计时器。

在这里插入图片描述

系统计数器具有以下主要特点:

  1. 64位单调递增计数器:系统计数器是一个64位宽度的寄存器,它在每个时钟周期内递增。由于它是单调递增的,因此不会受到任何系统事件的影响,如电源管理事件或处理器休眠状态。

  2. 全局同步:系统计数器在所有处理器核心和处理器之间保持全局同步。这意味着,在多处理器系统中,不需要额外的同步机制以确保计数器的一致性。

  3. 基于架构的访问:ARM64架构提供了一组指令,以便操作系统和应用程序可以直接访问系统计数器。这些指令包括:

    • CNTFRQ_EL0:用于读取系统计数器的频率,以便将计数器值转换为实际时间。
    • CNTPCT_EL0:用于读取系统计数器的当前计数值。
  4. 异常级别访问控制:ARM64架构中的异常级别(EL)机制允许操作系统控制应用程序和其他操作系统组件对系统计数器的访问。例如,操作系统可以允许用户级别应用程序(在EL0运行)访问系统计数器,也可以将其限制在内核级别(在EL1或更高级别运行)。

  5. 虚拟化支持:ARM64架构还为虚拟化环境提供了系统计数器支持。在虚拟化环境中,宿主操作系统可以为每个虚拟机配置虚拟系统计数器,从而使它们能够使用类似于物理计数器的计时功能。

Linux内核在ARM64架构上使用该定时器来实现"TSC时钟"

u64 rdtsc(void)
{
	u64 val;

	/*
	 * According to ARM DDI 0487F.c, from Armv8.0 to Armv8.5 inclusive, the
	 * system counter is at least 56 bits wide; from Armv8.6, the counter
	 * must be 64 bits wide.  So the system counter could be less than 64
	 * bits wide and it is attributed with the flag 'cap_user_time_short'
	 * is true.
	 */
	asm volatile("mrs %0, cntvct_el0" : "=r" (val));

	return val;
}

system counter的精度一般不会超过100MHz,一般是达不到CPU cycle级别的精度。

因此还可以借助PMU系列寄存器中的PMCCNTR_EL0(需要内核开启使能),读取此寄存器就可以知道当前CPU已运行了多少cycle

7. 实际代码演示

7.1 x86_64架构DPDK获取TSC频率和cycle代码
  • timestamp - Getting TSC rate from x86 kernel - Stack Overflow

最简单的方法是通过dmesg消息来获取:

onceday->~:# dmesg  |grep tsc
[    0.000001] tsc: Detected 2995.199 MHz processor

其频率也和lscpu里面的BogoMIPS有关,是其二分之一:

BogoMIPS:            5990.39

其次也可以通过代码来获取,下面代码源自dpdk

/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2017 Intel Corporation
 */
#include <stdio.h>
#include <stdint.h>

#include <fcntl.h>
#include <unistd.h>
#include <cpuid.h>

static unsigned int rte_cpu_get_model(uint32_t fam_mod_step)
{
    uint32_t family, model, ext_model;

    family = (fam_mod_step >> 8) & 0xf;
    model  = (fam_mod_step >> 4) & 0xf;

    if (family == 6 || family == 15) {
        ext_model = (fam_mod_step >> 16) & 0xf;
        model += (ext_model << 4);
    }

    return model;
}

static int32_t rdmsr(int msr, uint64_t *val)
{
    int fd;
    int ret;

    fd = open("/dev/cpu/0/msr", O_RDONLY);
    if (fd < 0)
        return fd;

    ret = pread(fd, val, sizeof(uint64_t), msr);

    close(fd);

    return ret;
}

static uint32_t check_model_wsm_nhm(uint8_t model)
{
    switch (model) {
    /* Westmere */
    case 0x25:
    case 0x2C:
    case 0x2F:
    /* Nehalem */
    case 0x1E:
    case 0x1F:
    case 0x1A:
    case 0x2E:
        return 1;
    }

    return 0;
}

static uint32_t check_model_gdm_dnv(uint8_t model)
{
    switch (model) {
    /* Goldmont */
    case 0x5C:
    /* Denverton */
    case 0x5F:
        return 1;
    }

    return 0;
}

uint64_t get_tsc_freq_arch(void)
{
    uint64_t tsc_hz = 0;
    uint32_t a, b, c, d, maxleaf;
    uint8_t  mult, model;
    int32_t  ret;

    /*
     * Time Stamp Counter and Nominal Core Crystal Clock
     * Information Leaf
     */
    maxleaf = __get_cpuid_max(0, NULL);
    printf("maxleaf: %d\n", maxleaf);
    if (maxleaf >= 0x15) {
        __cpuid(0x15, a, b, c, d);

        /* EBX : TSC/Crystal ratio, ECX : Crystal Hz */
        if (b && c)
            return c * (b / a);
    }

    __cpuid(0x1, a, b, c, d);
    model = rte_cpu_get_model(a);
    printf("model: %d\n", model);

    if (check_model_wsm_nhm(model))
        mult = 133;
    else if ((c & bit_AVX) || check_model_gdm_dnv(model))
        mult = 100;
    else
        return 0;

    printf("mult: %d\n", mult);

    ret = rdmsr(0xCE, &tsc_hz);
    if (ret < 0)
        return 0;

    return ((tsc_hz >> 8) & 0xff) * mult * 1E6;
}

/** C extension macro for environments lacking C11 features. */
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L
#define RTE_STD_C11 __extension__
#else
#define RTE_STD_C11
#endif

uint64_t rte_rdtsc(void)
{
    union {
        uint64_t tsc_64;

        // RTE_STD_C11

        struct {
            uint32_t lo_32;
            uint32_t hi_32;
        };
    } tsc;

#ifdef RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT
    if (unlikely(rte_cycles_vmware_tsc_map)) {
        /* ecx = 0x10000 corresponds to the physical TSC for VMware */
        asm volatile("rdpmc" : "=a"(tsc.lo_32), "=d"(tsc.hi_32) : "c"(0x10000));
        return tsc.tsc_64;
    }
#endif

    asm volatile("rdtsc" : "=a"(tsc.lo_32), "=d"(tsc.hi_32));
    return tsc.tsc_64;
}

uint64_t rte_get_tsc_cycles(void)
{
    return rte_rdtsc();
}

/**
 * Macro to align a value to the multiple of given value. The resultant
 * value will be of the same type as the first parameter and will be no lower
 * than the first parameter.
 */
#define RTE_ALIGN_MUL_CEIL(v, mul) \
    ((((v) + (typeof(v))(mul)-1) / ((typeof(v))(mul))) * (typeof(v))(mul))

/**
 * Macro to align a value to the multiple of given value. The resultant
 * value will be of the same type as the first parameter and will be no higher
 * than the first parameter.
 */
#define RTE_ALIGN_MUL_FLOOR(v, mul) (((v) / ((typeof(v))(mul))) * (typeof(v))(mul))

/**
 * Macro to align value to the nearest multiple of the given value.
 * The resultant value might be greater than or less than the first parameter
 * whichever difference is the lowest.
 */
#define RTE_ALIGN_MUL_NEAR(v, mul)                     \
    ({                                                 \
        typeof(v) ceil  = RTE_ALIGN_MUL_CEIL(v, mul);  \
        typeof(v) floor = RTE_ALIGN_MUL_FLOOR(v, mul); \
        (ceil - (v)) > ((v)-floor) ? floor : ceil;     \
    })

#define CYC_PER_10MHZ 1E7

int main(void)
{
    uint64_t start, end, mhz;
    uint64_t tsc_hz = get_tsc_freq_arch();
    printf("tsc_hz: %lu\n", tsc_hz);
    start = rte_get_tsc_cycles();
    sleep(1);
    end = rte_get_tsc_cycles();
    printf("start_clock: %lu\n", start);
    printf("end_clock: %lu\n", end);
    printf("diff_clock: %lu\n", end - start);
    /* Round up to 10Mhz. 1E7 ~ 10Mhz */
    mhz = (end - start);
    mhz = RTE_ALIGN_MUL_NEAR(mhz, CYC_PER_10MHZ);
    printf("mhz: %lu Mhz\n", (uint64_t)(mhz/1E6));
    return 0;
}

编译运行后,输出如下(获取频率失败,只能走手动测量):

ubuntu->performance:$ ./tsc.out 
maxleaf: 13
model: 94
mult: 100
tsc_hz: 0
start_clock: 30482663960076192
end_clock: 30482687903183604
diff_clock: 23943107412
mhz: 2390 Mhz
7.2 ARM64架构获取TSC(System Counter)

获取cycle值和频率比较方便,直接使用汇编读取其值。

/** Read generic counter frequency */
static uint64_t __rte_arm64_cntfrq(void)
{
    uint64_t freq;

    asm volatile("mrs %0, cntfrq_el0" : "=r"(freq));
    return freq;
}

/** Read generic counter */
static uint64_t __rte_arm64_cntvct(void)
{
    uint64_t tsc;

    asm volatile("mrs %0, cntvct_el0" : "=r"(tsc));
    return tsc;
}
d_clock: 30482687903183604
diff_clock: 23943107412
mhz: 2390 Mhz

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/591578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

交换机安全功能介绍

今天海翎光电的小编来给大家聊聊以太网交换机安全功能。 交换机作为局域网中最常见的设备&#xff0c;在安全上面临着重大威胁&#xff0c;这些威胁有的是针对交换机管理上的漏洞&#xff0c;攻击者试图控制交换机。有的针对的是交换机的功能&#xff0c;攻击者试图扰乱交换机的…

新手快速学会使用DDL对数据库和表的操作

前言 SQL是一种操作关系型数据库的结构化查询语言。今天这篇文章将详细讲述数据定义语言DDL对数据库和表的相关操作。 文章目录 前言1. DDL-操作数据库1.1 查询1.2 创建数据库1.3 删除数据库1.4 使用数据库2.1 数据类型2.2 查询表2.3 创建表2.4 删除表2.5 修改表 3. 实战案例详…

统计学的假设检验/置信区间计算

假设检验的核心其实就是反证法。反证法是数学中的一个概念&#xff0c;就是你要证明一个结论是正确的&#xff0c;那么先假设这个结论是错误的&#xff0c;然后以这个结论是错误的为前提条件进行推理&#xff0c;推理出来的结果与假设条件矛盾&#xff0c;这个时候就说明这个假…

Navicat 15获取用户的密码

我使用Navicat连接好了mysql但是密码忘记了&#xff1b;可以通过如下操作找回密码 我使用的Navicat版本是 15.0.27 1、选择文件 --> 导出连接 2、选择你要知道密码的连接  勾选导出密码&#xff08;默认位置是桌面&#xff09; 3、 在Password 这栏找到加密后的密码 …

基于Android应用开发实现UWB(超宽带)通信

什么是超宽带UWB 超宽带通信是一种无线电技术,专注于设备之间的精确测距(测量位置的精度为 10 厘米)。这种无线电技术可以使用低能量密度进行短距离测量,并在大部分无线电频谱上执行高带宽信号传输。UWB 的带宽大于 500 MHz(或超过 20% 的小数带宽)。 从 Android 13 开…

[GXYCTF2019]Ping Ping Ping解题过程

1、来看看靶场 发现就只有这个提示&#xff0c;尝试一下在url输入框进行测试 页面返回ping的结果&#xff0c;然后我之前也做过另外一道类似的题 链接&#xff1a;[ACTF2020 新生赛]Exec1命令注入_[actf2020 新生赛]exec 1_旺仔Sec的博客-CSDN博客 尝试用管道符 果然是可以的…

chatgpt赋能python:Python中语句太长之续行符的使用

Python中语句太长之续行符的使用 如果你是一位有10年Python编程经验的工程师&#xff0c;那么你一定会遭遇语句太长的问题。这是导致程序出错的常见问题。在很多情况下&#xff0c;一条语句的长度会超过Python规定的最大长度&#xff0c;这时候我们就需要使用续行符进行换行了…

C语言——数据在内存中的存储(上)

数据在内存中的存储 1. 数据类型的介绍 之前已经介绍过C语言中的基本数据类型了&#xff0c;主要有&#xff1a; char //字符数据类型short //短整型int //整形long //长整型long long //更长的整形float //单精度浮点数double //双精度浮点数 注意&#xff1a;C语言中是是没…

JVM学习笔记(中)

1、垃圾回收算法 标记清除法 特点&#xff1a; 速度较快会产生内存碎片 注意&#xff1a;这里的清除并不是真正意义上的清除&#xff0c;即每个字节都清0&#xff0c;而是记录一下被清除的对象的起始和结束的地址&#xff0c;当下一次分配给一个新对象时&#xff0c;新对象…

一文看懂数字化转型丨三叠云

相信很多朋友在最近这几年对“数字化”、“数字化转型”等名词听得很多了吧&#xff0c;去网上搜搜“数字化转型”&#xff0c;这几年各式各样的信息如火如荼&#xff0c;充斥着互联网平台和大大小小的企业。 一、什么是数字化转型&#xff1f; 那么“数字化”和“数字化转型”…

【ros/ros2】LCN及ros2节点的LCN改写

文章目录 序言1. ros2两种节点类型2. LCN是什么3. LCN状态转换4. LCN状态转换要做的事5. LCN节点功能划分6. ros2节点的LCN改写 序言 背景&#xff1a;ros2节点改写为lifecycle node节点 1. ros2两种节点类型 Node&#xff1a;和ros1中一样的节点基类LifecycleNode&#xff…

法规标准-ISO 11270标准解读(2014版)

ISO 11270是做什么的&#xff1f; ISO 11270全名为智能交通系统-车道保持辅助系统(LKAS)-性能要求及测试步骤&#xff0c;其中主要是对LKAS系统的功能要求、性能要求及测试步骤进行了介绍。 功能要求 LKAS应至少提供以下操作和状态转换&#xff1a; ——从LKAS off到LKAS on的…

chatgpt赋能python:Python中的精度问题

Python中的精度问题 如果你曾经在Python中处理浮点数&#xff0c;你可能会遇到精度问题。当使用不同的运算符和内置函数时&#xff0c;浮点数很容易产生舍入误差。这种误差可能会导致意想不到的结果&#xff0c;特别是在科学计算和金融应用中。 为什么会出现精度问题&#xf…

Spring AOP简介及相关案例

目录 一、Spring AOP简介 二、AOP相关术语 三、AOP入门案例 1. 引入依赖 2. 编写连接点 3. 编写通知类 4. 配置切面 5. 测试 四、通知类型 1. 编写通知方法 2. 配置切面 3. 测试 五、切点表达式 六、多切面配置 1. 编写发送邮件的通知 2. 配置切面 3. 测试 …

flex 布局的基本概念 - 详解

flex 布局的基本概念 Flexible Box 模型&#xff0c;通常被称为 flexbox&#xff0c;是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。本文给出了 flexbox 的主要特性&#xff0c;更多的细节将在别的文档中探索。我们说 flexbox 是一种一维的…

Metasploit超详细安装及使用教程(图文版)

通过本篇文章&#xff0c;我们将会学习以下内容&#xff1a; 1、在Windows上安装Metasploit 2、在Linux和MacOS上安装Metasploit 3、在Kali Linux中使用 Metasploit 4、升级Kali Linux 5、使用虚拟化软件构建渗透测试实验环境 6、配置SSH连接 7、使用SSH连接Kali 8、配…

C++ vector与map的结合运用

目录 vector和map的简单介绍&#xff1a; 今天我们用vector容器和map容器实现以下简单的功能&#xff1a; 案例描述&#xff1a; 图解: ​ 实现步骤&#xff1a; 代码实现&#xff1a; 运行结果&#xff1a; vector和map的简单介绍&#xff1a; map和vector都是C STL&…

C++11 -- 包装器

文章目录 function包装器function包装器的概念function的运用function实例化使用function解决逆波兰表达式 bind包装器bind包装器相关介绍bind绑定函数固定参数 function包装器 function包装器的概念 function包装器,也叫做适配器,它的本质是一个类模板. 例如: 1 template&l…

chatgpt赋能python:Python中的迭代器

Python中的迭代器 在Python中&#xff0c;迭代器是一种对象&#xff0c;它可以让我们可以遍历&#xff08;或迭代&#xff09;序列中的元素而不必了解它们如何存储在内存中。迭代器是Python中许多高级构造的基础 - 他们节省了空间&#xff0c;并且它们能够帮助我们更有效地处理…

Redis高级篇 - 多级缓存

多级缓存 1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后&#xff0c;先查询Redis&#xff0c;如果未命中则查询数据库&#xff0c;如图&#xff1a; 存在下面的问题&#xff1a; 请求要经过Tomcat处理&#xff0c;Tomcat的性能成为整个系统的瓶颈 Redis缓存失效时…