2012-01-29 3 views
1

У меня есть кусок кода, который должен делать много вычислений, основанных на двойных значениях, что занимает слишком много времени. Могу ли я ускорить это, сбросив несколько десятичных знаков? если я использую форматтер для разбора двойника, не будет ли это делать сначала исчисление, а затем пролить лишние десятичные числа, так что ничего не получится? Каков наилучший способ сделать это?Java удваивает скорость вычислений

Просто что-то, чтобы получить представление:

double avgRatingForPreferredItem = (double) tempAverageRating.get(matrix.get(0).getItemID1())/matrix.size(); 
    double avgRatingForRandomItem = (double) tempAverageRating.get(matrix.get(0).getItemID2())/matrix.size(); 

double numarator = 0; 
    for (MatrixColumn matrixCol : matrix) { 
    numarator += (matrixCol.getRatingForItemID1() - avgRatingForPreferredItem) * (matrixCol.getRatingForItemID2() - avgRatingForRandomItem); 
    } 

    double numitor = 0; 
    double numitorStanga = 0; 
    double numitorDreapta = 0; 
    for (MatrixColumn matrixCol : matrix) { 
    numitorStanga += (matrixCol.getRatingForItemID1() - avgRatingForPreferredItem) * (matrixCol.getRatingForItemID1() - avgRatingForPreferredItem); 
    numitorDreapta += (matrixCol.getRatingForItemID2() - avgRatingForRandomItem) * (matrixCol.getRatingForItemID2() - avgRatingForRandomItem); 
    } 

    numitor = Math.sqrt(numitorStanga * numitorDreapta); 

    double corelare = numarator/numitor; 
+0

Сколько матриц в большинстве случаев входит в матрицу? – harold

+0

Вы работаете на новейшей JVM, доступной вам? –

+0

Почему вы считаете, что вычисления с плавающей запятой являются причиной вашей проблемы с производительностью? Если я посмотрю на ваш код, это может быть класс 'MatrixColumn' и любой тип' matrix'. Вы называете эти методы много раз. И на сегодняшних настольных компьютерах эти вызовы методов, вероятно, дороже операций с плавающей запятой, даже если их реализация тривиальна. – Codo

ответ

3

Я не верю, что фактические значения могут иметь значение.

Это стоит по крайней мере пытается сократить вычисления здесь:

for (MatrixColumn matrixCol : matrix) { 
numitorStanga += (matrixCol.getRatingForItemID1() - avgRatingForPreferredItem) 
       * (matrixCol.getRatingForItemID1() - avgRatingForPreferredItem); 
numitorDreapta += (matrixCol.getRatingForItemID2() - avgRatingForRandomItem) 
       * (matrixCol.getRatingForItemID2() - avgRatingForRandomItem); 
} 

Это зависит от того, насколько умный ЛТ компилятор - и я предполагаю, что getRatingforItemID1 и getRatingforItemID2 только сквозная свойства - но ваш код не менее выглядит, как будто он делает избыточные вычитания. Итак:

for (MatrixColumn matrixCol : matrix) { 
    double diff1 = matrixCol.getRatingForItemID1() - avgRatingForPreferredItem; 
    double diff2 = matrixCol.getRatingForItemID2() - avgRatingForPreferredItem; 
    numitorStanga += diff1 * diff1; 
    numitorDreapta += diff2 * diff2; 
} 

Вы можете попробовать изменить все к float вместо double - на некоторых архитектурах, которые могут сделать вещи быстрее; на других это вполне может быть.

Вы действительно уверены, что это код, который вы указали, который имеет проблему? Это всего лишь алгоритм O (N) - сколько времени он принимает и насколько велика матрица?

+0

В момент разработки мне не сильно понравилась производительность, только читаемость. Вычисление чего-то для любого заданного ввода займет около 50 секунд, с которыми я мог бы жить в демонстрационных целях. Теперь я пытаюсь хранить всю единую комбинацию в хеше, а не вычислять каждый раз. – Buffalo

+0

@ Buffalo: Лично я считаю, что мой восстановленный код читаем в любом случае - более очевидно, что он пытается добавить квадраты :) Но, как я уже сказал, вы уверены, что это действительно узкое место? –

2

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

+0

Это именно то, что я пытался предотвратить. – Buffalo

1

Другой способ сделать это быстрее - использовать массивы вместо объектов. Проблема с использованием объектов вы не имеете ни малейшего представления о том, как они расположены в памяти (часто плохо в моем опыте, как JVM не оптимизирует для этого вообще)

double avgRatingForPreferredItem = (double) tempAverageRating.get(matrix.get(0).getItemID1())/matrix.size(); 
double avgRatingForRandomItem = (double) tempAverageRating.get(matrix.get(0).getItemID2())/matrix.size(); 

double[] ratingForItemID1 = matrix.getRatingForItemID1(); 
double[] ratingForItemID2 = matrix.getRatingForItemID2(); 
double numarator = 0, numitorStanga = 0, numitorDreapta = 0; 
for (int i = 0; i < ratingForItemID1.length; i++) { 
    double rating1 = ratingForItemID1[i] - avgRatingForPreferredItem; 
    double rating2 = ratingForItemID2[i] - avgRatingForRandomItem; 
    numarator += rating1 * rating2; 
    numitorStanga += rating1 * rating1; 
    numitorDreapta += rating2 * rating2; 
} 

double numitor = Math.sqrt(numitorStanga * numitorDreapta); 
double corelare = numarator/numitor; 

Доступ к данным непрерывно в памяти может быть 5x быстрее чем случайный доступ.

0

Возможно, вы сможете ускорить работу своего алгоритма (в зависимости от используемого диапазона значений), изменив значения с плавающей запятой на длинные значения, которые масштабируются в соответствии с количеством десятичных знаков, которое вам нужно, то есть value * 10000 для 4 знаков после запятой.

Если вы решили сделать это, вам необходимо сохранить масштаб в виду для деления и умножения (numitorDreapta += (diff2 * diff2)/10000;), что добавит некоторый беспорядок в ваш код.

Вам нужно будет преобразовать до и после, но если вам нужно выполнить множество вычислений с использованием целочисленной арифметики вместо плавающей точки, вы можете получить ускорение, которое вы ищете.

Смежные вопросы