On Programming

A discussion of programming strategies and results

Runtime CPU Feature Checking

| Comments

Writing game engines is all about getting the most performance out of your machine. It is always on the bleeding edge pushing the limits of today’s hardware, but in the PC market we can’t forget about those who have older machines. This leaves two options: write for old machines and end up with a game looking like it was written half a decade ago or write for both old and new machines.

The choice is obviously the latter, but how do we detect what the end user’s CPU can handle? Enter CPUID. CPUID is an assembly instruction used to identify all the fun extensions on the processor like SSE and AES-NI. Luckily, we can embed assembly directly inside C and C++ so this information is actually easy to acquire. First we start with the function from wikipedia:

1
2
3
4
5
6
7
8
9
10
11
  void cpuid(unsigned info, unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx)
  {
    *eax = info;
    __asm volatile
      ("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
       "cpuid;"
       "mov %%ebx, %%esi;"
       "mov %%edi, %%ebx;"
       :"+a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx)
       : :"edi");
  }
Now all we need is some helper functions to make this much easier to read and use. Below is my helper function to detect if the user has the hardware accelerated AES instruction set:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  bool has_aes_ni()
  {
    unsigned int eax, ebx, ecx, edx;
    cpuid(1, &eax, &ebx, &ecx, &edx);
    return ((edx & 0x2000000) != 0);
  }

  int main()
  {
    if (has_aes_ni())
      printf("AES-NI Detected!\n");
    else
      printf("You do not have AES-NI support!\n");
    return 0;
  }
Naturally theres a lot of CPU extensions to look through and write helper functions for, but this should get you started. After you’ve detected the CPU features its a simple matter of redirecting functions calls to either the optimized or fallback functions. Happy coding!

Comments