Muchas veces tiendo a usar funciones implementadas en la API de Windows en vez de las nativas de C/C++. El motivo es que al estar disponibles en el propio sistema evitamos que se enlacen en nuestra aplicación, y así el ejecutable resultante sea más compacto. El inconveniente es que probablemente sean más lentas, particularmente porque nunca generarán código en linea (inline).
Nada mejor que hacer una pequeña comparativa de Windows API (WinAPI) vs la librería de funciones de C (C RTL), comparando sus equivalente. A saber:
– ZeroMemory contra memset.
– FillMemory contra memset.
– StrCpy contra _tcscpy / strcpy / wcscpy.
– StrCmp contra _tcscmp / strcmp / wcscmp.
– StrCat contra _tcscat / strcat / wcscat.
– CopyMemory contra memcpy.
– MoveMemory contra memmove.
No he querido complicarme demasiado, y he utilizado el perfil de Release x86 con los ajustes predeterminados.
Con Embarcadero C++ Builder 10.2.3, los resultados han sido de 3.235ms para WinAPI y de solamente 453ms para C. 8 veces más rápido. Si bien todas las funciones de Windows parecen estar menos optimizadas, el caso con StrCmp es particularmente exagerado.
Usando Microsoft Visual C++ 2017, los resultados han sido de 3.703ms para WinAPI y de solamente 266ms para C. La diferencia es aún más notoria, un factor de 14 veces. Con él, las funciones de C son aún más rápidas, casi el doble que en C++ Builder, lo que indica que están mejor optimizadas. Sin embargo, la invocación a funciones de Windows, se hace más lenta. No tengo explicación para esto último, salvo que tenga que ver con la alineación de los bloques de memoria.
API de Windows (ms) | Biblioteca de C/C++ (ms) | |
Embarcadero C++ Builder | 3.235 | 453 |
Microsoft Visual C++ | 3.703 | 266 |
Te dejo el código completo usado en la comparativa:
// -----------------------------------------------------------------------------
#pragma hdrstop
#pragma argsused
// -----------------------------------------------------------------------------
#include
#include
#include
#include
// -----------------------------------------------------------------------------
#define KI_ITER 1000000L
#define KI_SIZE 1024
// -----------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
{
unsigned long lStart, lStop;
TCHAR acBuffer1[KI_SIZE + 16], acBuffer2[KI_SIZE + 16];
//WinAPI
lStart = GetTickCount();
for (unsigned int iCount = 0; iCount < KI_ITER; iCount++)
{
ZeroMemory(acBuffer1, sizeof(acBuffer1));
FillMemory(acBuffer1, sizeof(acBuffer1) - 1, 1);
StrCpy(acBuffer2, acBuffer1);
StrCmp(acBuffer2, acBuffer1);
StrCat(acBuffer2, _T("test"));
CopyMemory(acBuffer2, acBuffer1, sizeof(acBuffer2));
MoveMemory(acBuffer2, acBuffer1, sizeof(acBuffer2));
}
lStop = GetTickCount();
_tprintf(_T("WinAPI: %ld ms\n"), lStop - lStart);
//C RTL
lStart = GetTickCount();
for (unsigned int iCount = 0; iCount < KI_ITER; iCount++)
{
memset(acBuffer1, sizeof(acBuffer1), 0);
memset(acBuffer1, sizeof(acBuffer1) - 1, 1);
_tcscpy(acBuffer2, acBuffer1);
_tcscmp(acBuffer2, acBuffer1);
_tcscat(acBuffer2, _T("test"));
memcpy(acBuffer2, acBuffer1, sizeof(acBuffer2));
memmove(acBuffer2, acBuffer1, sizeof(acBuffer2));
}
lStop = GetTickCount();
_tprintf(_T("C RTL: %ld ms\n"), lStop - lStart);
getchar();
return(0);
}
// -----------------------------------------------------------------------------
Aunque si quieres ir a lo rápido, puedes descargarlo junto a los binarios compilados aquí (43 Kb. en formato ZIP).
Increíble que la winapi sea tan lenta 🙁
Supongo que las comprobaciones de seguridad que se han ido agregando a la nuevas versiones de Windows no benefician en nada al rendimiento.