mathnet c# .net performance math

mathnet - c# math module



La implementación más rápida de log2(int) y log2(float) (5)

Esto es lo que he usado:

unsigned log2(register unsigned n) { register unsigned i; for (i=0; (n & 1); n>>=1, i++); return i; }

Edición: verifique estas (variantes ffz): https://patchwork.kernel.org/patch/8845631/

La pregunta es

¿Hay otras implementaciones (y / o más rápidas) de un 2log básico?

Aplicaciones

Las operaciones log2 (int) y log2 (float) son muy útiles en muchos contextos diferentes. Para nombrar algunos: algoritmos de compresión, motores 3D y aprendizaje automático. En casi todos estos contextos se utilizan en el código de bajo nivel que se llama miles de millones de veces ... Especialmente la operación log2 (int) es muy útil.

Debido a que uso log2 todo el tiempo, no quiero dar una aplicación específica en la que estoy trabajando aquí. Lo que es lo mismo es el hecho de que se trata de un drenador de rendimiento real (como lo demuestran las pruebas de rendimiento de varias aplicaciones). Para mí es clave conseguir esto lo más rápido posible.

El código fuente completo para probar todas las implementaciones se agrega en la parte inferior, para que pueda verlo por sí mismo.

Y, por supuesto ... ejecute sus pruebas al menos 3 veces y asegúrese de que los contadores sean lo suficientemente grandes como para alcanzar varios segundos. También hago la operación ''agregar'' para garantizar que el JIT''ter no elimine mágicamente todo el bucle. Así que vamos a empezar con el trabajo real.

Implementación trivial

La implementación trivial de un 2log en C # es:

(int)(Math.Log(x) / Math.Log(2))

Esta implementación es trivial, pero también muy lenta. Requiere 2 operaciones de registro, que ya son bastante lentas. Por supuesto, podemos optimizar esto haciendo que 1.0/Math.Log(2) una constante.

Tenga en cuenta que debemos modificar esta constante un poco para obtener los resultados correctos (como resultado de los errores de punto flotante) o agregar un número pequeño para obtener los resultados correctos. Escogí lo último, pero en realidad no importa, el resultado final es lento en todos los casos.

Búsqueda de tablas

Una solución más rápida para esto es utilizar una tabla de búsqueda. Mientras que puedes usar una tabla de búsqueda de cualquier potencia de 2, usualmente uso un tamaño de tabla de 256 o 64K entradas.

Primero creamos la tabla de búsqueda:

lookup = new int[256]; for (int i = 1; i < 256; ++i) { lookup[i] = (int)(Math.Log(i) / Math.Log(2)); }

A continuación, implementamos el 2log de la siguiente manera:

private static int LogLookup(int i) { if (i >= 0x1000000) { return lookup[i >> 24] + 24; } else if (i >= 0x10000) { return lookup[i >> 16] + 16; } else if (i >= 0x100) { return lookup[i >> 8] + 8; } else { return lookup[i]; } }

Como puede ver, las búsquedas de tablas son una implementación mucho más rápida, pero como una estafa no se pueden usar para calcular log2(float) .

Remoción de rama

Como todos sabemos, los procesadores no son muy buenos en la ramificación, por lo que pensé que las búsquedas en la tabla se pueden mejorar eliminando las ramificaciones. En lugar de los grupos de if''s, introduje una segunda tabla con los valores y cambié los bits para encontrar la entrada en la tabla:

nobranch = new int[16] { 0, 0, 8, 8, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24 }; private static int LogDoubleLookup(int i) { int n = (i | (i >> 4)); n = (n | (n >> 2)); n = (n | (n >> 1)); n = ((n & 0x1000000) >> 21) | ((n & 0x10000) >> 14) | ((n & 0x100) >> 7) | (n & 1); int br = nobranch[n]; return lookup[i >> br] + br; }

Si ejecuta esta prueba, encontrará que en realidad es más lenta que la solución if-then-else.

Y luego estaba el Intel 80386

Intel entendió hace años que esta es una operación importante, por lo que implementaron Bit-Scan-Forward (BSF) en sus procesadores. Otros procesadores tienen instrucciones similares. Esta es, con mucho, la forma más rápida de hacer un 2log que conozco, pero desafortunadamente ahora conozco la forma de usar estas buenas funciones de C # ... No me gusta la idea de tener una implementación que ya no se ejecute cuando una nueva tableta o teléfono llega al mercado, y no conozco ninguna solución multiplataforma que me permita usar esta función directamente.

