• CPUID读取有关Cache的信息


       1: void cpuidTest()
       2: {
       3:     u32 val_eax, val_ebx, val_ecx, val_edx; 
       4:     asm("cpuid"
       5:             : "=a" (val_eax),
       6:               "=b" (val_ebx),
       7:               "=d" (val_ecx),
       8:               "=c" (val_edx)
       9:             : "a" (2));
      10:  
      11:     printk("eax: 0x%08X
    ", val_eax);
      12:     printk("ebx: 0x%08X
    ", val_ebx);
      13:     printk("ecx: 0x%08X
    ", val_ecx);
      14:     printk("edx: 0x%08X
    ", val_edx);
      15: }

    读出结果如下:

       1: [190894.986103] ###################################################################
       2: [190894.986109] eax: 0x76035A01
       3: [190894.986110] ebx: 0x00F0B0FF
       4: [190894.986111] ecx: 0x00CA0000
       5: [190894.986112] edx: 0x00000000
       6: [190894.986951] ###################################################################

    解析出有效的descriptor

       1: 76H: TLB Instruction TLB: 2M/4M pages, fully associative, 8 entries 
       2: 03H: TLB Data TLB: 4 KByte pages, 4-way set associative, 64 entries
       3: 5AH:TLB Data TLB0: 2-MByte or 4 MByte pages, 4-way set associative, 32 entries
       4: F0H:Prefetch 64-Byte prefetching
       5: B0H:TLB Instruction TLB: 4 KByte pages, 4-way set associative, 128 entries
       6: FFH: General CPUID leaf 2 does not report cache descriptor information, use CPUID leaf 4 to query cache parameters
       7: CAH: STLB Shared 2nd-Level TLB: 4 KByte pages, 4-way associative, 512 entries

    可以看到,

    General CPUID leaf 2 does not report cache descriptor information, use CPUID leaf 4 to query cache parameters

    没有返回Cache相关的信息,都是TLB信息。如果需要了解Cache的信息,需要使用4作为EAX的输入。

    我们重新组装代码,读取Cache相关的信息:

       1: void cpuidTest()
       2: {
       3:     u32 val_eax, val_ebx, val_ecx, val_edx; 
       4:     asm("cpuid"
       5:             : "=a" (val_eax),
       6:               "=b" (val_ebx),
       7:               "=d" (val_ecx),
       8:               "=c" (val_edx)
       9:             : "a" (4), "c"(1));
      10:  
      11:     u32 ways,partitions,line_Size, sets;
      12:  
      13:     ways = val_ebx >> 22;
      14:     partitions = (val_ebx >> 12) & 0x3FF;
      15:     line_Size = (val_ebx) & 0xFFF;
      16:     sets = val_ecx;
      17:  
      18:     printk("eax: 0x%08X
    ", val_eax);
      19:     printk("ebx: 0x%08X
    ", val_ebx);
      20:     printk("ecx: 0x%08X
    ", val_ecx);
      21:     printk("edx: 0x%08X
    ", val_edx);
      22:  
      23:     printk("ways: %d
    ", ways+1);
      24:     printk("partitions: %d
    ", partitions+1);
      25:     printk("line_size: %d
    ", line_Size+1);
      26:     printk("sets: %d
    ", sets+1);
      27:     printk("Cache L1 size: %d
    ", (ways + 1)*(partitions + 1)*(line_Size + 1)*(sets + 1));
      28: }

    结果如下:

       1: [193334.815202] ###################################################################
       2: [193334.815206] eax: 0x00000021
       3: [193334.815207] ebx: 0x01C0003F
       4: [193334.815208] ecx: 0x00000000
       5: [193334.815209] edx: 0x0000003F
       6: [193334.815209] ways: 8
       7: [193334.815210] partitions: 1
       8: [193334.815211] line_size: 64
       9: [193334.815211] sets: 1
      10: [193334.815212] Cache L1 size: 512
      11: [193334.815672] ###################################################################
    可见,L1的Cache是“全相关”,即只有一个cache set,其中有8路,即8个缓存行,每个缓存行里面包含的数据是64bytes,总共512bytes的缓存。

    Linux是怎么读取的呢?

       1: daniel@ubuntu:/mod/pslist$ cat /proc/cpuinfo
       2: processor    : 0
       3: vendor_id    : GenuineIntel
       4: cpu family    : 6
       5: model        : 42
       6: model name    : Intel(R) Core(TM) i5-2500 CPU @ 3.30GHz
       7: stepping    : 7
       8: cpu MHz        : 3269.310
       9: cache size    : 6144 KB
      10: fdiv_bug    : no
      11: hlt_bug        : no
      12: f00f_bug    : no
      13: coma_bug    : no
      14: fpu        : yes
      15: fpu_exception    : yes
      16: cpuid level    : 5
      17: wp        : yes
      18: flags        : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc up pni monitor ssse3 lahf_lm
      19: bogomips    : 6538.62
      20: clflush size    : 64
      21: cache_alignment    : 64
      22: address sizes    : 36 bits physical, 48 bits virtual
      23: power management:
       1: static int show_cpuinfo(struct seq_file *m, void *v)
       2: {
       3:     struct cpuinfo_x86 *c = v;
       4:     unsigned int cpu;
       5:     int i;
       6:  
       7: ******
       8: /* Cache size */
       9: if (c->x86_cache_size >= 0)
      10:     seq_printf(m, "cache size	: %d KB
    ", c->x86_cache_size);
      11: ******
      12: }
       1: unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
       2: {
       3:     /* Cache sizes */
       4:     unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0;
       5:     unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
       6:     unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
       7:     unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
       8: #ifdef CONFIG_X86_HT
       9:     unsigned int cpu = c->cpu_index;
      10: #endif
      11:  
      12:     if (c->cpuid_level > 3) {
      13:         static int is_initialized;
      14:  
      15:         if (is_initialized == 0) {
      16:             /* Init num_cache_leaves from boot CPU */
      17:             num_cache_leaves = find_num_cache_leaves();
      18:             is_initialized++;
      19:         }
      20:  
      21:         /*
      22:          * Whenever possible use cpuid(4), deterministic cache
      23:          * parameters cpuid leaf to find the cache details
      24:          */
      25:         for (i = 0; i < num_cache_leaves; i++) {
      26:             struct _cpuid4_info_regs this_leaf;
      27:             int retval;
      28:  
      29:             retval = cpuid4_cache_lookup_regs(i, &this_leaf);
      30:             if (retval >= 0) {
      31:                 switch (this_leaf.eax.split.level) {
      32:                 case 1:
      33:                     if (this_leaf.eax.split.type ==
      34:                             CACHE_TYPE_DATA)
      35:                         new_l1d = this_leaf.size/1024;
      36:                     else if (this_leaf.eax.split.type ==
      37:                             CACHE_TYPE_INST)
      38:                         new_l1i = this_leaf.size/1024;
      39:                     break;
      40:                 case 2:
      41:                     new_l2 = this_leaf.size/1024;
      42:                     num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
      43:                     index_msb = get_count_order(num_threads_sharing);
      44:                     l2_id = c->apicid >> index_msb;
      45:                     break;
      46:                 case 3:
      47:                     new_l3 = this_leaf.size/1024;
      48:                     num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
      49:                     index_msb = get_count_order(
      50:                             num_threads_sharing);
      51:                     l3_id = c->apicid >> index_msb;
      52:                     break;
      53:                 default:
      54:                     break;
      55:                 }
      56:             }
      57:         }
      58:     }
      59:     /*
      60:      * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
      61:      * trace cache
      62:      */
      63:     if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
      64:         /* supports eax=2  call */
      65:         int j, n;
      66:         unsigned int regs[4];
      67:         unsigned char *dp = (unsigned char *)regs;
      68:         int only_trace = 0;
      69:  
      70:         if (num_cache_leaves != 0 && c->x86 == 15)
      71:             only_trace = 1;
      72:  
      73:         /* Number of times to iterate */
      74:         n = cpuid_eax(2) & 0xFF;
      75:  
      76:         for (i = 0 ; i < n ; i++) {
      77:             cpuid(2, &regs[0], &regs[1], &regs[2], &regs[3]);
      78:  
      79:             /* If bit 31 is set, this is an unknown format */
      80:             for (j = 0 ; j < 3 ; j++)
      81:                 if (regs[j] & (1 << 31))
      82:                     regs[j] = 0;
      83:  
      84:             /* Byte 0 is level count, not a descriptor */
      85:             for (j = 1 ; j < 16 ; j++) {
      86:                 unsigned char des = dp[j];
      87:                 unsigned char k = 0;
      88:  
      89:                 /* look up this descriptor in the table */
      90:                 while (cache_table[k].descriptor != 0) {
      91:                     if (cache_table[k].descriptor == des) {
      92:                         if (only_trace && cache_table[k].cache_type != LVL_TRACE)
      93:                             break;
      94:                         switch (cache_table[k].cache_type) {
      95:                         case LVL_1_INST:
      96:                             l1i += cache_table[k].size;
      97:                             break;
      98:                         case LVL_1_DATA:
      99:                             l1d += cache_table[k].size;
     100:                             break;
     101:                         case LVL_2:
     102:                             l2 += cache_table[k].size;
     103:                             break;
     104:                         case LVL_3:
     105:                             l3 += cache_table[k].size;
     106:                             break;
     107:                         case LVL_TRACE:
     108:                             trace += cache_table[k].size;
     109:                             break;
     110:                         }
     111:  
     112:                         break;
     113:                     }
     114:  
     115:                     k++;
     116:                 }
     117:             }
     118:         }
     119:     }
     120:  
     121:     if (new_l1d)
     122:         l1d = new_l1d;
     123:  
     124:     if (new_l1i)
     125:         l1i = new_l1i;
     126:  
     127:     if (new_l2) {
     128:         l2 = new_l2;
     129: #ifdef CONFIG_X86_HT
     130:         per_cpu(cpu_llc_id, cpu) = l2_id;
     131: #endif
     132:     }
     133:  
     134:     if (new_l3) {
     135:         l3 = new_l3;
     136: #ifdef CONFIG_X86_HT
     137:         per_cpu(cpu_llc_id, cpu) = l3_id;
     138: #endif
     139:     }
     140:  
     141:     c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d));
     142:  
     143:     return l2;
     144: }

    c->x86_cache_size = l3 ? l3 : (l2 ? l2 : (l1i+l1d));

       1: static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
       2:                 unsigned int *ecx, unsigned int *edx)
       3: {
       4:     /* ecx is often an input as well as an output. */
       5:     asm volatile("cpuid"
       6:         : "=a" (*eax),
       7:           "=b" (*ebx),
       8:           "=c" (*ecx),
       9:           "=d" (*edx)
      10:         : "0" (*eax), "2" (*ecx));
      11: }

    为什么x86_cache_size是6144KB,而我们得到的L1缓存为512Bytes,L2也是512Bytes,L3是1536Bytes呢?

    上面的程序有个错误,改过来结果

    asm("cpuid"
            : "=a" (val_eax),
              "=b" (val_ebx),
              "=d" (val_ecx),
              "=c" (val_edx) //c和d写反了,不配对
            : "a" (4), "c"(1));

       1: [261214.698170] ###################################################################
       2: [261214.698174] eax: 0x00000041
       3: [261214.698175] ebx: 0x05C0003F
       4: [261214.698176] ecx: 0x00000FFF
       5: [261214.698177] edx: 0x00000000
       6: [261214.698178] ways: 24
       7: [261214.698178] partitions: 1
       8: [261214.698179] line_size: 64
       9: [261214.698180] sets: 4096
      10: [261214.698181] Cache L3 size: 6144 KB


    Intel与缓存有关的总线技术:

    Strong Uncacheable:

    所有的读和写操作,都按照编码时设定的严格顺序出现在系统总线(System Bus)上,不会发生乱序。

    所有可能的硬件优化都被禁止,比如对可能的内存访问进行预测(speculative memory accesses),pagetable walks, prefectches of speculated branch targets.等等。

    当系统的IO被映射到物理内存空间时,这种模式是有用的,可以保证对IO设备的操作严格,不会引起歧义。

    但是如果用来操作RAM内存,会极大降低性能。

    Uncacheable:

    和Strong Uncacheable是类似,区别在于:

    Uncacheable可以通过对MTRR寄存器进行编程来将该区域类型修改为WC类型,但是Strong Uncacheable区域不可以被修改。

    MTRR的意思是“内存类型及范围寄存器(Memory Type & Range Register)”。

    Write Combining(WC):

    读写不被缓存在缓存中,但是写被缓存在WC Buffer中。而且不强制要求一致性(coherency)。

    对内存的写可能被delay并且combine在WC buffer中,以减少对内存的访问次数。

    直到一些特定的事件发生时,才被写回到内存中。

    主要用于一些对写内存的顺序不感冒的场合,比如video frame buffer等等。

    Write Through(WT):写透

    读被缓存在Cache中。

    写操作会直接写到内存中。

    如果缓存命中,则更新缓存,或者使用缓存失效(Invalidate)。

    这种方式本身可以保证一致性(Coherency)。

    Write Back(WB):

    读写都被缓存在Cache中。

    写操作会被积累在缓存中,直到有人触发了write-back操作,才被写回到内存中。

    或者当缓存行选中让位时,需要先将缓存行中的内容回写到内存中。

    Write Protected(WP):

    读缓存在Cache中。

    写操作首先传到系统总线上,即写到内存中。

    然后让所有的processor的缓存中,与写的内存相关的缓存行全部失效。


    DMA操作时对缓存的影响与依赖

    通过DMA模式,从外部设备读入了一段数据到内存时,需要将该段内存对应的高速缓存行全部清空。注意,DMA操作是外设与内存之间的直接交换数据,不会经过CPU,因此也不会经过缓存。但是DMA操作之后,内存与缓存之间可能出现不一致现象,因此需要使相应的缓存失效。

    同样,如果需要进行DMA操作,将内存中的一段数据写出到外部设备上时,也需要先将缓存中的内容回写到内存中,再从内存中回写到外部设备上。


    广义上的缓存,大致有三种类型:

    Cache:

    这是狭义上的缓存,包含数据缓存和指令缓存,通常L1缓存分为数据和指令缓存两种,而L2和L3都是Unified Cache。

    指令缓存,CPU基本上已经很好的支持和优化了,比如分支预测等等。

    TLB: Translation Look-aside Buffers,快表

    为了回忆分页机制的页面映射过程,会将页目录以及页表中的一部分先缓存在CPU内部的Buffer中,这个Buffer就是TLB。

    这是专门用于分页机制的缓存。

    Write Buffer

    就是上文提到的WC Buffer。

    CPU对内存进行写操作时,如果当前系统总线已经被锁住,此时可以将内容先写到一个缓存中,狭义上的Cache可以充当这个角色,但是如果对于Write Combining,并没有利用到狭义Cache时,就提供了WC Buffer供CPU来作写缓冲。


    可以通过两种方式,控制某段内存区域适用的缓存方式:

    1. 通过页表(PAT)中项目的字段,可以以页为粒度,对该页适合的缓存方式进行指定;

    2. 通过MTRR进行设置,可以设置任意粒度的内存区域适用的缓存方式。


    Order在缓存中和内存中的反应

    处理器执行一段程序时,缓存接收到的改变和内存接收到的改变并不完全相同,体现出来的指令的序列也不相同。

    指令序,Program Ordering, 指的就是缓存接收到的顺序,与执行的程序中的顺序是一样的;

    处理器序,Processor Ordering,指的是出现在系统总线上的顺序,可以与指令序不同。

    如果二者相同,称为“强序”, 否则,称为“弱序”

  • 相关阅读:
    联赛模拟测试19
    联考Day5
    联赛模拟测试18(A.施工未补)
    题解 CF960G 【Bandit Blues】
    题解 P5518 【[MtOI2019]幽灵乐团 / 莫比乌斯反演基础练习题】
    概率与数学期望笔记
    题解 P3704 【[SDOI2017]数字表格】
    主定理
    【题解】Hikari与组合数
    【题解】P2303 [SDOI2012] Longge 的问题
  • 原文地址:https://www.cnblogs.com/long123king/p/3522717.html
Copyright © 2020-2023  润新知