在Intel处理器上获取L3缓存信息时遇到了问题。在AMD上获得L3线路长度很简单,如下所示:
mov eax, 0x80000006
cpuid
shl edx, 24
shr edx, 24
对Intels的相同操作要复杂得多。 我知道这可以使用这个序列来完成:
mov eax, 2
cpuid
并通过本手册调整寄存器值:http://www.microbe.cz/docs/CPUID.pdf(第26页,"表2-7。描述符解码值")。
但我的程序没有找到任何枚举的描述符,并为缓存大小和行长度返回0。
是否有更简单和/或更充分的方法来获取英特尔的缓存大小和行长度?
这是完整的代码。将所有cpuid输出(eax,ebx,ecx,edx)压入堆栈,然后将每个值与硬编码描述符列表进行比较。比较是在低8位进行的,然后这些位缩小。
__declspec(dllexport) __declspec(naked) void GetMetricLevel2(int &length) {
__asm {
// check CPUID availability
pushfd
pop eax
mov ebx, eax
xor eax, 00200000h
push eax
popfd
pushfd
pop eax
cmp eax, ebx
jnz HAS_CPUID
mov edx, -1 // return -1 by reference
jmp RET_ARG
HAS_CPUID:
mov eax, 2 // L3 Intel, incomplete
mov ecx, 0
cpuid
push ecx
or ecx, eax
or ecx, ebx
or ecx, edx
cmp ecx, 0
pop ecx // experimental
je CPU_AMD // if all registers are 0, we try AMD scheme
CPU_INTEL:
push ebp
mov ebp, 0
push 0
push eax // store counter
jmp CALL_BEGIN
CYCLE_BEGIN:
pop ecx
inc ecx
push ecx
push eax
mov eax, 2
cpuid
CALL_BEGIN:
push eax
push ebx
push ecx
push edx
mov ch, 4
PARSE_REG:
pop edx
mov cl, 4
PARSE_DESCR:
DD0H://512,4w
cmp dl, 0xD0
jne DD1H
add ebp, 512d
jmp MISS_L3CACHE
DD1H://1024,4w
cmp dl, 0xD1
jne DD2H
add ebp, 1024d
jmp MISS_L3CACHE
DD2H://2048,4w
cmp dl, 0xD2
jne DD6H
add ebp, 2048d
jmp MISS_L3CACHE
DD6H://1024,8w
cmp dl, 0xD6
jne DD7H
add ebp, 1024d
jmp MISS_L3CACHE
DD7H://2048,8w
cmp dl, 0xD7
jne DD8H
add ebp, 2048d
jmp MISS_L3CACHE
DD8H://4096,8w
cmp dl, 0xD8
jne DDCH
add ebp, 4096d
jmp MISS_L3CACHE
DDCH://1536,12w
cmp dl, 0xDC
jne DDDH
add ebp, 1536d
jmp MISS_L3CACHE
DDDH://3072,12w
cmp dl, 0xDD
jne DDEH
add ebp, 3072d
jmp MISS_L3CACHE
DDEH://6144,12w
cmp dl, 0xDE
jne DE2H
add ebp, 6144d
jmp MISS_L3CACHE
DE2H://2048,16w
cmp dl, 0xE2
jne DE3H
add ebp, 2048d
jmp MISS_L3CACHE
DE3H://4096,16w
cmp dl, 0xE3
jne DE4H
add ebp, 4096d
jmp MISS_L3CACHE
DE4H://8192,16w
cmp dl, 0xE4
jne DEAH
add ebp, 8192d
jmp MISS_L3CACHE
DEAH://12mb,24w
cmp dl, 0xEA
jne DEBH
add ebp, 12288d
jmp MISS_L3CACHE
DEBH://18mb,24w
cmp dl, 0xEB
jne DECH
add ebp, 18432d
jmp MISS_L3CACHE
DECH://24mb,24w
cmp dl, 0xEC
jne MISS_L3CACHE
add ebp, 24576d
MISS_L3CACHE:
dec cl
cmp cl, 0
shr edx, 8 // it's 8-bit descriptor
jne PARSE_DESCR
dec ch
cmp ch, 0
jne PARSE_REG
CALL_FINISH:
pop eax
cmp al, 0
je CYCLE_FINISH // replace to je then
dec al
jmp CYCLE_BEGIN
CYCLE_FINISH:
mov edx, ebp
shl edx, 8 // 8 bits for cache string length
mov dl, 64d // Intel always has 64 byte L3 string
add esp, 4
pop ebp
jmp RET_ARG
CPU_AMD:
mov eax, 0x80000006 // L3 AMD
cpuid
shl edx, 24
shr edx, 24
RET_ARG:
mov eax, [esp+4] // first argument lies here
mov [eax], edx // return by reference
ret
}
}
答案 0 :(得分:4)
您的代码存在许多问题。您应该使用__cpuid
编译器内在函数并在C ++中完全编写它。它使代码更容易编写和维护。
您的代码存在两个主要问题。首先,您没有正确使用CPUID功能2。使用此功能时,将忽略ECX中的值。第二个是当函数2返回0FFh
描述符时,你没有使用CPUID函数4来确定缓存大小。
您的代码的其他问题包括:
shr edx, 8
设置了标志,因此实际使用了内部循环字节计数器。循环仍然有效,因为当EDX变为0时,它不包含任何可能的L3描述符。您的部分问题是您使用的是过时的手册。您应该使用最新的Intel Software Developers Manual。
它没有经过很好的测试,它可能在缓存描述符switch语句中有一些转录错误,但这里是一个使用CPUID函数2和4来确定大小,关联性的C实现并缓存L3缓存的行大小:
#include <intrin.h>
int
get_intel_l3_info(unsigned *size, unsigned *assoc, unsigned *linesize) {
int regs[4];
int i;
__cpuid(regs, 0); /* Maximum Input Value */
int max_leaf = regs[0];
if (max_leaf < 2) {
return -1; /* no way to find L3 cache info */
}
__cpuid(regs, 1); /* Additional Information */
int family = (regs[0] >> 8) & 0xF;
int model = (regs[0] >> 4) & 0xF;
__cpuid(regs, 2); /* Cache and TLB Information */
regs[0] &= 0xFFFFFF00; /* least significant byte of EAX is invalid */
for (i = 0; i < 4; i++) {
if (regs[i] < 0) { /* invalid if most significant bit set */
regs[i] = 0;
}
}
unsigned char *descriptors = (unsigned char *) regs;
const int kb = 1024;
const int mb = 1024 * kb;
#define RETINFO(s, a, l) *size = (s); *assoc = (a); *linesize = (l); return 0
int use_leaf_4 = 0;
for (i = 0; i < 32; i++) {
switch(descriptors[i]) {
case 0x22: RETINFO(512 * kb, 4, 64);
case 0x23: RETINFO(1 * mb, 8, 64);
case 0x25: RETINFO(2 * mb, 8, 64);
case 0x29: RETINFO(4 * mb, 8, 64);
case 0x40: RETINFO(0, 0, 0); /* no L3 cache */
case 0x46: RETINFO(4 * mb, 4, 64);
case 0x47: RETINFO(8 * mb, 8, 64);
case 0x49:
if (family == 0x0F && model == 0x06) {
RETINFO(4 * mb, 16, 64);
}
break;
case 0x4A: RETINFO(6 * mb, 12, 64);
case 0x4B: RETINFO(8 * mb, 16, 64);
case 0x4C: RETINFO(12 * mb, 12, 64);
case 0x4D: RETINFO(16 * mb, 16, 64);
case 0xD0: RETINFO(512 * kb, 4, 64);
case 0xD1: RETINFO(1 * mb, 4, 64);
case 0xD6: RETINFO(1 * mb, 8, 64);
case 0xD7: RETINFO(2 * mb, 8, 64);
case 0xD8: RETINFO(4 * mb, 8, 64);
case 0xDC: RETINFO(1 * mb + 512 * kb, 12, 64);
case 0xDD: RETINFO(3 * mb, 12, 64);
case 0xDE: RETINFO(6 * mb, 12, 64);
case 0xE2: RETINFO(2 * mb, 16, 64);
case 0xE3: RETINFO(4 * mb, 16, 64);
case 0xE4: RETINFO(8 * mb, 16, 64);
case 0xEA: RETINFO(12 * mb, 24, 64);
case 0xEB: RETINFO(18 * mb, 24, 64);
case 0xEC: RETINFO(24 * mb, 24, 64);
case 0xFF:
use_leaf_4 = 1;
break;
}
}
if (!use_leaf_4 || max_leaf < 4) {
return -1; /* failed, no L3 info found */
}
i = 0;
while(1) {
__cpuidex(regs, 4, i); /* Deterministic Cache Parameters */
if ((regs[0] & 0x1F) == 0) {
return RETINFO(0, 0, 0); /* no L3 cache */
}
if (((regs[0] >> 5) & 0x7) == 3) {
int lsize = (regs[1] & 0xFFF) + 1;
int partitions = ((regs[1] >> 12) & 0x3FF) + 1;
int ways = ((regs[1] >> 22) & 0x3FF) + 1;
int sets = regs[2] + 1;
RETINFO(ways * partitions * lsize * sets,
ways, lsize);
}
i++;
}
}