Otras implementaciones

Como señaló l4V (¡gracias!) Hay un par de otras implementaciones, específicamente:

  • Bucle trivial. Omití esto porque es trivial, esto no es realmente rápido. Implementado en TestTrivial .
  • Sindicatos IEEE / int de 64 bits que pueden utilizarse. Implementado en TestFloat
  • Tablas de búsqueda DeBruijn. Implementado en TestDeBruijn
  • Búsqueda binaria. Implementado en TestBinary

Además de que me gusta el nombre, las tablas de búsqueda de DeBruijn son tan rápidas como las tablas de búsqueda normales, por lo que es uno de los algoritmos más rápidos aquí ... todos los otros algoritmos que he probado son mucho más lentos.

Código de prueba completa

public class Log2Test { public static void TestNaive() { Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += (int)(Math.Log(i) / Math.Log(2.0)); } sw.Stop(); Console.WriteLine("Result: {0} - naive implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } public static int LogTrivialLoop(int v) { int r = 0; while ((v >>= 1) > 0) // unroll for more speed... { r++; } return r; } public static void TestTrivialLoop() { Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += LogTrivialLoop(i); } sw.Stop(); Console.WriteLine("Result: {0} - loop implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } public static int LogFloat(int v) { Helper h = new Helper() { U1 = v, U2 = 0x43300000 }; h.D -= 4503599627370496.0; return (h.U2 >> 20) - 0x3FF; } public static void TestFloat() { Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += LogFloat(i); } sw.Stop(); Console.WriteLine("Result: {0} - IEEE float implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } [StructLayout(LayoutKind.Explicit)] private struct Helper { [FieldOffset(0)] public int U1; [FieldOffset(4)] public int U2; [FieldOffset(0)] public double D; } public static void TestConstant() { double c = 1.0 / Math.Log(2.0); Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += (int)(0.00000000001 + Math.Log(i) * c); } sw.Stop(); Console.WriteLine("Result: {0} - naive 2 implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } private static int LogLookup(int i) { if (i >= 0x1000000) { return lookup[i >> 24] + 24; } else if (i >= 0x10000) { return lookup[i >> 16] + 16; } else if (i >= 0x100) { return lookup[i >> 8] + 8; } else { return lookup[i]; } } public static void TestLookup() { lookup = new int[256]; for (int i = 1; i < 256; ++i) { lookup[i] = (int)(Math.Log(i) / Math.Log(2)); } Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += LogLookup(i); } sw.Stop(); Console.WriteLine("Result: {0} - table lookup implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } private static int LogDoubleLookup(int i) { int n = (i | (i >> 4)); n = (n | (n >> 2)); n = (n | (n >> 1)); n = ((n & 0x1000000) >> 21) | ((n & 0x10000) >> 14) | ((n & 0x100) >> 7) | (n & 1); int br = nobranch[n]; return lookup[i >> br] + br; } public static void TestDoubleLookup() { // Lookup table was already constructed earlier Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += LogDoubleLookup(i); } sw.Stop(); Console.WriteLine("Result: {0} - double table lookup implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } private static int LogBinary(int v) { /* This is the worst implementation ever... - apparently C# is a slow-branching language int[] b = { 0x2, 0xC, 0xF0, 0xFF00, 0x7FFF0000 }; int[] S = { 1, 2, 4, 8, 16 }; int r = 0; // result of log2(v) will go here for (int i = 4; i >= 0; i--) // unroll for speed... { if ((v & b[i]) != 0) { v >>= S[i]; r |= S[i]; } } return r; */ int r = (((v > 0xFFFF)) ? 0x10 : 0); v >>= r; int shift = ((v > 0xFF) ? 0x8 : 0); v >>= shift; r |= shift; shift = ((v > 0xF) ? 0x4 : 0); v >>= shift; r |= shift; shift = ((v > 0x3) ? 0x2 : 0); v >>= shift; r |= shift; r |= (v >> 1); return r; } public static void TestBinary() { // Lookup table was already constructed earlier Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += LogBinary(i); } sw.Stop(); Console.WriteLine("Result: {0} - binary search implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } private static readonly int[] MultiplyDeBruijnBitPosition = new int[32] { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; private static int LogDeBruijn(int v) { v |= v >> 1; // first round down to one less than a power of 2 v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; return MultiplyDeBruijnBitPosition[(uint)(v * 0x07C4ACDDU) >> 27]; } public static void TestDeBruijn() { // Lookup table was already constructed earlier Stopwatch sw = new Stopwatch(); sw.Start(); int n = 0; for (int i = 1; i < 100000000; ++i) { n += LogDeBruijn(i); } sw.Stop(); Console.WriteLine("Result: {0} - de Bruijn implementation took {1:0.000}s", n, sw.Elapsed.TotalSeconds); } private static int[] lookup; private static readonly int[] nobranch = new int[16] { 0, 0, 8, 8, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24 }; static void Main(string[] args) { TestConstant(); TestNaive(); TestDeBruijn(); TestBinary(); TestFloat(); TestTrivialLoop(); TestLookup(); TestDoubleLookup(); Console.ReadLine(); } }


Para más algoritmos, consulte aquí http://www.asmcommunity.net/forums/topic/?id=15010

También hice algunas pruebas en C ++ y mi implementación de BSR es más lenta que la tabla de búsqueda

