проблема следующая. AIX 5.2 VisualAge c++ компилятор 6.2
написал простенький пример: создается меп из 10000 элементов, каждый элемент содержит вектор из 90000 элементов. потом это все разрушается. на этапе разрушения включаю omp parallel for. результат - падение производительности в 3-4 раза. причем не только на этапе разрушения, но и при создании. :-0 объясните кто-нить сей парадокс...
>проблема следующая. AIX 5.2 VisualAge c++ компилятор 6.2
>написал простенький пример: создается меп из 10000 элементов, каждый элемент содержит вектор
>из 90000 элементов. потом это все разрушается. на этапе разрушения включаю
>omp parallel for. результат - падение производительности в 3-4 раза. причем
>не только на этапе разрушения, но и при создании. :-0 объясните
>кто-нить сей парадокс...Чтобы дать осмысленный ответ нужно знать подробности:
Сколько процессоров? Каких? Какого размера у них кэши?
Сколько openmp порождает потоков?
Да и исходник бы посмотреть, если не жалко.
Например, если процессор один,
потоков сто, то замедление вполне вероятно.
>
>Чтобы дать осмысленный ответ нужно знать подробности:
>
>Сколько процессоров? Каких? Какого размера у них кэши?
>
>Сколько openmp порождает потоков?
>
>Да и исходник бы посмотреть, если не жалко.
>
>Например, если процессор один,
>потоков сто, то замедление вполне вероятно.
да запросто. пример тестовый без авторских прав :)struct VectorElement {
float f;
};
struct MapElement {
int i;
std::vector<VectorElement*> v;};
int main(int argc, char *argv[]) {
std::map<int, MapElement> m;
VectorElement *ve;
MapElement me;struct tms buf = {0, 0, 0, 0};
times(&buf);
std::cout << pthread_self()
<< " Before init : u ="
<< buf.tms_utime / sysconf(_SC_CLK_TCK)
<< "; s =" << buf.tms_stime / sysconf(_SC_CLK_TCK)
<< std::endl;for (int i = 0; i < 1000; ++i) {
for (int j = 0; j < 90000; ++j) {
ve = new VectorElement;
ve->f = j;
me.v.push_back(ve);
}me.i = i;
m.insert(std::make_pair<int, MapElement>(i, me));
me.v.clear();
}times(&buf);
std::cout << pthread_self()
<< " After init : u ="
<< buf.tms_utime / sysconf(_SC_CLK_TCK)
<< "; s ="
<< buf.tms_stime / sysconf(_SC_CLK_TCK)
<< std::endl;std::vector<VectorElement*> *p;
int j, i;
std::vector<std::map<int, MapElement>::iterator> mis;
for (std::map<int, MapElement>::iterator mIt = m.begin();
mIt != m.end(); ++mIt) {
mis.push_back(mIt);
}times(&buf);
std::cout << pthread_self()
<< " After preparing for destroy : u ="
<< buf.tms_utime / sysconf(_SC_CLK_TCK)
<< "; s ="
<< buf.tms_stime / sysconf(_SC_CLK_TCK)
<< std::endl;#pragma omp parallel private(i, p, j)
{
#pragma omp for schedule(dynamic)
for (i = 0; i < mis.size(); ++i) {
p = &mis[i]->second.v;for (j = 0; j < p->size(); ++j) {
delete (*p)[j];
}p->clear();
}
}times(&buf);
std::cout << pthread_self()
<< " After destroy : u ="
<< buf.tms_utime / sysconf(_SC_CLK_TCK)
<< "; s ="
<< buf.tms_stime / sysconf(_SC_CLK_TCK)
<< std::endl;m.clear();
}
процессоров 2 PowerPC_Power4, 6 гиг озу, скоко кеша не знаю
потоков 2. aix дефолтом выставляет. определял и через OMP_NUM_THREADS.статистика такова: без прагмы удаление массива за 44 сек, с включенным омп - 150-160сек
пока стояла дирректива parallel for - тормозило даже создание массива (20 сек без омп против 45-50 с включ омп)заранее спасибо огромное
>>
>>Чтобы дать осмысленный ответ нужно знать подробности:
>
>да запросто. пример тестовый без авторских прав :)
>
>
> #pragma omp parallel private(i, p, j)
> {
> #pragma omp for schedule(dynamic)
> for (i = 0; i < mis.size(); ++i) {
> p = &mis[i]->second.v;
>
> for (j = 0; j < p->size(); ++j) {
> delete (*p)[j];
> }
>
> p->clear();
> }
> }
>
>процессоров 2 PowerPC_Power4, 6 гиг озу, скоко кеша не знаю
>потоков 2. aix дефолтом выставляет. определял и через OMP_NUM_THREADS.
>Ага. Ну вот теперь все гораздо понятней.
(У меня, правда, Solaris, но все выглядит очень похоже)
первым делом запускаем это дело под профилировщиком.
В моем случае это Performance Analyzer из Sun Studio.
Два раза: для OMP_NUM_THREADS=1 и OMP_NUM_THREADS=2видим почти одинаковые картинки, но для двух потоков
лишнее время тратится на функции из malloc.h, и libc-шную синхронизацию.Вспоминаем, что обычный malloc с друзьями
"mt safe, not mt hot". Проверяем на чем-нибудь попроще
чем изначальный пример на C++Пишу программу с openmp которая много раз маллочит и реаллочит.
Убеждаюсь, что чем больше потоков тем хуже.#pragma omp parallel for
for(i=0; i<N; i++) {
p[i] = malloc(1);
for (j=0; j<1000; j++) {
p[i] = realloc(p[i], 1);
}
}
-bash-3.00$ cc -xO4 t.c -xopenmp
-bash-3.00$ export OMP_NUM_THREADS=1; time ./a.outreal 0m7.469s
user 0m7.462s
sys 0m0.005s
-bash-3.00$ export OMP_NUM_THREADS=2; time ./a.outreal 0m29.631s
user 0m55.658s
sys 0m1.110s
Что же делать? Нужен другой аллокатор!
На Солярисе есть libumem. Попробуем:-bash-3.00$ cc -xO4 t.c -xopenmp -lumem
-bash-3.00$ export OMP_NUM_THREADS=1; time ./a.outreal 0m5.768s
user 0m5.734s
sys 0m0.005s
-bash-3.00$ export OMP_NUM_THREADS=2; time ./a.outreal 0m2.756s
user 0m5.761s
sys 0m0.006sПолегчало. Теперь к исходному примеру:
-bash-3.00$CC -xO4 t.cc -xopenmp -lumem
-bash-3.00$ export OMP_NUM_THREADS=1; time ./a.out
1 Before init : u =0; s =01 After init : u =27; s =1
1 After preparing for destroy : u =27; s =1
1 After destroy : u =47; s =2real 0m50.877s
user 0m47.380s
sys 0m2.242s
-bash-3.00$ export OMP_NUM_THREADS=2; time ./a.out
1 Before init : u =0; s =0
1 After init : u =27; s =1
1 After preparing for destroy : u =27; s =1
1 After destroy : u =65; s =2real 0m49.948s
user 1m5.873s
sys 0m2.114sНу вот, основной кусок починился.
Дальше сам.