- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
double dotProduct(double vec[]) {
int k, m, v;
double rtn;
rtn = 0.0;
k = vec.length / 4;
m = vec.length % 4;
v = 0;
while ((k--) > 0) {
rtn += vec[v];
rtn += vec[v + 1];
rtn += vec[v + 2];
rtn += vec[v + 3];
v += 4;
}
while ((m--) > 0) {
rtn += vec[v];
v++;
}
return rtn;}
guest 10.07.2009 13:19 # 0
guest 10.07.2009 13:27 # 0
Ага, именно так и стоит в Java делать))). Особенно при обходе массивов
guest 10.07.2009 13:44 # +2
guest 10.07.2009 13:48 # 0
Для C/C++ это действительно агрессивная оптимизация - экономия машинных тактов (код то компилируется в машинный). А вот есть ли в этом смысл для Java...
Может тут и высмеивают простое перенесение "исподвертов" одного языка в другой, хотя выигрыш заранее не известен...
guest 10.07.2009 13:57 # 0
guest 10.07.2009 14:07 # −1
guest 10.07.2009 14:32 # 0
#5
Я JVM не знаю... Может она отлавливает циклы с однотипными действиями внутри байт-кода и, при компиляции налету в машинный, разворачивает в цепь непрерывных действий, передача управления назад вообще не выполняется.
Ловля таких вещей вполне по силам управляемому коду... Потому я осторожно высказал предположение, что для Java такой подход ничего не даёт.
guest 10.07.2009 14:32 # 0
guest 10.07.2009 15:24 # 0
IMHO программы пишутся для человека, а не для машины. Скажите пожалуйста для чего и где нужна эта "оптимизация"?
Хочется оптимизировать ассемблер - пишите на нём.
guest 10.07.2009 15:38 # +2
Исходя из контекста - программа написана на Java. Среди многих Java-кодеров (в том числе и среди создателей самой технологии Java)
такие оптимизации считаются антипаттерном в 99% случаев.
+ современные JIT-компиляторы снабжены оптимизационными фичами.
ИМХО - писать такой код - издевательство над собой и теми кто будет его читать.
guest 10.07.2009 16:23 # 0
guest 10.07.2009 17:26 # 0
100к элементов в каждом массиве.. выполняется ничуть не медленнее, чем с такой "оптимизацией", в среднем даже быстрее
guest 10.07.2009 17:51 # 0
#9
Как я упомянул выше, для С/C++ такой подход нормален. Данный код размещён сюда, скорее всего, именно для демострации неправильного применения оптимизации.
Но разворачивание циклов, указательная арифметика, использование сдвига и прочее - всё это позволяет вам играть в трёхмерные компьютерные игры.
Программы действительно пишутся для человека, но код создаётся для машины.
Не сама по себе оптимизация виновата, а то место, в котором её применили.
guest 10.07.2009 18:00 # 0
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 20; ++i)
{
DoCheck();
}
}
static double MultiplyFast(double[] v1, double[] v2)
{
double res = 0.0;
long n = v1.Length;
long m = n - v1.Length % 4;
while (n != m)
{
res += v1[--n];
}
while (m != 0)
{
res += v1[m - 1] * v2[m - 1];
res += v1[m - 2] * v2[m - 2];
res += v1[m - 3] * v2[m - 3];
res += v1[m - 4] * v2[m - 4];
m -= 4;
}
return res;
}
static double MultiplySlow(double[] v1, double[] v2)
{
double res = 0.0;
long size = v1.Length;
for (long i = 0; i < size; ++i)
{
res += (v1[i] * v2[i]);
}
return res;
}
static void DoCheck()
{
const int vectorSize = 10000;
double[] vector_1 = new double[vectorSize];
double[] vector_2 = new double[vectorSize];
Random rand = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < vectorSize; ++i)
{
vector_1[i] = rand.NextDouble();
vector_2[i] = rand.NextDouble();
}
double res_slow = 0.0;
long counter = 100000;
DateTime begin_slow = DateTime.Now;
while (--counter != 0)
{
res_slow += MultiplySlow(vector_1, vector_2);
}
DateTime end_slow = DateTime.Now;
double res_fast = 0.0;
counter = 100000;
DateTime begin_fast = DateTime.Now;
whil
guest 10.07.2009 18:01 # 0
counter = 100000;
DateTime begin_fast = DateTime.Now;
while (--counter != 0)
{
res_fast += MultiplyFast(vector_1, vector_2);
}
DateTime end_fast = DateTime.Now;
Console.WriteLine(@"Fast result: {0}, SLow result: {1}",
(long)(end_fast.Ticks - begin_fast.Ticks), (long)(end_slow.Ticks - begin_slow.Ticks));
}
}
}
guest 10.07.2009 18:03 # 0
Fast result: 82345331, SLow result: 100314426
Fast result: 82970343, SLow result: 92501776
Fast result: 78595259, SLow result: 87032921
Fast result: 78751512, SLow result: 85939150
Fast result: 77970247, SLow result: 90157981
Fast result: 82657837, SLow result: 85939150
Fast result: 86876668, SLow result: 91251752
Fast result: 80782801, SLow result: 93126788
Fast result: 81095307, SLow result: 92970535
Fast result: 85001632, SLow result: 90782993
Fast result: 81251560, SLow result: 89220463
Fast result: 82189078, SLow result: 88126692
Fast result: 82814090, SLow result: 92345523
Fast result: 78595259, SLow result: 87814186
Fast result: 80001536, SLow result: 87501680
Fast result: 81876572, SLow result: 89064210
Fast result: 79845283, SLow result: 89845475
Fast result: 80939054, SLow result: 87657933
Fast result: 81564066, SLow result: 88282945
Fast result: 83595355, SLow result: 88282945
для до-диеза, для джавы не думаю, что бедет сильно отличаться
guest 10.07.2009 18:06 # 0
guest 10.07.2009 22:37 # 0
#14-#17
Comparison result: 0,0843373493975904
Comparison result: 0,0975609756097561
Comparison result: 0,0987654320987654
Comparison result: 0,121951219512195
Comparison result: 0,0864197530864198
Comparison result: 0,0864197530864198
Comparison result: 0,0987654320987654
Comparison result: 0,0975609756097561
Comparison result: 0,108433734939759
13 элементов 1.0e7 вызывов (относительное ускорение)
------------------------------------------
Comparison result: 0,120689655172414
Comparison result: 0,137931034482759
Comparison result: 0,152542372881356
Comparison result: 0,152542372881356
Comparison result: 0,152542372881356
Comparison result: 0,135593220338983
Comparison result: 0,137931034482759
Comparison result: 0,137931034482759
Comparison result: 0,155172413793103
10003 элементов на 10000 вызывов
--------------------------------------------
Comparison result: 0,103448275862069
Comparison result: 0,103448275862069
Comparison result: 0,103448275862069
Comparison result: 0,11864406779661
Comparison result: 0,103448275862069
Comparison result: 0,11864406779661
Comparison result: 0,103448275862069
Comparison result: 0,0862068965517241
Comparison result: 0,103448275862069
1000003 Элементов на 100 вызывов
Думаю, что здесь можно признать ускорение на 10%
В C++ получилось где-то 25% +- трамвайная остановка... Но там я для красоты сделал развёртку на два оператора.
guest 11.07.2009 07:17 # 0
И ещё всем C# .NET программистам пища для размышлений. Как известно в Visual Studio (у меня Professional Edition 2008) можно делать версии с различной оптимизацией на уровнях IL и JIT. Вот результаты Debug неоптимизированного кода в сравнениее с кодом, оптимизированным и на IL, и на машинном уровнях:
Оптимизированный
Comparison result: 0,302325581395349
Comparison result: 0,25
Comparison result: 0,17948717948718
Comparison result: 0,205128205128205
Comparison result: 0,268292682926829
Comparison result: 0,205128205128205
Comparison result: 0,225
Comparison result: 0,24390243902439
Comparison result: 0,230769230769231
Comparison result: 0,25
----------------------------------------
Неоптимизированный
Comparison result: 0,15
Comparison result: 0,180327868852459
Comparison result: 0,137931034482759
Comparison result: 0,135593220338983
Comparison result: 0,12280701754386
Comparison result: 0,11864406779661
Comparison result: 0,120689655172414
Comparison result: 0,137931034482759
Comparison result: 0,12280701754386
Comparison result: 0,120689655172414
---------------------------------------------
Для теста использовались массивы из 100003 элементов,
число вызывов 1000 раз.
Как видно прирост производительности для оптимизированной версии в два раза больше.
Вот и подумаем, нужна ли подобная оптимизация на управляемом коде...
guest 25.07.2009 11:11 # +2
Серверный компилятор от Sun HotSpot JVM циклы раскручивает. Поэтому такая оптимизация для это JVM ничего не даст (проверено).
А для знакомого с такого рода оптимизациями код вполне себе читабелен.
Да, целочисленное деление там вообще не нужно (для округления до длин, кратных четырём). Можно обойтись битовыми операциями.
Что-то типа n = a.length & ~3
guest 21.10.2009 13:41 # 0
Насколько я знаю, современные компиляторы сами развертывают циклы по массивов, причем оптимальнее, чем ручками.
Если же все-таки в вашем конкретном случае ручная оптимизация оказывается лучше, то ее лучше всего вынести в отдельный класс.
В идеале я бы сделал две реализации и один интерфейс, чтобы и сохранить более читабельный вариант, и пользоваться более оптимальным в конечном продукте.
Естественно, в оптимизированном методе при этом должна быть ссылка на неоптимизированный.