Frage

In einer App Ich bin Profilieren, fand ich in einigen Szenarien, dass diese Funktion der Lage ist, 10% der gesamten Ausführungszeit zu übernehmen.

Ich habe die Jahre gesehen Diskussion über schnellere sqrt Implementierungen mit hinterhältigen Gleitkommazahlen Tricks, aber ich weiß nicht, ob solche Dinge auf modernen CPUs veraltet sind.

MSVC ++ 2008-Compiler verwendet wird, als Referenz ... obwohl ich davon ausgehen würde sqrt wird nicht fügen viel Aufwand aber.

Sehen Sie hier auch für ähnliche Diskussion auf diese ist eine weit verbreitete Methode, aber ist es wirklich viel schneller? Wie viele Zyklen ist SQRT ohnehin in diesen Tagen?

War es hilfreich?

Lösung

Ja, es ist möglich, auch ohne Tricks:

1) Opfer Genauigkeit für Geschwindigkeit. Der sqrt-Algorithmus ist iterativ, neu implementieren mit weniger Iterationen

2) Lookup-Tabellen:. Entweder nur für den Startpunkt der Iteration, oder in Kombination mit Interpolation Ihnen die ganzen Weg dorthin zu gelangen

3) Caching: Sie sind immer sqrting den gleichen begrenzten Satz von Werten? wenn ja, kann Caching gut funktionieren. Ich habe in Grafikanwendungen dies nützlich gefunden, wo die gleiche Sache für viele Formen der gleiche Größe berechnet wird, so können die Ergebnisse sinnvollerweise im Cache gespeichert werden.

Andere Tipps

Es gibt eine große Vergleichstabelle hier: http://assemblyrequired.crashworks.org/timing-square-root/

Lange Rede kurzer Sinn, SSE2 der ssqrts ist etwa 2x schneller als FPU fsqrt und eine Annäherung + Iteration ist etwa 4-fach schneller als die (8x insgesamt).

Auch wenn Sie versuchen, eine Single-Precision sqrt zu nehmen, stellen Sie sicher, dass die eigentlich das, was Sie bekommen. Ich habe von mindestens einem Compiler gehört, dass der Schwimmer Argument zu einem doppelten, rufen Sie mit doppelter Genauigkeit sqrt umwandeln würde, dann float konvertieren zurück.

Sie sind sehr wahrscheinlich, indem Sie Ihre Algorithmen mehr Verbesserungen in der Geschwindigkeit zu gewinnen als durch Änderung ihrer Implementierungen : Versuchen Sie, Anruf sqrt() weniger statt Anrufe schneller. (Und wenn Sie denken, dies nicht möglich ist - die Verbesserungen für sqrt() Sie erwähnen, sind genau das: Verbesserungen des Algorithmus verwendet, um eine Quadratwurzel zu berechnen.)

Da es sehr häufig verwendet wird, ist es wahrscheinlich, dass Ihre Standard-Bibliothek Implementierung von sqrt() ist nahezu optimal für den allgemeinen Fall. Es sei denn, Sie eine eingeschränkte Domain (zum Beispiel, wenn Sie weniger Präzision benötigen), wo der Algorithmus einiger Abkürzungen nehmen, dann ist es sehr unwahrscheinlich, jemand mit einer Implementierung kommt, der schneller ist.

Beachten Sie, dass, da diese Funktion 10% Ihrer Ausführungszeit verwendet, auch wenn es Ihnen gelingt, mit einer Implementierung zu kommen, dass nur 75% der Zeit von std::sqrt() nimmt, wird dies nur noch Ihre Ausführungszeit bringen von 2,5% . Für die meisten Anwendungen Benutzer wäre dies nicht einmal bemerken, es sei denn, sie eine Uhr zu messen verwenden.

