Francis Cugler
Francis Cugler

Reputation: 7895

Invalid results querying my system’s cache information with GetLogicalProcessorInformation()

I wrote this small program to query and display information about my system's information on its cache.

#include <Windows.h>
#include <iostream>
#include <string>
#include <array>
#include <vector>

template<typename T>
auto msg = [](std::string_view label, T value, std::string descriptor = std::string()) {
    std::cout << label.data() << ": " << value << descriptor << '\n' ;
};

const static std::array<std::string_view, 4> CacheTypes{
    "Unified",
    "Instruction",
    "Data",
    "Trace"
};

void QueryCacheInformation() {
    DWORD bufferSize = 0;
    GetLogicalProcessorInformation(0, &bufferSize);
    std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(bufferSize / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
    GetLogicalProcessorInformation(buffer.data(), &bufferSize);

    auto getCacheType = [](_PROCESSOR_CACHE_TYPE type) {
        return std::string(CacheTypes[type]); 
    };
   
    auto showAll = [&](int i, std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> &buff) {
        msg<DWORD>(std::string_view("CPU L" + std::to_string(i) + " cache type"), static_cast<int>(buff[i].Cache.Type), std::string(" " + getCacheType(buff[i].Cache.Type)) );
        msg<DWORD>(std::string_view("CPU L" + std::to_string(i) + " cache size"), buff[i].Cache.Size, " bytes");
        msg<DWORD>(std::string_view("CPU L" + std::to_string(i) + " cache line size"), buff[i].Cache.LineSize, " bytes");
        std::cout << '\n';
    };

    for (auto& info : buffer) {
        switch (info.Cache.Level) {
        case 0:
            break;
        case 1:           
        case 2:
        case 3:
            showAll(info.Cache.Level, buffer);
            break;
        default:
            std::cout << "System has no cache!\n";
        }
    }
}

int main() {
    QueryCacheInformation();    
    system("pause");
    return 0;
}

And this is the output when I run this program:

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L2 cache type: 1 Instruction
CPU L2 cache size: 32768 bytes
CPU L2 cache line size: 64 bytes

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L1 cache type: 2 Data
CPU L1 cache size: 32768 bytes
CPU L1 cache line size: 64 bytes

CPU L2 cache type: 1 Instruction
CPU L2 cache size: 32768 bytes
CPU L2 cache line size: 64 bytes

Press any key to continue . . .

The program appears to be querying through processor nodes and retrieving information about its caches and displaying the results. However, I'm running this program on my Intel(R) Core(TM)2 Quad CPU Q9650. I'm running this on a Windows 7 64b and I'm compiling this using MS Visual Studio 2017 with the language flag set to ISO C++ Latest Draft Standard (/std:c++latest).

According to CPU-World's datasheet pertaining to my specific processor, here is what it is reporting about my system's architecture and its cache:

Cache Level Cache Properties
Level 1 cache size 4 x 32 KB 8-way set associative instruction caches
4 x 32 KB 8-way set associative data caches
Level 2 cache size 2 x 6 MB 24-way set associative caches (each L2 cache is shared between 2 cores)

Lower on the page, it has these CPU ID tables with more information about its cache:

TLB/Cache details:

  • 64-byte Prefetching
  • Data TLB: 4-KB Pages, 4-way set associative, 256 entries
  • Data TLB: 4-MB Pages, 4-way set associative, 32 entries
  • Instruction TLB: 2-MB pages, 4-way, 8 entries or 4M pages, 4-way, 4 entries
  • Instruction TLB: 4-KB Pages, 4-way set associative, 128 entries
  • L1 Data TLB: 4-KB pages, 4-way set associative, 16 entries
  • L1 Data TLB: 4-MB pages, 4-way set associative, 16 entries

Cache: L1 Data L1 Instruction L2
Size: 4 x 32 KB 4 x 32 KB 2 x 6 MB
Associativity: 8-way set associative 8-way set associative 24-way set associative
Line Size: 64 bytes 64 bytes 64 bytes
Comments: Direct-mapped Direct-mapped Non-inclusive Direct-Mapped 1 cache per 2 cores

According to the datasheets, this should be the block diagram of my CPU's architecture.

Q9650 Block Diagram

However, this does not match the printed results from my program. According to the saved data structures from GetLogicalProcessorInformation() it is claiming that my CPU has 8 L1 Data Caches and 2 L2 Instruction Caches all having the same exact size which is not the case. Now as for the line sizes they are all the same and this information appears to be correct. It's just that the "types" and some of the "sizes" are not. My CPU should have a total of 128KB of L1 Data Cache, 128KB of L1 Instruction Cache, and 12MB of L2 Cache. I'm not sure where I'm going wrong and why I'm not getting matching values and types...

Am I querying and extracting the information correctly? Is it within the for loop, the switch statement, or the lambda's I'm using? Or something else that I'm completely overlooking?

I'm new to this API so any and all help, tips, and suggestions will be useful.

Upvotes: 3

Views: 178

Answers (1)

1201ProgramAlarm
1201ProgramAlarm

Reputation: 32732

The problem is in your lambda and the data you pass to it. You're using i (the cache level) to access elements of buff when you should be passing in the info value from your loop instead.

Change the lambda and call site to be like this:

    auto showAll = [&](int i, SYSTEM_LOGICAL_PROCESSOR_INFORMATION &info) {
        msg<DWORD>(std::string_view("CPU L" + std::to_string(i) + " cache type"), static_cast<int>(info.Cache.Type), std::string(" " + getCacheType(info.Cache.Type)) );
        msg<DWORD>(std::string_view("CPU L" + std::to_string(i) + " cache size"), info.Cache.Size, " bytes");
        msg<DWORD>(std::string_view("CPU L" + std::to_string(i) + " cache line size"), info.Cache.LineSize, " bytes");
        std::cout << '\n';
    };
// ...
    showAll(info.Cache.Level, info);

(replacing buff[i] with info in the lambda, and passing in info instead of the buffer vector).

Upvotes: 4

Related Questions