  • Estoy usando BDS2006, es probable que haya una desaceleración por el estado de push / popping por la directiva asm
  • tu búsqueda está bien, pero estoy usando una tabla de 11 bits en lugar de 8
  • divide 32 bits en 3 ramas en lugar de 4
  • y todavía es lo suficientemente pequeño para manejar sin función init

código:

//--------------------------------------------------------------------------- DWORD log2_slow(const DWORD &x) { DWORD m,i; if (!x) return 0; if (x>=0x80000000) return 31; for (m=1,i=0;m<x;m<<=1,i++); if (m!=x) i--; return i; } //--------------------------------------------------------------------------- DWORD log2_asm(const DWORD &x) { DWORD xx=x; asm { mov eax,xx bsr eax,eax; mov xx,eax; } return xx; } //--------------------------------------------------------------------------- BYTE _log2[2048]= { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, }; DWORD log2(const DWORD &x) { if (x>=0x00400000) return _log2[x>>22]+22; else if (x>=0x00000800) return _log2[x>>11]+11; else return _log2[x]; } //---------------------------------------------------------------------------

código de prueba:

DWORD x,j,i,n=256; tbeg(); for (i=0;i<32;i++) for (j=0;j<n;j++) x=log2 (j<<i); tend(); mm_log->Lines->Add(tstr(1)); tbeg(); for (i=0;i<32;i++) for (j=0;j<n;j++) x=log2_asm (j<<i); tend(); mm_log->Lines->Add(tstr(1)); tbeg(); for (i=0;i<32;i++) for (j=0;j<n;j++) x=log2_slow(j<<i); tend(); mm_log->Lines->Add(tstr(1));

Mis resultados en AMD A8-5500 3.2 GHz:

[ 0.040 ms] log2 (x) - 11bit lookup table [ 0.060 ms] log2_asm (x) - BSR [ 0.415 ms] log2_slow(x) - shift loop

Nota:

  • log2 (0) -> 0 debido al uso de DWORDS, en real debería ser -inf
  • Todos los demás valores son correctos para todas las funciones

Tomó la solución binaria ya mencionada y eliminó la ramificación. Hice algunas pruebas y resultó ser 1.3 veces más rápido que DeBruijn.

public static int Log2(int v) { int r = 0xFFFF - v >> 31 & 0x10; v >>= r; int shift = 0xFF - v >> 31 & 0x8; v >>= shift; r |= shift; shift = 0xF - v >> 31 & 0x4; v >>= shift; r |= shift; shift = 0x3 - v >> 31 & 0x2; v >>= shift; r |= shift; r |= (v >> 1); return r; }


Hay algunos algoritmos de enteros aquí .

Cía#:

public static uint FloorLog2(uint x) { x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return (uint)(NumBitsSet(x) - 1); } public static uint CeilingLog2(uint x) { int y = (int)(x & (x - 1)); y |= -y; y >>= (WORDBITS - 1); x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return (uint)(NumBitsSet(x) - 1 - y); } public static int NumBitsSet(uint x) { x -= ((x >> 1) & 0x55555555); x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); x = (((x >> 4) + x) & 0x0f0f0f0f); x += (x >> 8); x += (x >> 16); return (int)(x & 0x0000003f); } private const int WORDBITS = 32;

Debe mirar el código original en el sitio que vinculé para el contexto, particularmente lo que sucede con Log2 (0).


inline int fast_log2(register double x) { return (reinterpret_cast<uint64_t&>(x) >> 52) - 1023; };