How to check which technologies the CPU supports at runtime?

5

I'm writing a PC program that has optimized function calls for SSE2 , SSE3 and maybe SSE4 . However, I can not guarantee that the PC that will run the program supports these technologies and would like to check the support so that I can choose the optimized version or even prevent the program from running.

The functions for verification would look like this:

namespace CPU
{
    /**
     * @return Retorna true se a CPU suporta SSE4.1.
     **/
    bool IsSSE41();

    bool IsSSE3();
    // ...
}

I saw that for Windows there is a function that can be used to check up to SSE3 in this link , in addition to cpuid . However, I'm looking for a more portable solution. What do you suggest?

    
asked by anonymous 17.03.2014 / 01:07

3 answers

2

Ideally, you should use CPUID as you suggested. It is an x86 architecture instruction that will return everything that the current CPU supports. To use this you need to use _cpuid() in Windows or directly assembly on other systems. A portable class I found here (by jcoffland ):

#pragma once

#ifdef _WIN32
#include <limits.h>
typedef unsigned __int32  uint32_t;
#else
#include <stdint.h>
#endif

class CPUID {
  uint32_t regs[4];

public:
  void CPUID(unsigned i) {
#ifdef _WIN32
    __cpuid((int *)regs, (int)i);
#else
    asm volatile
      ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
       : "a" (i), "c" (0));
#endif
  }

  const uint32_t& EAX() const {return regs[0];}
  const uint32_t& EBX() const {return regs[1];}
  const uint32_t& ECX() const {return regs[2];}
  const uint32_t& EDX() const {return regs[3];}
};

Now just use it according to the Wikipedia reference . For example:

bool hasSSE41() {
    CPUID cpuid(1);
    return (cpuid.ECX() >> 19) & 1;
}
    
17.03.2014 / 14:53
4

Just to add @ Guilherme Bernal's answer and share the code. I did a program to test the class that Guilherme posted and it looks like it is working. I have not tested fully since this varies from PC to PC. (I compiled it in MSVC 2013, but it should work in GCC too.)

I tested on a Core i5 and the result seems correct (compared to CPUz).

It looks like this:

#ifndef HARDWARE_HPP_INCLUDED
#define HARDWARE_HPP_INCLUDED

#include <string>

#ifdef _WIN32
#include <limits.h>
#include <intrin.h>
typedef unsigned __int32  uint32_t;
#else
#include <stdint.h>
#endif

namespace hardware
{
    enum TechEAX
    {
        kFSGSBASE = (1 << 0),
        kBMI = (1 << 3),
        kHLE = (1 << 4),
        kAVX2 = (1 << 5),
        kBMI2 = (1 << 8),
        kRTM    = (1 << 11),
        kRDSEED = (1 << 18),
        kADX = (1 << 19),
    };

    enum TechECX
    {
        kSSE3 = (1 << 0),
        kPCLMUL = (1 << 1),
        kLZCNT = (1 << 5),
        kSSSE3 = (1 << 9),
        kFMA = (1 << 12),
        kCMPXCHG16B = (1 << 13),
        kSSE4_1 = (1 << 19),
        kSSE4_2 = (1 << 20),
        kMOVBE = (1 << 22),
        kPOPCNT = (1 << 23),
        kAES = (1 << 25),
        kXSAVE = (1 << 26),
        kOSXSAVE = (1 << 27),
        kAVX = (1 << 28),
        kF16C = (1 << 29),
        kRDRND = (1 << 30),

        kLAHF_LM = (1 << 0),
        kABM = (1 << 5),
        kSSE4a = (1 << 6),
        kPRFCHW = (1 << 8),
        kXOP = (1 << 11),
        kLWP = (1 << 15),
        kFMA4 = (1 << 16),
        kTBM = (1 << 21)
    };

    enum TechEDX
    {
        kCMPXCHG8B = (1 << 8),
        kCMOV = (1 << 15),
        kMMX = (1 << 23),
        kFXSAVE = (1 << 24),
        kSSE = (1 << 25),
        kSSE2 = (1 << 26),

        kMMXEXT = (1 << 22),
        kLM = (1 << 29),
        k3DNOWP = (1 << 30),
        k3DNOW = (1 << 31)
    };

    class CPU
    {
        public:
            CPU();

            std::string getVendor() const;

