Оценка массива уравнений работает очень медленно
Я работал над проектом по моделированию биологически вдохновленных нейронных сетей с использованием arrayfire. Я подошел к моменту проведения некоторых временных тестов и был разочарован результатами, которые я получил. Я решил попробовать одну из самых быстрых и простых в использовании моделей для временного теста - модель Ижикевича. Когда я запустил новый тест с этой моделью, результаты были хуже. Код, который я использую ниже. Он не делает ничего особенного. Это просто стандартная матричная алгебра. Тем не менее, это займет более 5 секунд, чтобы сделать единственную оценку уравнения только для 10 нейронов! Каждая остановка после этого занимает примерно столько же времени.
Код:
unsigned int neuron_count = 10;
array a = af::constant(0.02, neuron_count);
array b = af::constant(0.2, neuron_count);
array c = af::constant(-65.0, neuron_count);
array d = af::constant(6, neuron_count);
array v = af::constant(-70.0, neuron_count);
array u = af::constant(-20.0, neuron_count);
array i = af::constant(14, neuron_count);
double tau = 0.2;
void StepIzhikevich()
{
v = v + tau*(0.04*pow(v, 2) + 5 * v + 140 - u + i);
//af_print(v);
u = u + tau*a*(b*v - u);
//Leaving off spike threshold checks for now
}
void TestIzhikevich()
{
StepIzhikevich();
timer::start();
StepIzhikevich();
printf("elapsed seconds: %g\n", timer::stop());
}
Вот результаты синхронизации для различного числа нейронов.
Результаты:
neurons seconds
10 5.18275
100 5.27969
1000 5.20637
10000 4.86609
Увеличение количества нейронов, похоже, не имеет огромного эффекта. Время идет немного. Я что-то здесь не так делаю? Есть ли лучший способ оптимизировать вещи с помощью arrayfire, чтобы получить лучшие результаты?
Когда я переключил уравнение v на использование v*v вместо pow(v, 2), время, необходимое для шага, уменьшилось до 3.75762. Это все еще очень медленно, поэтому происходит нечто странное.
[РЕДАКТИРОВАТЬ] Я попытался разделить обработку на куски и нашел что-то новое. Вот код, который я использую сейчас.
Код:
unsigned int neuron_count = 10;
array a = af::constant(0.02, neuron_count);
array b = af::constant(0.2, neuron_count);
array c = af::constant(-65.0, neuron_count);
array d = af::constant(6, neuron_count);
array v = af::constant(-70.0, neuron_count);
array u = af::constant(-20.0, neuron_count);
array i = af::constant(14, neuron_count);
array g = af::constant(0.0, neuron_count);
double tau = 0.2;
void StepIzhikevich()
{
array j = tau*(0.04*pow(v, 2));
//af_print(j);
array k = 5 * v + 140 - u + i;
//af_print(k);
array l = v + j + k;
//af_print(l);
v = l; //If this line is here time is long on second loop
//g = l; //If this is here then time is short.
//u = u + tau*a*(b*v - u);
//Leaving off spike threshold checks for now
}
void TestIzhikevich()
{
timer::start();
StepIzhikevich();
printf("elapsed seconds: %g\n", timer::stop());
timer::start();
StepIzhikevich();
printf("elapsed seconds: %g\n", timer::stop());
}
Когда я запускаю его, не переназначая его обратно на v или не назначая его новой переменной g, тогда время для шага как в первом, так и во втором запуске мало
Результаты:
истекших секунд: 0,0036143
истекшие секунды: 0,00340621
Однако, когда я ставлю v = l; обратно, затем в первый раз он работает быстро, но с тех пор он медленно.
Результаты:
прошедшие секунды: 0,0034497
истекших секунд: 2.98624
Любые идеи о том, что вызывает это?
[РЕДАКТИРОВАТЬ 2]
Я до сих пор не знаю, почему это происходит, но я нашел обходной путь, скопировав массив v, прежде чем использовать его снова.
Код:
unsigned int neuron_count = 100000;
array v = af::constant(-70.0, neuron_count);
array u = af::constant(-20.0, neuron_count);
array i = af::constant(14, neuron_count);
double tau = 0.2;
void StepIzhikevich()
{
//array vp = v;
array vp = v.copy();
//af_print(vp);
array j = tau*(0.04*pow(vp, 2));
//af_print(j);
array k = 5 * vp + 140 - u + i;
//af_print(k);
array l = vp + j + k;
//af_print(l);
v = l; //If this line is here time is long on second loop
}
void TestIzhikevich()
{
for (int i = 0; i < 10; i++)
{
timer::start();
StepIzhikevich();
printf("loop: %d ", i);
printf("elapsed seconds: %g\n", timer::stop());
timer::start();
}
}
Вот результаты сейчас. Во второй раз он работает немного медленно, но теперь быстро. Огромное улучшение по сравнению с ранее.
Результаты: цикл: 0 истекших секунд: 0.657355
цикл: 1 прошедших секунд: 0,981287
цикл: 2 прошедших секунды: 0.000416182
цикл: 3 прошедших секунды: 0.000415045
цикл: 4 прошедших секунды: 0,000421014
цикл: 5 прошедших секунд: 0.000413339
цикл: 6 прошедших секунд: 0.00041675
цикл: 7 прошедших секунд: 0,000412202
цикл: 8 прошедших секунд: 0.000473321
цикл: 9 прошедших секунд: 0,000677432