I have a whole load of unfinished hobby projects. Many times I was taken by some idea, implemented the most challenging part and lost my interest.
But some of the projects have been actually finished and may be interesting to someone.
It’s time to let the world see my creations π
The hero of the day is the small cross-platform utility that measures the clock speed of the CPU and shows it alongside its summary information, such as brand name, family, stepping, etc.
It started as a command-line application made in Borland C++ Builder. This IDE provides a straightforward and convenient syntax for embedding the x86 Assembler instructions into your regular C++ code. Which is great, when your application built around one of them: rdtsc.
Here’s the code (please take into account, that I was not professional or even amateur programmer back then, so give me some slack):
#include <stdio.h> #include <conio.h> #include <windows.h> __int64 Get_freq() { DWORD ts_hi, ts_lo; LARGE_INTEGER qpf, count_1, count_2; ULARGE_INTEGER ts_before, ts_after; QueryPerformanceFrequency(&qpf); __int64 count_persec = qpf.QuadPart; asm { rdtsc push eax push edx } QueryPerformanceCounter(&count_1); while (count_2.QuadPart - count_1.QuadPart < count_persec) { QueryPerformanceCounter(&count_2); } asm { rdtsc push eax push edx } asm { pop ts_hi pop ts_lo } ts_after.HighPart = ts_hi; ts_after.LowPart = ts_lo; asm { pop ts_hi pop ts_lo } ts_before.HighPart = ts_hi; ts_before.LowPart = ts_lo; return ts_after.QuadPart - ts_before.QuadPart; }; void main() { char ch; printf("Press \"q\" for exit or any other for detecting CPU clock frequency again\n\n" ); ch = getch(); while (ch != 'q'){ printf("Frequency = %u Hz\n", Get_freq()); ch = getch(); } }
There was not so much information on this topic available except the MSDN library and Intel’s Instruction Set Reference. But Borland’s documentation was good enough too, as far as I remember.
In a few months, I ported this to the MS Visual C++. As you can see, the ASM instructions were replaced with the MSVC’s intrinsic functions. The clock frequency alone is not so exciting, so I added some basic processor information, that is returned by the cpuid instruction:
#include <windows.h> #include <intrin.h> #include <stdio.h> int i; int table[4]; unsigned __int64 nFreq; void GetFreq() { unsigned __int64 ts_before, ts_after; LARGE_INTEGER count_persec, count_1, count_2; QueryPerformanceFrequency(&count_persec); ts_before = __rdtsc(); QueryPerformanceCounter(&count_1); while (count_2.QuadPart - count_1.QuadPart < count_persec.QuadPart) QueryPerformanceCounter(&count_2); ts_after = __rdtsc(); nFreq = ts_after - ts_before; }; int main() { char c[3][16] = {0}; for (i = 2; i <= 4; i++) { __cpuid(table, 0x80000000 | i); memcpy(&c[i - 2], &table, 16); } __cpuid(table, 1); int nStepping = table[0] & 0xF; int nModel = (table[0] & 0xF0) >> 4; int nFamily = (table[0] & 0xF00) >> 8; GetFreq(); printf("Frequency: %u\n", nFreq); printf("Brand: %s\n", c); printf("Family: %x\t", nFamily); printf("Model: %x\t", nModel); printf("Stepping: %x\t", nStepping); getchar(); }
But this was just a preparation for the next step.
The final version of 2006 – GUI application made using the Windows 32 API aka WinApi:
// ΠΠΊΠ»ΡΡΠ°Π΅ΠΌ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΡΠΉ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΡΠ½ΡΠΉ ΡΠ°ΠΉΠ» Π΄Π»Ρ Windows-ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌ #include <windows.h> #include <intrin.h> #include <string> #include <stdlib.h> int i; int table[4]; char buff[16]; // CPU Information std::string sBrand; int nFamily; int nModel; int nStepping; unsigned __int64 nFreq; void GetFreq(); //---------------- void OnCreate(HWND, HINSTANCE); WNDCLASS CreateClass(WNDPROC pfWProc, HINSTANCE hInst, LPCSTR sIcon, LPCSTR sCursor, LPCSTR sName, HBRUSH hbColor = (HBRUSH)12) { WNDCLASS wc = {CS_HREDRAW | CS_VREDRAW, pfWProc, NULL, NULL, hInst, LoadIcon(hInst, sIcon), LoadCursor(NULL, sCursor), hbColor, NULL, sName}; return wc; } // ΠΠ±ΡΡΠ²Π»Π΅Π½ΠΈΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ ΠΎΠΊΠ½Π° (ΠΎΠΊΠΎΠ½Π½ΠΎΠΉ ΠΏΡΠΎΡΠ΅Π΄ΡΡΡ) LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); HINSTANCE hInst; // ΠΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ HWND Tab; // Π’ΠΎΡΠΊΠ° Π²Ρ ΠΎΠ΄Π° Π² ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ - ΡΡΠ½ΠΊΡΠΈΡ WinMain int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; // Π£Π½ΠΈΠΊΠ°Π»ΡΠ½ΡΠΉ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΠΎΠΊΠ½Π° (handle) MSG msg; // ΠΠ±ΡΡΠ²Π»Π΅Π½ΠΈΠ΅ ΡΡΡΡΠΊΡΡΡΡ ΡΠΈΠΏΠ° MSG, Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡΠΌΠΈ hInst = hInstance; // Π‘ΠΎΡ ΡΠ°Π½ΡΠ΅ΠΌ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ RegisterClass(&CreateClass(WndProc, hInst, MAKEINTRESOURCE(102), IDC_ARROW, "Form")); // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΈ ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΠΌ ΠΎΠΊΠΎΠ½Π½ΡΠΉ ΠΊΠ»Π°ΡΡ RECT WorkArea; SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkArea, 0); GetFreq(); // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΠΎΠΊΠ½ΠΎ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ hWnd = CreateWindow( "Form", // ΠΠΌΡ ΠΊΠ»Π°ΡΡΠ° ΠΎΠΊΠ½Π° "OverSoft CPU Informer XP 0.001", // ΠΠ°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΠΎΠΊΠ½Π° WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, // Π‘ΡΠΈΠ»Ρ ΠΎΠΊΠ½Π° WorkArea.right/2 - 215, WorkArea.bottom/2 - 75, // ΠΠΎΡΠΈΠ·ΠΎΠ½ΡΠ°Π»ΡΠ½Π°Ρ ΠΈ Π²Π΅ΡΡΠΈΠΊΠ°Π»ΡΠ½Π°Ρ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ ΠΎΠΊΠ½Π° 430, 150, // Π¨ΠΈΡΠΈΠ½Π° ΠΈ Π²ΡΡΠΎΡΠ° ΠΎΠΊΠ½Π° NULL, // Π₯Π΅Π½Π΄Π» ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΎΠ³ΠΎ ΠΎΠΊΠ½Π° NULL, // Π₯Π΅Π½Π΄Π» ΠΌΠ΅Π½Ρ hInst, // ΠΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ NULL); // ΠΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΎΠΊΠ½Π° UpdateWindow(hWnd); // ΠΠ΅ΡΠ΅ΡΠΈΡΠΎΠ²ΡΠ²Π°Π΅ΠΌ ΠΎΠΊΠ½ΠΎ // Π‘ΡΠ°Π½Π΄Π°ΡΡΠ½ΡΠΉ ΡΠΈΠΊΠ» ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } // ΠΠΊΠΎΠ½Π½Π°Ρ ΠΏΡΠΎΡΠ΅Π΄ΡΡΠ° LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CREATE: OnCreate(hWnd, hInst); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } } void GetBrandStr(); void GetId1(); void OnCreate(HWND hWnd, HINSTANCE hInst) { GetBrandStr(); GetId1(); //GetFreq(); CreateWindow("static", "Brand:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 20, 80, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", sBrand.c_str(), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 20, 300, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", "Family:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 40, 80, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", CharUpper(_itoa(nFamily, buff, 16)), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 40, 350, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", "Model:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 60, 80, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", CharUpper(_itoa(nModel, buff, 16)), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 60, 350, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", "Stepping:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 80, 80, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", CharUpper(_itoa(nStepping, buff, 16)), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 80, 350, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", "Frequency:", WS_CHILD | SS_RIGHT | WS_VISIBLE, 20, 100, 80, 20, hWnd, NULL, hInst, NULL); CreateWindow("static", _ui64toa(nFreq, buff, 10), WS_CHILD | SS_LEFT | WS_VISIBLE, 110, 100, 350, 20, hWnd, NULL, hInst, NULL); } void GetBrandStr() { char buff[48]; for (i = 2; i <= 4; i++) { __cpuid(table, 0x80000000 | i); memcpy(&buff[16*(i - 2)], &table, 16); } i = 0; while (buff[i]==' ') ++i; while (buff[i]!=0) { sBrand+= buff[i]; ++i; } } void GetId1() { __cpuid(table, 1); nStepping = table[0] & 0xF; nModel = (table[0] & 0xF0) >> 4; nFamily = (table[0] & 0xF00) >> 8; } void GetFreq() { unsigned __int64 ts_before, ts_after; LARGE_INTEGER count_persec, count_1, count_2; QueryPerformanceFrequency(&count_persec); ts_before = __rdtsc(); QueryPerformanceCounter(&count_1); while (count_2.QuadPart - count_1.QuadPart < count_persec.QuadPart) QueryPerformanceCounter(&count_2); ts_after = __rdtsc(); nFreq = ts_after - ts_before; };
Why is it called “OverSoft CPU Informer XP”? Well, that’s a story for another day π
A few years later, in 2011, when I was a full-time web developer already and used FreeBSD as a desktop system at work, I ported the CLI version to the GCC just for fun. My colleague, who’s an actual C++ developer helped me with that. Thanks to a MinGW project, the utility may run on Windows (as well as on macOS) too:
And now, I’m sharing the cross-platform code on GitHub under a permissive license. To make it somewhat usable, I added the Makefile and README files. The rest is a genuine 9-years-old cpp code, which is still compilable by some miracle. The binary executable files for 32 and 64 bit Windows and 64 bit Linux are also available there.