Сбой: передача возвращаемого значения в код C ++ и повышение производительности на встроенном ассемблере - PullRequest
0 голосов
/ 05 марта 2012

Я пытался вычислить результат x = x + sin (градус) * radius. Хотя я совершенно не знаком с языком ассемблера, я попробовал его в C ++, чтобы улучшить производительность, затем я получил следующую функцию:

void calcu()
{
    //double x;
    __asm
    {
        mov ecx,989680H
        mov eax,0
        start:
        push eax
        fldpi
        fmul
        mov ebx,0B4H
        push ebx
        fdivp st(1),st
        fsin
        mov eax,5
        push eax
        fmul
        fadd
        add eax,1EH
        fistp x
        loop start
    };
}
  • Первая проблема заключается в том, что программа вызывает сбой при вызове этого функция. После небольшой отладки я обнаружил, что утверждение push eax тот самый. Как это может произойти, когда я попытался вставить значение в стек
  • Во-вторых, вы можете заметить, что вверху есть комментарий этой функции, так как я не знаю, как получить возвращаемое значение операторы сборки для переменной x.
  • Последний вопрос: работает ли ассемблерный код намного быстрее, чем версия C ++, когда я пытался вызвать эту функцию 10 000 000 раз?

Ответы [ 2 ]

2 голосов
/ 05 марта 2012

Если вы хотите изучить ассемблер, я бы настоятельно рекомендовал написать код на C или C ++ и проверить сгенерированную сборку на предмет выбора.

Это даст вам основу.

#include <cmath>

double compute(double x, double degree, double radius) {
  return x + std::sin(degree) * radius;
}

Дает следующий LLVM IR:

define double @_Z7computeddd(double %x, double %degree, double %radius)
                             nounwind uwtable readnone
{
  %1 = tail call double @sin(double %degree) nounwind readnone
  %2 = fmul double %1, %radius
  %3 = fadd double %2, %x
  ret double %3
}

declare double @sin(double) nounwind readnone

, который дает следующую сборку:

    .text
    .globl  _Z7computeddd
    .align  16, 0x90
    .type   _Z7computeddd,@function
_Z7computeddd:                          # @_Z7computeddd
.Ltmp1:
    .cfi_startproc
# BB#0:
    subq    $24, %rsp
.Ltmp2:
    .cfi_def_cfa_offset 32
    movsd   %xmm2, 16(%rsp)         # 8-byte Spill
    movsd   %xmm0, 8(%rsp)          # 8-byte Spill
    movaps  %xmm1, %xmm0
    callq   sin
    mulsd   16(%rsp), %xmm0         # 8-byte Folded Reload
    addsd   8(%rsp), %xmm0          # 8-byte Folded Reload
    addq    $24, %rsp
    ret
.Ltmp3:
    .size   _Z7computeddd, .Ltmp3-_Z7computeddd
.Ltmp4:
    .cfi_endproc
.Leh_func_end0:


    .section    ".note.GNU-stack","",@progbits

Обратите внимание, что по-прежнему существует вызов sin.

2 голосов
/ 05 марта 2012

Вам повезло, я только что закончил сверхбыструю функцию синуса, которая даже не требует сборки. На моей машине это в 8 раз быстрее, чем функция glibc sin.

Любой float является допустимым вводом, и он вернет синус этого числа. Обратите внимание, что это приблизительное значение, наихудшая ошибка 0,000296, средняя ошибка 0,000124 и средняя относительная ошибка 0,02%.

Если вы определите CC_FAST_TRUNC, он будет использовать небольшой хак для быстрого округления числа с плавающей точкой до int. Это намного быстрее, чем простое приведение, но требует поплавков IEEE754 и того, что FPU находится в режиме двойной точности. Так что для мобильности не определяйте это.

#include <math.h>

/* #define CC_FAST_TRUNC */

/*

    Approximation of sin(x) by the formula f(x) = ax^5 + bx^3 + cx

    Constants chosen such that:

        f(pi/2) = 1
        f'(pi/2) = 0
        f(pi/6) = 1/2

    Resulting in:

        a =   9 / (4  * pi^5)
        b = -41 / (8  * pi^3)
        c = 201 / (64 * pi)

*/

inline float fast_sin(float x) {
    const float a =  0.00735246819687011731341356165096815f;
    const float b = -0.16528911397014738207016302002888890f;
    const float c =  0.99969198629596757779830113868360584f;

    long k;
    float x2;

    #ifdef CC_FAST_TRUNC
    union {
        double d;
        long i;
    } dtoi_hack;
    #endif

    /* find offset of x from the range -pi to pi */
    #ifdef CC_FAST_TRUNC
    dtoi_hack.d = M_1_PI * x + 103079215104.5;
    k = dtoi_hack.i >> 16;
    #else
    k = (long) (M_1_PI * x + copysign(0.5, x));
    #endif

    /* bring x into range */
    x -= k * M_PI;

    /* calculate sine */
    x2 = x * x;
    x *= c + x2*(b + a*x2);

    /* if x is in an odd pi count we must flip */
    x -= (2 * (k & 1)) * x; /* trick for x = (k % 2) == 0 ? x : -x; */

    return x;
}

Это взято из моего проекта commonc .

...