Насколько эффективен этот код распараллеливания? Есть ли лучший способ сделать это? - PullRequest
1 голос
/ 25 августа 2010

Я создаю большой индекс Lucene, и каждый вставляемый документ требует небольшого «объединения», прежде чем его можно будет вставить. Я читаю все документы из базы данных и вставляю их в указатель. Lucene позволяет вам создавать несколько разных индексов и объединять их позже, поэтому я придумал следующее:

// we'll use a producer/consumer pattern for the job
var documents = new BlockingCollection<Document>();

// we'll have a pool of index writers (each will create its own index)
var indexWriters = new ConcurrentBag<IndexWriter>();

// start filling the collection with documents
Task writerTask = new Task(() => {
    foreach(document in database)
        documents.Add(document);
    domains.CompleteAdding();
}, TaskCreationOptions.LongRunning);
writerTask.Start();

// iterate through the collection, obtaining index writers from the pool and
// creating them when necessary.
Parallel.ForEach(documents.GetConsumingEnumerable(token.Token), document =>
{
    IndexWriter writer;
    if(!indexWriters.TryTake(out writer))
    {
        var dirInfo = new DirectoryInfo(string.Concat(_indexPath, "\\~", Guid.NewGuid().ToString("N")));
        dirInfo.Create();
        var dir = FSDirectory.Open(dirInfo);
        var indexWriter = new IndexWriter(dir, getAnalyzer(), true, IndexWriter.MaxFieldLength.UNLIMITED);
    }
    // prepare and insert the document into the current index
    WriteDocument(writer, document);
    indexWriters.Add(writer); // put the writer back in the pool
});

// now get all of the writers and merge the indexes together...

Мое единственное беспокойство, которое заставило меня задуматься, состояло в том, чтобы вытащить IndexWriter из пула (и затем вернуть его в конце) для каждой итерации, может быть менее эффективно, чем просто создать оптимальное количество потоков для начала, но я Также известно, что ConcurrentBag очень эффективен и имеет чрезвычайно низкие накладные расходы на обработку.

Мое решение в порядке? Или это кричит о лучшем решении?

UPDATE:

После некоторых тестов загрузка из базы данных немного медленнее, чем фактическая индексация, я думаю. Кроме того, окончательное слияние индексов также происходит медленно, потому что я могу использовать только один поток, и я объединял 16 индексов с примерно 1,7 миллионами документов. Тем не менее, я открыт для размышлений по первоначальному вопросу.

1 Ответ

1 голос
/ 25 августа 2010

Одна проблема с Parallel.ForEach, которую я видел, состоит в том, что она может решить добавить потоки сверх нормального для каждого ядра, когда загрузка ЦП мала.Это имеет смысл для задач, ожидающих ответа удаленного сервера, но из-за медленного процесса, интенсивно использующего диск, это может иногда приводить к снижению производительности, так как диск теперь перегружается.

Если ваша обработка связана с диском, а не с процессором, вы можете попробовать добавить ParallelOptions и установить MaxDegreeOfParallelism на Parallel.ForEach, чтобы избежать ненужной перебивки диска.

...