Wie genau müssen Sie Ihre sqrt zu sein? Sie können sehr schnell vernünftige Annäherungen erhalten: siehe Quake3 ausgezeichneten inverse Quadratwurzel Funktion für Inspiration (beachten Sie, dass der Code ist GPL'ed, so dass Sie nicht direkt integrieren möchten).

Sie wissen nicht, ob Sie dieses Problem behoben, aber ich habe vor über sie gelesen, und es scheint, dass das schnellste, was zu tun ist, um die sqrt Funktion mit einer Inline-Assembler-Version ersetzen;

Sie eine Beschreibung einer Last von Alternativen sehen

Hier ist eine schnelle Art und Weise mit einem Blick Tabelle von nur 8 KB. Fehler sind ~ 0,5% des Ergebnisses. Sie können die Tabelle leicht vergrößern, wodurch der Fehler zu reduzieren. Läuft etwa 5-mal schneller als die reguläre sqrt ()

// LUT for fast sqrt of floats. Table will be consist of 2 parts, half for sqrt(X) and half for sqrt(2X).
const int nBitsForSQRTprecision = 11;                       // Use only 11 most sagnificant bits from the 23 of float. We can use 15 bits instead. It will produce less error but take more place in a memory. 
const int nUnusedBits   = 23 - nBitsForSQRTprecision;       // Amount of bits we will disregard
const int tableSize     = (1 << (nBitsForSQRTprecision+1)); // 2^nBits*2 because we have 2 halves of the table.
static short sqrtTab[tableSize]; 
static unsigned char is_sqrttab_initialized = FALSE;        // Once initialized will be true

// Table of precalculated sqrt() for future fast calculation. Approximates the exact with an error of about 0.5%
// Note: To access the bits of a float in C quickly we must misuse pointers.
// More info in: http://en.wikipedia.org/wiki/Single_precision
void build_fsqrt_table(void){
    unsigned short i;
    float f;
    UINT32 *fi = (UINT32*)&f;

    if (is_sqrttab_initialized)
        return;

    const int halfTableSize = (tableSize>>1);
    for (i=0; i < halfTableSize; i++){
         *fi = 0;
         *fi = (i << nUnusedBits) | (127 << 23); // Build a float with the bit pattern i as mantissa, and an exponent of 0, stored as 127

         // Take the square root then strip the first 'nBitsForSQRTprecision' bits of the mantissa into the table
         f = sqrtf(f);
         sqrtTab[i] = (short)((*fi & 0x7fffff) >> nUnusedBits);

         // Repeat the process, this time with an exponent of 1, stored as 128
         *fi = 0;
         *fi = (i << nUnusedBits) | (128 << 23);
         f = sqrtf(f);
         sqrtTab[i+halfTableSize] = (short)((*fi & 0x7fffff) >> nUnusedBits);
    }
    is_sqrttab_initialized = TRUE;
}

// Calculation of a square root. Divide the exponent of float by 2 and sqrt() its mantissa using the precalculated table.
float fast_float_sqrt(float n){
    if (n <= 0.f) 
        return 0.f;                           // On 0 or negative return 0.
    UINT32 *num = (UINT32*)&n;
    short e;                                  // Exponent
    e = (*num >> 23) - 127;                   // In 'float' the exponent is stored with 127 added.
    *num &= 0x7fffff;                         // leave only the mantissa 

    // If the exponent is odd so we have to look it up in the second half of the lookup table, so we set the high bit.
    const int halfTableSize = (tableSize>>1);
    const int secondHalphTableIdBit = halfTableSize << nUnusedBits;
    if (e & 0x01) 
        *num |= secondHalphTableIdBit;  
    e >>= 1;                                  // Divide the exponent by two (note that in C the shift operators are sign preserving for signed operands

    // Do the table lookup, based on the quaternary mantissa, then reconstruct the result back into a float
    *num = ((sqrtTab[*num >> nUnusedBits]) << nUnusedBits) | ((e + 127) << 23);
    return n;
}
scroll top