Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Мультипоточная реализация quicksort 
V
    Опции темы
Coder
Дата 5.3.2009, 03:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: нет
Всего: 11



Пытаюсь сделать мультипоточную быструю сортировку. Основная загвоздка вот в чем:
Допустим, на вход поступает массив: 2, 6, 8, 4, 0, 3, 7, 4. Одному потоку достаются элементы [0..3] другому [4..7]. На выходе от потоков получаем два отсортированных массива: 2, 4, 6, 8 и 0, 3, 4, 7. Ну а здесь получается нужно еще как-то эти два массива слить (фактически заново отсортировать), что нарушает условие задачи.

В общем у меня ощущение, что я выбрал в принципе не верный путь решения  smile
По идее потоки должны сортировать не свои кусочки массива, а работать как бы сообща над всем массивом сразу. 
Есть идеи, как это можно правильно перейти от рекурсивного алгоритма к мультипоточному?
PM MAIL   Вверх
Lipetsk
  Дата 5.3.2009, 09:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


в форме ;)
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 28.1.2009
Где: Липецк

Репутация: 2
Всего: 5



заново сортировать не надо, вам осталось получать минимальные элементы из двух очередей
PM   Вверх
Coder
Дата 5.3.2009, 09:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: нет
Всего: 11



Lipetsk, я немного не уточнил - число потоков не обязательно равно 2, может быть и больше (этот параметр задается) => подмассивов будет больше. 

PS. Я уже нашел способ объединить в порядке возрастания два массива, остается решить, как объединять большее число. Если я конечно вообще на правильном пути  smile  


PM MAIL   Вверх
maxim1000
Дата 5.3.2009, 14:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 33
Всего: 110



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


--------------------
qqq
PM WWW   Вверх
Silent
Дата 5.3.2009, 20:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 252
Регистрация: 3.10.2006

Репутация: 1
Всего: 9



Первый проход будет в однопоточном режиме, массив отсортируется относительно среднего элемента. Потом запускаем по потоку на правую и левую часть, и так рекурсивно, то есть один поток после упорядочивания создает два дочерних потока и завершается. Можно ввести ограничение на количество одновременно выполняемых потоков.
PM MAIL   Вверх
Coder
Дата 6.3.2009, 10:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: нет
Всего: 11



Вот что у меня получилось.

Код

#include <stdlib.h>
#include <windows.h>
#include <iostream>
#include <time.h>

using namespace std;

// массив для сортировки
int *super_array;

// структура для передачи параметров потоку
struct param_RL{
    int R, L;
};

CRITICAL_SECTION cs;
CRITICAL_SECTION inc_cs;

int ThreadCount = 0;        // содержит текущее число потоков
int MaxThreads = 2;            // максимальное число потоков

void m_qsort(int l, int r);

DWORD WINAPI thread_sort(LPVOID lpParameter){
    param_RL *p = (param_RL*)lpParameter;

    // сортируем в потоке
    m_qsort(p->L,p->R);

    delete p;

    // выходим, уменьшаем счетчик потоков
    EnterCriticalSection(&inc_cs);
    ThreadCount--;
    LeaveCriticalSection(&inc_cs);

    ExitThread(0);
    return 0;
}

void m_qsort(int l, int r){
    int i = l;
    int j = r;
    int x = super_array[(r + l) /2];
    do  {
        while(super_array[i]<x)
            i++;
        while(super_array[j]>x)
            j--;
        if (i<=j){
            EnterCriticalSection(&cs);
            swap(super_array[i++], super_array[j--]);
            LeaveCriticalSection(&cs);
        }
    }while(i<=j);
 
    if (i < r){
        // выбираем метод запуска для сортировки новой части
        if (ThreadCount>=MaxThreads){
            m_qsort(i,r);    // вызываем рекурсию внутри потока
        }
        else{    // создаем новый поток
            EnterCriticalSection(&inc_cs);
            ThreadCount++;
            LeaveCriticalSection(&inc_cs);

            param_RL *p = new param_RL;
            p->L=i;
            p->R=r;
            CreateThread(NULL,0,thread_sort,(PVOID)p,0,0);
        }
    }

    if (l < j){
        // выбираем метод запуска для сортировки новой части
        if (ThreadCount>=MaxThreads){
            m_qsort(l,j);
        }
        else{
            EnterCriticalSection(&inc_cs);
            ThreadCount++;
            LeaveCriticalSection(&inc_cs);
            
            param_RL *p = new param_RL;
            p->L=l;
            p->R=j;
            CreateThread(NULL,0,thread_sort,(PVOID)p,0,0);
        }
    
    }
}

