OpenMP按需嵌套并行

时间:2019-03-26 22:10:09

标签: c++ parallel-processing openmp

我有一份作业列表,正在与OpenMP并行处理:

public class ComboBoxAutoComplete<T> {

    private ComboBox<T> cmb;
    String filter = "";
    private ObservableList<T> originalItems;

    public ComboBoxAutoComplete(ComboBox<T> cmb) {
        this.cmb = cmb;

        ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(cmb);
        comboBoxListViewSkin.getPopupContent().addEventFilter(KeyEvent.ANY, (event) -> {
            if( event.getCode() == KeyCode.SPACE ) {
                event.consume();
            }
        });
        cmb.setSkin(comboBoxListViewSkin);

        originalItems = FXCollections.observableArrayList(cmb.getItems());
        cmb.setTooltip(new Tooltip());
        cmb.setOnKeyPressed(this::handleOnKeyPressed);
        cmb.setOnHidden(this::handleOnHiding);


    }

    public void handleOnKeyPressed(KeyEvent e) {
        ObservableList<T> filteredList = FXCollections.observableArrayList();
        KeyCode code = e.getCode();

        ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(cmb);
        comboBoxListViewSkin.getPopupContent().addEventFilter(KeyEvent.ANY, (event) -> {
            if( event.getCode() == KeyCode.SPACE ) {
                event.consume();
            }
        });
        cmb.setSkin(comboBoxListViewSkin);


        if (code.isLetterKey()||code.isDigitKey()) {
            filter += e.getText();
        }
        if (code == KeyCode.BACK_SPACE && filter.length() > 0) {
            filter = filter.substring(0, filter.length() - 1);
            cmb.getItems().setAll(originalItems);
        }
        if (code == KeyCode.ESCAPE) {
            filter = "";
        }
        if (filter.length() == 0) {
            filteredList = originalItems;
            cmb.getTooltip().hide();
        } else {
            Stream<T> itens = cmb.getItems().stream();
            String txtUsr = unaccent(filter.toString().toLowerCase());
            itens.filter(el -> unaccent(el.toString().toLowerCase()).contains(txtUsr)).forEach(filteredList::add);
            cmb.getTooltip().setText(txtUsr);
            Window stage = cmb.getScene().getWindow();
            double posX = stage.getX() + cmb.getBoundsInParent().getMinX();
            double posY = stage.getY() + cmb.getBoundsInParent().getMinY();
            cmb.getTooltip().show(stage, posX, posY);
            cmb.show();
        }
        cmb.getItems().setAll(filteredList);
    }



    public void handleOnHiding(Event e) {
        ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(cmb);
        comboBoxListViewSkin.getPopupContent().addEventFilter(KeyEvent.ANY, (event) -> {
            if( event.getCode() == KeyCode.SPACE ) {
                event.consume();
            }
        });
        cmb.setSkin(comboBoxListViewSkin);
        filter = "";
        cmb.getTooltip().hide();
        T s = cmb.getSelectionModel().getSelectedItem();
        cmb.getItems().setAll(originalItems);
        cmb.getSelectionModel().select(s);
    }

    private String unaccent(String s) {
        ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(cmb);
        comboBoxListViewSkin.getPopupContent().addEventFilter(KeyEvent.ANY, (event) -> {
            if( event.getCode() == KeyCode.SPACE ) {
                event.consume();
            }
        });
        cmb.setSkin(comboBoxListViewSkin);
        String temp = Normalizer.normalize(s, Normalizer.Form.NFD);
        Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
        return pattern.matcher(temp).replaceAll("");
    }

}

所有作业都有一些顺序的部分,如果单独调用,则可以并行化这些部分:

void processAllJobs()
{
#pragma omp parallel for
    for(int i = 0; i < n; ++i) 
        processJob(i);
}

当我运行void processJob(int i) { for(int iteration = 0; iteration < iterationCount; ++iteration) { doSomePreparation(i); std::vector<Subtask> subtasks = getSubtasks(i); #pragma omp parallel for for(int j = 0; j < substasks.size(); ++j) subtasks[j].Process(); doSomePostProcessing(i) } } 时,将为外循环(在每个作业上)创建线程,并为内循环(在子任务上)创建线程。一切都很好,是有意的。

有时候,非常大的工作需要大量时间来处理。时间足够长,以至于外循环中的所有其他线程已经在最后一个线程之前完成了,并且什么也不做。有没有办法在未使用的线程完成后立即重新利用它们以并行化内部循环?我想象有一种方法可以在每次进入内部并行区域时检查未使用的线程数。

我无法预测工作需要多长时间。这可能不仅是一项持久的工作-也许有两项或三项。

1 个答案:

答案 0 :(得分:3)

您对问题的描述听起来更像是OpenMP任务分配将是一个更好的选择。您的代码将如下所示:

void processAllJobs()
{
#pragma omp parallel master
    for(int i = 0; i < n; ++i) 
#pragma omp task
        processJob(i);
}

然后作业的处理如下:

void processJob(int i)
{
    for(int iteration = 0; iteration < iterationCount; ++iteration)
    {
        doSomePreparation(i);
        std::vector<Subtask> subtasks = getSubtasks(i);
#pragma omp taskloop   // add grainsize() clause, if Process() is very short
        for(int j = 0; j < substasks.size(); ++j)
            subtasks[j].Process();
        doSomePostProcessing(i)
    }
}

这样,您就可以自然负载平衡(假设您有足够的任务),而不必依赖嵌套的并行性。