Оптимизация деления / экспоненциальный расчет - PullRequest
2 голосов
/ 15 апреля 2010

Я унаследовал проект численного моделирования Visual Studio / VB.Net, в котором, вероятно, неэффективные вычисления. Профилирование указывает, что функция вызывается много (1 миллион раз плюс) и тратит около 50% от общего расчета в этой функции. Вот проблемная часть

Результат = (A * (E ^ C)) / (D ^ C * B) (где A-C - локальные двойные переменные и D & E глобальные двойные переменные)

Затем результат сравнивается с порогом, который также может иметь дополнительные улучшения, но я оставлю их в другой день

любые мысли или помощь будут оценены

Steve

Ответы [ 4 ]

1 голос
/ 16 апреля 2010

Оператор экспоненты (Math.Pow) не очень быстрый, нет специальной инструкции CPU для его вычисления. Вы упомянули, что D и E являются глобальными переменными. Это дает проблеск надежды получить его быстрее, если вы можете изолировать их изменения. Переписать уравнение с использованием логарифмов:

log(r) = log((a x e^c) / (b x d^c))
       = log(a x e^c) - log (b x d^c)
       = log(a) + log(e^c) - log(b) - log(d^c)
       = log(a) + c*log(e) - log(b) - c*log(d)
       = log(a) - log(b) + c x (log(e) - log(d))
result = exp(r)

Который предоставляет эту функцию для вычисления результата:

  Function calculate(ByVal a As Double, ByVal b As Double, ByVal c As Double, ByVal d As Double, ByVal e As Double) As Double
    Dim logRes = Math.Log(a) - Math.Log(b) + c * (Math.Log(e) - Math.Log(d))
    Return Math.Exp(logRes)
  End Function

Я рассчитал время с классом StopWatch, оно точно так же быстро, как ваше оригинальное выражение. Не случайно конечно. Вы добьетесь успеха благодаря тому, что сможете заранее рассчитать член Math.Log (e) - Math.Log (d).

1 голос
/ 16 апреля 2010

Одно простое ускорение - это

Result = (A/B) * (E/D)^C

По крайней мере, вы делаете на один показатель меньше. В зависимости от того, что C, могут быть более быстрые способы. Например, если C - маленькое целое число.

редактирование: добавив доказательства, чтобы показать, что это быстрее

    public static void main(String[] args) {
    StopWatch sw = new StopWatch();

    float e = 1.123F;
    float d = 4.456F;
    float c = 453;
    sw.start();
    int max = 5000;
    double result = 0;
    for (int a = 1; a < max; a++) {
        for (float b = 1; b < max; b++) {
            result = (a * (Math.pow(e, c))) / (Math.pow(d, c) * b);
        }
    }
    sw.split();
    System.out.println("slow: " + sw.getSplitTime() + " result: " + result);
    sw.stop();
    sw.reset();

    sw.start();
    result = 0;
    for (int a = 1; a < max; a++) {
        for (float b = 1; b < max; b++) {
            result = a / b * Math.pow(e/d, c);
        }
    }

    sw.split();
    System.out.println("fast: " + sw.getSplitTime() + " result: " + result);
    sw.stop();
    sw.reset();
}

Это вывод

slow: 26062 result: 7.077390271736578E-272
fast: 12661 result: 7.077392136525382E-272

Есть некоторые искажения в числах. Я бы подумал, что более быстрая версия более точна (но это только чувство, потому что я не могу точно понять, почему)

0 голосов
/ 10 февраля 2011

Для функции Math.Floor () посетите: http://bitsbyta.blogspot.com/2010/12/math-floor-function-vbnet.html

Все функции математической библиотеки в vb.net доступны по адресу: http://www.bitsbyta.blogspot.com/

0 голосов
/ 16 апреля 2010

Хорошо сделано для профилирования. Я также проверил бы, что A-C отличается при каждом вызове. Другими словами, возможно ли, что вызывающая сторона фактически вычисляет одно и то же значение снова и снова? Если это так, измените его так, чтобы он кешировал ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...