            bool checkTechnology(TechEAX tech) const;
            bool checkTechnology(TechECX tech) const;
            bool checkTechnology(TechEDX tech) const;

        private:
            void load(unsigned value) const;

            const uint32_t& EAX() const
            {
                return regs[0];
            }

            const uint32_t& EBX() const
            {
                return regs[1];
            }

            const uint32_t& ECX() const
            {
                return regs[2];
            }

            const uint32_t& EDX() const
            {
                return regs[3];
            }

        private:
            uint32_t regs[4];
    };

}


#endif
#include "hardware.hpp"

namespace hardware
{
    CPU::CPU()
    {
        this->load(0);
    }

    bool CPU::checkTechnology(TechEAX tech) const
    {
        this->load(7);
        return (EAX() & tech) != 0;
    }

    bool CPU::checkTechnology(TechECX tech) const
    {
        this->load(1);
        return (ECX() & tech) != 0;
    }

    bool CPU::checkTechnology(TechEDX tech) const
    {
        this->load(1);
        return (EDX() & tech) != 0;
    }

    std::string CPU::getVendor() const
    {
        std::string vendor;
        vendor += std::string((const char *)&EBX(), 4);
        vendor += std::string((const char *)&EDX(), 4);
        vendor += std::string((const char *)&ECX(), 4);

        if (vendor == "AMDisbetter!" || vendor == "AuthenticAMD") return "AMD";
        if (vendor == "GenuineIntel") return "Intel";
        if (vendor == "VIA VIA VIA ") return "VIA";
        if (vendor == "CentaurHauls") return "Centaur";
        if (vendor == "CyrixInstead") return "Cyrix";
        if (vendor == "TransmetaCPU" || vendor == "GenuineTMx86") return "Transmeta";
        if (vendor == "Geode by NSC") return "National Semiconductor";
        if (vendor == "NexGenDriven") return "NexGen";
        if (vendor == "RiseRiseRise") return "Rise";
        if (vendor == "SiS SiS SiS ") return "SiS";
        if (vendor == "UMC UMC UMC ") return "UMC";
        if (vendor == "Vortex86 SoC") return "Vortex";
        if (vendor == "KVMKVMKVMKVM") return "KVM";
        if (vendor == "Microsoft Hv") return "Microsoft Hyper-V";
        if (vendor == "VMwareVMware") return "VMware";
        if (vendor == "XenVMMXenVMM") return "Xen HVM";

        return vendor;
    }

    void CPU::load(unsigned value) const
    {
#ifdef _WIN32
        __cpuid((int *) regs, (int)value);

#else
        asm volatile
            ("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
            : "a" (i), "c" (0));
        // ECX is set to zero for CPUID function 4
#endif
    }
}
#include <iostream>
#include "hardware.hpp"

int main(int argc, char** argv)
{
    using namespace hardware;

    CPU cpu;

    std::cout << "Fabricante: " << cpu.getVendor() << std::endl;
    std::cout << "       SSE: " << cpu.checkTechnology(kSSE) << std::endl;
    std::cout << "      SSE2: " << cpu.checkTechnology(kSSE2) << std::endl;
    std::cout << "      SSE3: " << cpu.checkTechnology(kSSE3) << std::endl;
    std::cout << "     SSSE3: " << cpu.checkTechnology(kSSSE3) << std::endl;
    std::cout << "    SSE4.1: " << cpu.checkTechnology(kSSE4_1) << std::endl;
    std::cout << "    SSE4.2: " << cpu.checkTechnology(kSSE4_2) << std::endl;
    std::cout << "     SSE4A: " << cpu.checkTechnology(kSSE4a) << std::endl;
    std::cout << "       AES: " << cpu.checkTechnology(kAES) << std::endl;
    std::cout << "       AVX: "  << cpu.checkTechnology(kAVX) << std::endl;
    std::cout << "      AVX2: " << cpu.checkTechnology(kAVX2) << std::endl;



    std::cin.get();

    return 0;
}

The output stayed (for the Intel Core i5 Sandy Bridge ):

Fabricante: Intel
       SSE: 1
      SSE2: 1
      SSE3: 1
     SSSE3: 1
    SSE4.1: 1
    SSE4.2: 1
     SSE4A: 1
       AES: 1
       AVX: 1
      AVX2: 0
    
17.03.2014 / 22:45
3

The Yeppp! library seems to have what you're looking for [ 1 , [

17.03.2014 / 07:49