void main(){
    InitializeCriticalSection(&cs);
    InitializeCriticalSection(&inc_cs);

    int n = 1000000;    // число элементов
    long s1, s2;        // для замера времени

    super_array=new int[n];
    srand((unsigned)time(NULL));
    for (int i=0; i<n; i++){
        super_array[i]=rand();
//        cout<<super_array[i]<<" ";
    }

    s1=GetTickCount();

    // запускаем сортировку
    m_qsort(0,n-1);
    
    // ждем завершения всех потоков
    while (true){
        EnterCriticalSection(&inc_cs);
        if (!ThreadCount){    
            LeaveCriticalSection(&inc_cs);
            break;
        }
        LeaveCriticalSection(&inc_cs);
    }
    s2=GetTickCount();

    //cout<<endl<<endl;
    //for (int i=0; i<n; i++){
    //    cout<<super_array[i]<<" ";
    //}
    cout<<endl<<endl<<s2-s1<<" ms.";

    delete []super_array;

    cin.get();    
    
    DeleteCriticalSection(&cs);
    DeleteCriticalSection(&inc_cs);
}


В общем схема такая, как расписал Silent. 
Цитата

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


Все вроде правильно - верно сортируется массив, работает заданное число потоков. Но вот что я не пойму - почему при чистой рекурсии (если MaxThreads=0) массив сортируется  быстрее (загружая на 50% процессор), хотя 2 поточная версия использует каждое ядро и грузит проц на 100%...

Вот данные замеров (число элементов = 10`000`000):
Число спец. потоков для сортировки  / время в миллисек.
0 / 13000
2 / 15203
4 / 49250

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

Есть мысли? 

PM MAIL   Вверх
dumb
Дата 7.3.2009, 03:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


sceloglauxalbifacies
****


Профиль
Группа: Экс. модератор
Сообщений: 2929
Регистрация: 16.6.2006

Репутация: нет
Всего: 158



Цитата(Coder @  6.3.2009,  10:25 Найти цитируемый пост)
почему при чистой рекурсии (если MaxThreads=0) массив сортируется  быстрее
после разделения работы между потоками никаких блокировок в рабочих циклах быть не должно.
PM MAIL   Вверх
Coder
Дата 7.3.2009, 05:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: нет
Всего: 11



Цитата(dumb @ 7.3.2009,  11:47)
Цитата(Coder @  6.3.2009,  10:25 Найти цитируемый пост)
почему при чистой рекурсии (если MaxThreads=0) массив сортируется  быстрее
после разделения работы между потоками никаких блокировок в рабочих циклах быть не должно.

Действительно! У нас ведь уже определены границы массива внутри одного потока и они не могут нарушиться. Закомментировал крит. секцию cs - теперь действительно поточная версия сортирует где-то на 1.3 сек. быстрее чисто рекурсивной. Да и вообще появился общий выигрыш во времени.

Еще думаю оптимизировать один кусок, путем создания массива хендлов потоков и функции WaitForMultipleObjects(). Возможно она не так будет грузить проц, как вот эта проверка:

Код

    // ждем завершения всех потоков
    while (true){
        EnterCriticalSection(&inc_cs);
        if (!ThreadCount){    
            LeaveCriticalSection(&inc_cs);
            break;
        }
        LeaveCriticalSection(&inc_cs);
    }



PM MAIL   Вверх
maxim1000
Дата 7.3.2009, 10:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 33
Всего: 110



кроме того, стоит подумать о накладных расходах
конечно, если мы создаём поток для того, чтобы отсортировать 10000000 элементов, скорее всего, станет быстрее
однако, для 10элементов я бы ужене был так уверен

создание потока - нетривиальное действие, которое затрагивуает обращение к системе, создание там каких-то структур и т.д.

так что его стоит минимизировать:
1. нет смысла создавать поток для сортировки маленьких кусочков массивов
2. возможно, стоит поразмыслить на пулом потоков - создать и вначале в количестве, например, равном количеству доступных ядер, а потом делать что-то типа набора текущих задач, из которых выбирать следующую длякаждого освободившегося потока


--------------------
qqq
PM WWW   Вверх
Coder
Дата 7.3.2009, 11:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 733
Регистрация: 13.12.2004

Репутация: нет
Всего: 11



maxim1000, насчет маленьких кусочков - в точку. Нужно будет экспериментально подобрать эту границу.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Алгоритмы"

maxim1000

Форум "Алгоритмы" предназначен для обсуждения вопросов, связанных только с алгоритмами и структурами данных, без привязки к конкретному языку программирования и/или программному продукту.


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, maxim1000.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Алгоритмы | Следующая тема »


 




[ Время генерации скрипта: 0.0620 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.