одновременное приложение не так быстро, как однопоточный - PullRequest
1 голос
/ 09 сентября 2011

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

Таким образом, я перебираю дерево, затем из конструктора я вызываю метод, который вызывает новый поток, запущенный через ExecutorService. Вызываемый объект:

    @Override
    public Void call() throws Exception {
        // Get descendants for every node and save it to a list.
        final ExecutorService executor =
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        int index = 0;
        final Map<Integer, Diff> diffs = mDiffDatabase.getMap();
        final int depth = diffs.get(0).getDepth().getNewDepth();
        try {
            boolean first = true;
            for (final AbsAxis axis = new DescendantAxis(mNewRtx, true); index < diffs.size()
                && ((diffs.get(index).getDiff() == EDiff.DELETED && depth < diffs.get(index).getDepth()
                    .getOldDepth()) || axis.hasNext());) {
                if (axis.getTransaction().getNode().getKind() == ENodes.ROOT_KIND) {
                    axis.next();
                } else {
                    if (index < diffs.size() && diffs.get(index).getDiff() != EDiff.DELETED) {
                        axis.next();
                    }

                    final Future<Integer> submittedDescendants =
                        executor.submit(new Descendants(mNewRtx.getRevisionNumber(), mOldRtx
                            .getRevisionNumber(), axis.getTransaction().getNode().getNodeKey(), mDb
                            .getSession(), index, diffs));
                    final Future<Modification> submittedModifications =
                        executor.submit(new Modifications(mNewRtx.getRevisionNumber(), mOldRtx
                            .getRevisionNumber(), axis.getTransaction().getNode().getNodeKey(), mDb
                            .getSession(), index, diffs));
                    if (first) {
                        first = false;
                        mMaxDescendantCount = submittedDescendants.get();
                        // submittedModifications.get();
                    }
                    mDescendantsQueue.put(submittedDescendants);
                    mModificationQueue.put(submittedModifications);
                    index++;
                }
            }

            mNewRtx.close();
        } catch (final AbsTTException e) {
            LOGWRAPPER.error(e.getMessage(), e);
        }
        executor.shutdown();
        return null;
    }

Поэтому для каждого узла он создает новый Callable, который пересекает дерево для каждого узла и подсчитывает потомков и модификации (на самом деле я объединяю две ревизии дерева). Ну, mDescendantsQueue и mModificationQueue являются BlockingQueues. Вначале у меня была только функция followndantsQueue, и я снова обошел дерево, чтобы получить модификации каждого узла (считая изменения, сделанные в поддереве текущего узла). Тогда я подумал, почему бы не сделать как параллельно, так и реализовать конвейерный подход. К сожалению, производительность, казалось, уменьшалась каждый раз, когда я выполнял еще один многопоточный «шаг».

Возможно, потому что XML-дерево обычно не такое глубокое, а издержки параллелизма слишком тяжелы: - /

Сначала я делал все последовательно, что было самым быстрым: - пересекая дерево - для каждого узла перебрать потомков и вычислить downndantCount иificationCount

После использования конвейерного подхода с BlockingQueues кажется, что производительность снизилась, но я фактически не принимал никаких временных мер, и мне пришлось бы отменить многие изменения, чтобы вернуться :( Возможно, производительность увеличивается с увеличением числа процессоров, потому что я только Core2Duo для тестирования прямо сейчас.

С наилучшими пожеланиями,
Johannes

Ответы [ 2 ]

1 голос
/ 09 сентября 2011

Вероятно, это должно помочь: Закон Амадаля , что в основном говорит о том, что увеличение производительности зависит (обратно пропорционально) от процента кода, который должен быть обработан синхронизацией.Следовательно, даже увеличивая за счет увеличения вычислительных ресурсов, он не приведет к лучшему результату.В идеале, если отношение (синхронизированной части к общей части) низкое, тогда при (число процессоров +1) должен получаться лучший выход (если вы не используете сетевой или другой ввод / вывод, в этом случае вы можете увеличить размербассейна).Так что просто следуйте по ссылке выше и посмотрите, поможет ли это

0 голосов
/ 09 сентября 2011

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

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

...