Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
acts-framework
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
This is an archived project. Repository and other project resources are read-only.
Show more breadcrumbs
Gabriel Farrugia
acts-framework
Commits
71dbacd5
Commit
71dbacd5
authored
7 years ago
by
Hadrien G
Committed by
Hadrien Benjamin Grasland
7 years ago
Browse files
Options
Downloads
Patches
Plain Diff
Okay clang-format, that really looks awful now, but if it's the only way to pass CI...
parent
aba656b1
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
Core/include/ACTFW/Concurrency/parallel_for.hpp
+82
-83
82 additions, 83 deletions
Core/include/ACTFW/Concurrency/parallel_for.hpp
with
82 additions
and
83 deletions
Core/include/ACTFW/Concurrency/parallel_for.hpp
+
82
−
83
View file @
71dbacd5
...
...
@@ -49,42 +49,42 @@ namespace Details {
return
(
outcome
==
LoopOutcome
<
T
>
(
LoopFlow
::
Break
));
}
// This macro wraps a user-provided for loop iteration code into a functor
// which returns a LoopOutcome. It handles all loop iteration outcomes which
// can only be handled by a macro, that is, everything but exceptions.
//
// The RETURN_TYPE parameter indicates the return type of the function inside
// which the underlying for loop is located, and INDEX is the name of the loop
// variable expected by the user's iteration code.
//
// Ideally, RETURN_TYPE should be inferred instead of caller-provider, but
// that does not seem possible in C++14. I think C++17's flavor of auto is
// powerful enough for such inference, so maybe something for the future...
//
#define _ACTFW_WRAPPED_LOOP_ITERATION(RETURN_TYPE, INDEX, ...) \
/* To produce a functor in a random context, we use a catch-all lambda */
\
[&](size_t INDEX) -> FW::Details::LoopOutcome<RETURN_TYPE> { \
/* We set up a fake for loop environment to catch all user actions */
\
for (int _dummy_##INDEX = 0; _dummy_##INDEX < 2; ++_dummy_##INDEX) { \
if (_dummy_##INDEX == 0) { \
/* The user's loop iteration code is pasted here. It may return */
\
/* or throw an exception, that is handled in higher-level code */
\
__VA_ARGS__ \
// This macro wraps a user-provided for loop iteration code into a functor
// which returns a LoopOutcome. It handles all loop iteration outcomes which
// can only be handled by a macro, that is, everything but exceptions.
//
// The RETURN_TYPE parameter indicates the return type of the function inside
// which the underlying for loop is located, and INDEX is the name of the loop
// variable expected by the user's iteration code.
//
// Ideally, RETURN_TYPE should be inferred instead of caller-provider, but
// that does not seem possible in C++14. I think C++17's flavor of auto is
// powerful enough for such inference, so maybe something for the future...
//
#define _ACTFW_WRAPPED_LOOP_ITERATION(RETURN_TYPE, INDEX, ...)
\
/* To produce a functor in a random context, we use a catch-all lambda */
\
[&](size_t INDEX) -> FW::Details::LoopOutcome<RETURN_TYPE> {
\
/* We set up a fake for loop environment to catch all user actions */
\
for (int _dummy_##INDEX = 0; _dummy_##INDEX < 2; ++_dummy_##INDEX) {
\
if (_dummy_##INDEX == 0) {
\
/* The user's loop iteration code is pasted here. It may return */
\
/* or throw an exception, that is handled in higher-level code */
\
__VA_ARGS__
\
\
/* If control reaches this point, the loop iteration code */
\
/* finished normally without continuing, breaking, or returning */
\
return FW::Details::LoopFlow::NormalIteration; \
} else { \
/* If control reaches this point, the loop iteration was skipped */
\
/* using the "continue" control flow keyword */
\
return FW::Details::LoopFlow::Continue; \
} \
/* If control reaches this point, the loop iteration code */
\
/* finished normally without continuing, breaking, or returning */
\
return FW::Details::LoopFlow::NormalIteration; \
} else { \
/* If control reaches this point, the loop iteration was skipped */
\
/* using the "continue" control flow keyword */
\
return FW::Details::LoopFlow::Continue; \
} \
} \
\
/* It control reaches this point, the loop was aborted using the */
\
/* "break" control flow keyword */
\
return FW::Details::LoopFlow::Break; \
}
/* It control reaches this point, the loop was aborted using the */
\
/* "break" control flow keyword */
\
return FW::Details::LoopFlow::Break;
\
}
// Thanks to the loop iteration wrapper above, most of the ACTSFW parallel
// for loop can now be written as real C++ code, instead of a macro.
...
...
@@ -113,11 +113,11 @@ namespace Details {
bool
exit_loop_early
=
false
;
LoopOutcome
<
T
>
exit_reason
=
LoopFlow
::
NormalIteration
;
// Our parallel for loop is implemented using OpenMP
#pragma omp parallel for
// Our parallel for loop is implemented using OpenMP
#pragma omp parallel for
for
(
size_t
index
=
start
;
index
<
end
;
++
index
)
{
// Skip remaining loop iterations if asked to exit the loop early
#pragma omp flush(exit_loop_early)
// Skip remaining loop iterations if asked to exit the loop early
#pragma omp flush(exit_loop_early)
if
(
exit_loop_early
)
continue
;
// Run this loop iteration and record the outcome, exceptions included
...
...
@@ -130,65 +130,64 @@ namespace Details {
// Abort the loop if the iteration's outcome states that we should do so
if
(
loop_outcome_is_fatal
(
outcome
))
{
#pragma omp critical
#pragma omp critical
{
exit_reason
=
std
::
move
(
outcome
);
exit_loop_early
=
true
;
#pragma omp flush(exit_loop_early)
#pragma omp flush(exit_loop_early)
}
}
}
// Analyze the loop termination cause and react accordingly
switch
(
exit_reason
.
which
())
{
// The loop exited normally or via break, no need to do anything
case
0
:
return
boost
::
optional
<
T
>
();
// The loop was exited due to an early return, propagate it up the stack
case
1
:
return
boost
::
get
<
T
>
(
std
::
move
(
exit_reason
));
// The loop was exited because an exception was thrown. Rethrow it.
case
2
:
auto
exception
=
boost
::
get
<
std
::
exception_ptr
>
(
std
::
move
(
exit_reason
));
std
::
rethrow_exception
(
std
::
move
(
exception
));
// The loop exited normally or via break, no need to do anything
case
0
:
return
boost
::
optional
<
T
>
();
// The loop was exited due to an early return, propagate it up the stack
case
1
:
return
boost
::
get
<
T
>
(
std
::
move
(
exit_reason
));
// The loop was exited because an exception was thrown. Rethrow it.
case
2
:
auto
exception
=
boost
::
get
<
std
::
exception_ptr
>
(
std
::
move
(
exit_reason
));
std
::
rethrow_exception
(
std
::
move
(
exception
));
}
}
/// The following macro is the out-of-order multithreaded equivalent of the
/// following serial loop (macro arguments capitalized for emphasis):
///
/// for(size_t INDEX = START; index < END; ++index) {
/// ...
/// }
///
/// Unlike raw OpenMP 3.1, we also support breaks, early returns, and
/// exception propagation, to allow for more idiomatic C++ code. On the other
/// hand, due to the way the C preprocessor handles macro arguments, the loop
/// iteration code should not contain any preprocessor directive.
///
/// Due to limitations of C++14's type inference, this macro may currently
/// only be called in a function which returns FW::ProcessCode. This
/// restriction may be lifted in the future if C++ type inference improves.
///
#define ACTFW_PARALLEL_FOR(INDEX, START, END, ...) \
/* This dummy do-while asserts that the macro's output is a statement */
\
do { \
/* Execute the parallel for loop */
\
auto optional_early_return \
= FW::Details::parallel_for_impl<FW::ProcessCode>( \
(START), \
(END), \
_ACTFW_WRAPPED_LOOP_ITERATION( \
FW::ProcessCode, INDEX, __VA_ARGS__)); \
/// The following macro is the out-of-order multithreaded equivalent of the
/// following serial loop (macro arguments capitalized for emphasis):
///
/// for(size_t INDEX = START; index < END; ++index) {
/// ...
/// }
///
/// Unlike raw OpenMP 3.1, we also support breaks, early returns, and
/// exception propagation, to allow for more idiomatic C++ code. On the other
/// hand, due to the way the C preprocessor handles macro arguments, the loop
/// iteration code should not contain any preprocessor directive.
///
/// Due to limitations of C++14's type inference, this macro may currently
/// only be called in a function which returns FW::ProcessCode. This
/// restriction may be lifted in the future if C++ type inference improves.
///
#define ACTFW_PARALLEL_FOR(INDEX, START, END, ...)
\
/* This dummy do-while asserts that the macro's output is a statement */
\
do {
\
/* Execute the parallel for loop */
\
auto optional_early_return
\
= FW::Details::parallel_for_impl<FW::ProcessCode>(
\
(START),
\
(END),
\
_ACTFW_WRAPPED_LOOP_ITERATION(
\
FW::ProcessCode, INDEX, __VA_ARGS__));
\
\
/* Return early from the host function if asked to do so */
\
if (optional_early_return) { \
return *optional_early_return; \
} \
} while (false);
/* Return early from the host function if asked to do so */
\
if (optional_early_return) { \
return *optional_early_return; \
} \
} while (false);
}
}
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment