Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Naturally Sweet Rcpp with Modern C++ and Boost ...

Naturally Sweet Rcpp with Modern C++ and Boost (useR! 2015)

"C++11 feels like a new language: The pieces just fit together better than they used to and I find a higher-level style of programming more natural than before and as efficient as ever." -- Bjarne Stroustrup.

Since 2011, Standard C++ has become a simpler, more productive language -- it has also expanded its support for numerics and parallelism. This allows us to stay within a portable C++ code while achieving the goals of Rcpp sugar.

This example-driven session provides a walkthrough showing how modern C++ can be used for a variety of statistical computing applications. We demonstrate how the advances in the core language, the standard library, and the wider libraries ecosystem (particularly Boost) enable simple and efficient code.

Topics include: shorter code with auto, decltype & range-based for loop; data wrangling with C++11 algorithms, lambdas, and Boost; random number generation with C++11; numerics & statistical analysis with Boost; easy parallelism with C++11 (async) and OpenMP; timing code with the chrono library; the most useful algorithms & containers in C++11 and Boost.

We end with a preview of the upcoming C++14 and C++17 features which further contribute to a simpler, more readable code -- as well as provide recommended resources to find out more!

Matt P. Dziubinski

July 01, 2015
Tweet

More Decks by Matt P. Dziubinski

Other Decks in Programming

Transcript

  1. Naturally Sweet Rcpp with Modern C++ and Boost Matt P.

    Dziubinski useR! 2015 in Aalborg, Denmark [email protected] // @matt_dz Department of Mathematical Sciences, Aalborg University CREATES (Center for Research in Econometric Analysis of Time Series)
  2. C++ Timeline https://isocpp.org/std/status "C++11 feels like a new language: The

    pieces just fit together better than they used to and I find a higher-level style of programming more natural than before and as efficient as ever." — Bjarne Stroustrup. 4
  3. Before Rcpp #include <R.h> #include <Rinternals.h> // not quite right

    int fibonacci_c_impl(int n) { if (n < 2) return n; return fibonacci_c_impl(n - 1) + fibonacci_c_impl(n - 2); } SEXP fibonacci_c(SEXP n) { SEXP result = PROTECT(allocVector(INTSXP, 1)); INTEGER(result)[0] = fibonacci_c_impl(asInteger(n)); UNPROTECT(1); return result; } fibonacci = function(n) .Call("fibonacci_c", n) 6
  4. With Rcpp // still not quite right // [[Rcpp::export]] int

    fibonacci(int n) { if (n < 2) return n; return fibonacci(n - 1) + fibonacci(n - 2); } • Function fibonacci available in R automatically. 7
  5. With Rcpp // still not quite right // [[Rcpp::export]] int

    fibonacci(int n) { if (n < 2) return n; return fibonacci(n - 1) + fibonacci(n - 2); } • Function fibonacci available in R automatically. • 400 CRAN packages may be onto something ;-) 7
  6. Before C++11 #include <iostream> #include <vector> int main() { std::vector<int>

    v(5); int element = 0; for (std::vector<int>::size_type i = 0; i < v.size(); ++i) v[i] = element++; int sum = 0; for (std::vector<int>::size_type i = 0; i < v.size(); ++i) sum += v[i]; std::cout << "sum = " << sum; } • Q.: Is it immediately clear what this code does? 8
  7. With C++11 #include <iostream> #include <vector> int main() { const

    std::vector<int> v {0, 1, 2, 3, 4}; auto sum = 0; for (auto element : v) sum += element; std::cout << "sum = " << sum; } • How about now? 9
  8. With C++11 #include <iostream> #include <vector> int main() { const

    std::vector<int> v {0, 1, 2, 3, 4}; auto sum = 0; for (auto element : v) sum += element; std::cout << "sum = " << sum; } • How about now? • (Not Your Father’s) C++ — Herb Sutter 9
  9. A few C++11 features So far we have used: •

    list initialization: const std::vector<int> v {0, 1, 2, 3, 4}; 10
  10. A few C++11 features So far we have used: •

    list initialization: const std::vector<int> v {0, 1, 2, 3, 4}; • Added benefit: const correctness 10
  11. A few C++11 features So far we have used: •

    list initialization: const std::vector<int> v {0, 1, 2, 3, 4}; • Added benefit: const correctness • auto specifier: auto sum = 0; 10
  12. A few C++11 features So far we have used: •

    list initialization: const std::vector<int> v {0, 1, 2, 3, 4}; • Added benefit: const correctness • auto specifier: auto sum = 0; • equivalent: int i = 0; auto i = 0; 10
  13. A few C++11 features So far we have used: •

    list initialization: const std::vector<int> v {0, 1, 2, 3, 4}; • Added benefit: const correctness • auto specifier: auto sum = 0; • equivalent: int i = 0; auto i = 0; • also equivalent: double x = 0.0; auto x = 0.0; 10
  14. A few C++11 features So far we have used: •

    list initialization: const std::vector<int> v {0, 1, 2, 3, 4}; • Added benefit: const correctness • auto specifier: auto sum = 0; • equivalent: int i = 0; auto i = 0; • also equivalent: double x = 0.0; auto x = 0.0; • http://en.cppreference.com/w/cpp/language/list_initialization https://isocpp.org/wiki/faq/const-correctness http://en.cppreference.com/w/cpp/language/auto 10
  15. A few C++11 features So far we have used: •

    range-based for loop: for (auto element : v) sum += element; 11
  16. A few C++11 features So far we have used: •

    range-based for loop: for (auto element : v) sum += element; • for (range_declaration : range_expression) loop_statement 11
  17. A few C++11 features So far we have used: •

    range-based for loop: for (auto element : v) sum += element; • for (range_declaration : range_expression) loop_statement • less error-prone 11
  18. A few C++11 features So far we have used: •

    range-based for loop: for (auto element : v) sum += element; • for (range_declaration : range_expression) loop_statement • less error-prone • expresses the intent to loop over the whole range 11
  19. A few C++11 features So far we have used: •

    range-based for loop: for (auto element : v) sum += element; • for (range_declaration : range_expression) loop_statement • less error-prone • expresses the intent to loop over the whole range • best practice (efficiency), non-scalar elements (e.g., std::string): for (const auto & element : v) 11
  20. A few C++11 features So far we have used: •

    range-based for loop: for (auto element : v) sum += element; • for (range_declaration : range_expression) loop_statement • less error-prone • expresses the intent to loop over the whole range • best practice (efficiency), non-scalar elements (e.g., std::string): for (const auto & element : v) • http://en.cppreference.com/w/cpp/language/range-for 11
  21. But Wait... There's More! • Core Language (e.g., auto, lambda

    expressions, ...) • Standard Library (e.g., std::async, <random>, ...) 12
  22. But Wait... There's More! • Core Language (e.g., auto, lambda

    expressions, ...) • Standard Library (e.g., std::async, <random>, ...) • Ecosystem (e.g., Boost, Catch, ...) 12
  23. But Wait... There's More! • Core Language (e.g., auto, lambda

    expressions, ...) • Standard Library (e.g., std::async, <random>, ...) • Ecosystem (e.g., Boost, Catch, ...) • ...what's the catch? 12
  24. Catch: C++ Automated Test Cases in Headers https://github.com/philsquared/Catch Catch: A

    modern, C++-native, header-only, framework for unit-tests, TDD and BDD 14
  25. Catch: C++ Automated Test Cases in Headers I // [[Rcpp::plugins(cpp11)]]

    #define STRICT_R_HEADERS // https://github.com/philsquared/Catch/blob/master/docs/configuration.md #define CATCH_CONFIG_RUNNER // https://raw.githubusercontent.com/philsquared/Catch/master/single_includ #include "catch.hpp" #include <vector> 15
  26. Catch: C++ Automated Test Cases in Headers II SCENARIO("vectors' elements

    can be added", "[vector]") { GIVEN("A vector with some items") { const std::vector<int> v {0, 1, 2, 3, 4}; REQUIRE(v.size() == 5); REQUIRE(v.capacity() >= 5); WHEN("the elements' values are summed up") { auto sum = 0; for (auto element : v) sum += element; THEN("the result is their sum" ) { REQUIRE(sum == 0 + 1 + 2 + 3 + 4); // is my code correct? REQUIRE(sum == 10); // what's the result? 16
  27. Catch: C++ Automated Test Cases in Headers III } }

    } } // [[Rcpp::export]] int run_tests() { return Catch::Session().run(); } /*** R run_tests() */ 17
  28. Catch: C++ Automated Test Cases in Headers IV > run_tests()

    ===================================================== All tests passed (4 assertions in 1 test case) [1] 0 18
  29. Catch: C++ Automated Test Cases in Headers: Report I //

    [[Rcpp::plugins(cpp11)]] #define STRICT_R_HEADERS // https://github.com/philsquared/Catch/blob/master/docs/configuration.md #define CATCH_CONFIG_RUNNER // https://raw.githubusercontent.com/philsquared/Catch/master/single_includ #include "catch.hpp" #include <vector> 19
  30. Catch: C++ Automated Test Cases in Headers: Report II SCENARIO("vectors'

    elements can be added", "[vector]") { GIVEN("A vector with some items") { const std::vector<int> v {0, 1, 2, 3, 4}; REQUIRE(v.size() == 5); REQUIRE(v.capacity() >= 5); WHEN("the elements' values are summed up") { auto sum = 0; for (auto element : v) sum *= element; // uh-oh THEN("the result is their sum" ) { REQUIRE(sum == 0 + 1 + 2 + 3 + 4); // is my code correct? } 20
  31. Catch: C++ Automated Test Cases in Headers: Report III }

    } } // [[Rcpp::export]] int run_tests() { return Catch::Session().run(); } /*** R run_tests() */ 21
  32. Catch: C++ Automated Test Cases in Headers: Report IV >

    run_tests() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ is a Catch v1.1 b14 (develop) host application. Run with -? for options ----------------------------------------------------- Scenario: vectors' elements can be added Given: A vector with some items When: the elements' values are summed up Then: the result is their sum ----------------------------------------------------- catch_vector_sum.cpp:10 ..................................................... 22
  33. Catch: C++ Automated Test Cases in Headers: Report V catch_vector_sum.cpp:27:

    FAILED: REQUIRE( sum == 0 + 1 + 2 + 3 + 4 ) with expansion: 0 == 10 ===================================================== test cases: 1 | 1 failed assertions: 3 | 2 passed | 1 failed [1] 1 23
  34. Catch: C++ Automated Test Cases in Headers: Configurability I //

    [[Rcpp::plugins(cpp11)]] #define STRICT_R_HEADERS // https://github.com/philsquared/Catch/blob/master/docs/configuration.md #define CATCH_CONFIG_RUNNER // https://raw.githubusercontent.com/philsquared/Catch/master/single_includ #include "catch.hpp" #include <cstddef> #include <vector> 24
  35. Catch: C++ Automated Test Cases in Headers: Configurability II SCENARIO("vectors'

    elements can be added", "[vector]") { GIVEN("A vector with some items") { const std::vector<int> v {0, 1, 2, 3, 4}; REQUIRE(v.size() == 5); REQUIRE(v.capacity() >= 5); WHEN("the elements' values are summed up") { auto sum = 0; for (auto element : v) sum += element; THEN("the result is their sum" ) { REQUIRE(sum == 0 + 1 + 2 + 3 + 4); } 25
  36. Catch: C++ Automated Test Cases in Headers: Configurability III }

    } } // [[Rcpp::export]] int run_tests_nocfg() { return Catch::Session().run(); } // [[Rcpp::export]] int run_tests_cfg(const std::vector<std::string> & args) { const auto argc = args.size(); std::vector<const char *> argv(argc); for (std::size_t i = 0; i != argc; ++i) argv[i] = (args[i]).c_str(); return Catch::Session().run(argc, argv.data()); } 26
  37. Catch: C++ Automated Test Cases in Headers: Configurability IV /***

    R # Showing results for successful tests run_tests_cfg(c("rcpp_testing_app", "--success")) # run_tests_nocfg() # run_tests_cfg(c("rcpp_testing_app", "-?")) # run_tests_cfg(c("rcpp_testing_app", "--list-tests")) # run_tests_cfg(c("rcpp_testing_app", "--list-tags")) # run_tests_cfg(c("rcpp_testing_app", "--list-reporters")) # run_tests_cfg(c("rcpp_testing_app", "[vector]")) */ 27
  38. Catch: C++ Automated Test Cases in Headers: Configurability V >

    run_tests_cfg(c("rcpp_testing_app", "--success")) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ rcpp_testing_app is a Catch v1.1 b14 (develop) host application. Run with -? for options --------------------------------------------------------------------------- Scenario: vectors' elements can be added Given: A vector with some items --------------------------------------------------------------------------- catch_vector_sum_cfg.cpp:11 ........................................................................... catch_vector_sum_cfg.cpp:17: PASSED: REQUIRE( v.size() == 5 ) with expansion: 5 == 5 28
  39. Catch: C++ Automated Test Cases in Headers: Configurability VI catch_vector_sum_cfg.cpp:18:

    PASSED: REQUIRE( v.capacity() >= 5 ) with expansion: 5 >= 5 29
  40. VII --------------------------------------------------------------------------- Scenario: vectors' elements can be added Given: A

    vector with some items When: the elements' values are summed up Then: the result is their sum --------------------------------------------------------------------------- catch_vector_sum_cfg.cpp:11 ........................................................................... catch_vector_sum_cfg.cpp:28: PASSED: REQUIRE( sum == 0 + 1 + 2 + 3 + 4 ) with expansion: 10 == 10 =========================================================================== All tests passed (3 assertions in 1 test case) [1] 0 30
  41. Catch: C++ Automated Test Cases in Headers Test-driven C++ with

    Catch - Phil Nash @ NDC Oslo 2015 https://vimeo.com/131632252 Header only C++14 mocking framework https://github.com/rollbear/trompeloeil Report to test frameworks: Catch! https://github.com/rollbear/trompeloeil/#catch 32
  42. Catch: Revisiting Fibonacci Numbers I // [[Rcpp::plugins("cpp11")]] #define STRICT_R_HEADERS #define

    CATCH_CONFIG_RUNNER #include "catch.hpp" #include <cstdint> // type-driven programming // ensures non-negative numbers using uint = std::uint_fast64_t; // [[Rcpp::export]] uint fibonacci(const uint n) { if (n < 2) return n; // `n` non-negative return fibonacci(n - 1) + fibonacci(n - 2); } 33
  43. Catch: Revisiting Fibonacci Numbers II // [[Rcpp::export]] int run_tests() {

    return Catch::Session().run(); } TEST_CASE("Fibonacci numbers computed correctly", "[fibonacci]") { REQUIRE(fibonacci(0) == 0); REQUIRE(fibonacci(1) == 1); REQUIRE(fibonacci(2) == 1); REQUIRE(fibonacci(3) == 2); REQUIRE(fibonacci(4) == 3); REQUIRE(fibonacci(5) == 5); REQUIRE(fibonacci(6) == 8); REQUIRE(fibonacci(35) == 9227465); REQUIRE(fibonacci(42) == 267914296); } 34
  44. Catch: Revisiting Fibonacci Numbers III /*** R run_tests() */ >

    run_tests() ===================================================== All tests passed (9 assertions in 1 test case) [1] 0 35
  45. Serial Computing // [[Rcpp::export]] uint fibonacci_serial(const uint n) { if

    (n < 2) return n; return fibonacci_serial(n - 1) + fibonacci_serial(n - 2); } 37
  46. Parallel Computing: std::async #include <future> // [[Rcpp::export]] uint fibonacci_parallel_async(const uint

    n) { if (n < 2) return n; constexpr auto launch_policy = std::launch::async; auto future_partial_result_1 = async(launch_policy, fibonacci_serial, n - 1); auto future_partial_result_2 = async(launch_policy, fibonacci_serial, n - 2); return future_partial_result_1.get() + future_partial_result_2.get(); } 38
  47. Parallel Computing: boost::async #define BOOST_THREAD_PROVIDES_VARIADIC_THREAD #define BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK #include <boost/thread.hpp> #include

    <boost/thread/future.hpp> // [[Rcpp::export]] uint fibonacci_parallel_async(const uint n) { if (n < 2) return n; constexpr auto launch_policy = boost::launch::async; auto future_partial_result_1 = async(launch_policy, fibonacci_serial, n - 1); auto future_partial_result_2 = async(launch_policy, fibonacci_serial, n - 2); return future_partial_result_1.get() + future_partial_result_2.get(); } 39
  48. Parallel Computing: async, performance, microbenchmark /*** R require("microbenchmark") microbenchmark( fibonacci_serial(35),

    fibonacci_parallel_async(35) ) */ Unit: milliseconds expr median neval fibonacci_serial(35) 36.78976 100 fibonacci_parallel_async(35) 22.35909 100 41
  49. Parallel Computing: async, performance, Boost.Timer I #include <boost/timer/timer.hpp> // [[Rcpp::export]]

    void run() { const uint N = 42; std::cout << "N = " << N << '\n'; { boost::timer::auto_cpu_timer timer; std::cout << "fibonacci_serial: " << fibonacci_serial(N) << '\n'; } { boost::timer::auto_cpu_timer timer; std::cout << "fibonacci_parallel_async: " << fibonacci_parallel_async(N) << '\n'; } } 42
  50. Parallel Computing: async, performance, Boost.Timer II /*** R run() */

    N = 42 fibonacci_serial: 267914296 1.066444s wall, 1.078125s user + 0.000000s system = 1.078125s CPU (101.1%) fibonacci_parallel_async: 267914296 0.667700s wall, 1.046875s user + 0.000000s system = 1.046875s CPU (156.8%) 43
  51. Compile-Time Computing: constexpr http://en.cppreference.com/w/cpp/language/constexpr constexpr uint fibonacci_recursive_constexpr(const uint n) {

    return n < 2 ? n : (fibonacci_recursive_constexpr(n - 1) + fibonacci_recursive_constexpr(n - 2)); } // [[Rcpp::export]] void run() { const uint N = 42; std::cout << "N = " << N << '\n'; { boost::timer::auto_cpu_timer timer; constexpr uint result = fibonacci_recursive_constexpr(N); std::cout << "fibonacci_recursive_constexpr: " << result << std::endl; } } 44
  52. Compile-Time Computing: constexpr — Performance N = 42 fibonacci_serial: 267914296

    1.066444s wall, 1.078125s user + 0.000000s system = 1.078125s CPU (101.1%) fibonacci_parallel_async: 267914296 0.667700s wall, 1.046875s user + 0.000000s system = 1.046875s CPU (156.8%) fibonacci_recursive_constexpr: 267914296 0.000012s wall, 0.000000s user + 0.000000s system = 0.000000s CPU (n/a%) 45
  53. Compile-Time Computing: constexpr — Assembly GCC Explorer: https://gcc.godbolt.org/ — awesome

    for finding things out! #include <cstdint> using uint = std::uint_fast64_t; // body: as before constexpr uint fibonacci_recursive_constexpr(const uint n); int main() { const uint N = 27; constexpr uint result = fibonacci_recursive_constexpr(N); return result; } Generated assembly code: // https://goo.gl/3w3xeb main: # @main mov eax, 196418 ret Recall: F27 = 196, 418 46
  54. Right-Algorithm Computing uint fibonacci_serial_faster(const uint N) { if (N <

    2) return N; uint F_n_1 = 0; // F(n - 1) uint F_n_2 = 1; // F(n - 2) uint F_n = 0; // F(n) for (uint n = 1; n <= N; ++n) { F_n = F_n_1 + F_n_2; F_n_2 = F_n_1; F_n_1 = F_n; } return F_n; } fibonacci_serial_faster: 267914296 0.000010s wall, 0.000000s user + 0.000000s system = 0.000000s CPU (n/a%) 47
  55. Right-Algorithm Computing — Assembly GCC Explorer: https://gcc.godbolt.org/ — really awesome

    for finding things out! // as before constexpr uint fibonacci_recursive_constexpr(const uint n); uint fibonacci_serial_faster(const uint N); int main() { const uint N = 27; constexpr uint result = fibonacci_recursive_constexpr(N); const uint result2 = fibonacci_serial_faster(N); return result + result2; } Generated assembly code: // https://goo.gl/ZmZ6nj main: # @main mov eax, 392836 ret Indeed: F27 + F27 = 392, 836 48
  56. Use Cases • std::async - ideally suited for task-parallel applications

    • coming in C++17: easily composable futures, will help a lot 49
  57. Use Cases • std::async - ideally suited for task-parallel applications

    • coming in C++17: easily composable futures, will help a lot • OpenMP - ideally suited for data-parallel applications 49
  58. Use Cases • std::async - ideally suited for task-parallel applications

    • coming in C++17: easily composable futures, will help a lot • OpenMP - ideally suited for data-parallel applications • constexpr - ideally suited for precomputing constants (replacing magic numbers with algorithms) 49
  59. Parallel Computing: async — Learn More Item 35: Prefer task-based

    programming to thread-based. Scott Meyers (2014) "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14" Futures for C++11 at Facebook: https://code.facebook.com/posts/1661982097368498 Standard http://en.cppreference.com/w/cpp/thread/async http://en.cppreference.com/w/cpp/thread/future http://en.cppreference.com/w/cpp/language/constexpr Boost http://boost.org/libs/thread http://theboostcpplibraries.com/boost.thread http://www.boost.org/doc/libs/master/doc/html/thread/build.html#thread.b http://www.boost.org/doc/libs/master/doc/html/thread/synchronization.htm 50
  60. Numerics & Parallel Computing: Monte Carlo • Problem: • want

    to obtain: θ = E[Zmoment] = ∫ Ω Z(ω)moment d P(ω), Z ∈ L1(P) • Solution: • Take ˆ θM = ZM = 1 M ∑M m=1 Zm(ω)moment, where Zm iid ∼ Law(Z) • SLLN ⇒ ˆ θM a.s. − − → θ for M → ∞ • Reference: http://planetmath.org/montecarlosimulation 52
  61. MC Example II // [[Rcpp::export]] double MC_moment(double moment, std::size_t M)

    { // source of randomness using Engine = std::mt19937; const auto seed = 1; Engine omega(seed); // statistical distribution using Normal = std::normal_distribution<>; const Normal Z(0, 1); double sum = 0.0; for (std::size_t m = 0; m != M; ++m) sum += std::pow(Z(omega), moment); const auto average = sum / M; return average; } 54
  62. MC Example III /*** R MC_moment(moment = 4.0, M =

    100L) MC_moment(moment = 4.0, M = 10000L) MC_moment(moment = 4.0, M = 1000000L) */ > MC_moment(moment = 4.0, M = 100L) [1] 3.315409 > MC_moment(moment = 4.0, M = 10000L) [1] 3.019765 > MC_moment(moment = 4.0, M = 1000000L) [1] 2.992081 https://en.wikipedia.org/wiki/Normal_distribution#Moments 55
  63. PRNG Example I #include <cstddef> // std::size_t #include <functional> //

    std::bind, std::ref #include <iostream> #include <random> int main() { const auto seed = 1; // worth a talk on its own: // http://www.pcg-random.org/posts/cpp-seeding-surprises.html // Random number engine: Mersenne twister MT19937 // see: http://en.wikipedia.org/wiki/Mersenne_twister // see: http://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_ using Engine = std::mt19937; Engine omega(seed); std::cout << "omega() = " << omega() << "\n"; 56
  64. PRNG Example II // Random number distribution: normal_distribution // see:

    http://en.cppreference.com/w/cpp/numeric/random/normal_distributi //using Normal = std::normal_distribution<double>; using Normal = std::normal_distribution<>; const Normal Z(0, 1); // draw a realization std::cout << "Z(omega) = " << Z(omega) << "\n"; // create a realization (random variate) generator `z` // by binding (by reference) `Z`'s argument to `omega` // see: http://en.cppreference.com/w/cpp/utility/functional/bind // see: http://en.cppreference.com/w/cpp/utility/functional/ref // don't: copy would result in identical outcomes // auto z = std::bind(Z, omega); 57
  65. PRNG Example III auto z = std::bind(Z, std::ref(omega)); // draw

    a realization std::cout << "z = " << z() << "\n"; // create a vector of doubles `x`, initially with size of M (elements) // http://en.cppreference.com/w/cpp/container/vector const std::size_t M = 10; std::vector<double> x(M); // overwrite each element of vector `x` with consecutive realizations // see: http://en.cppreference.com/w/cpp/language/range-for for (auto & x_m : x) x_m = z(); // print out vector `x` and its size std::cout << "x: \n"; for (auto x_m : x) std::cout << x_m << " "; endl(std::cout); std::cout << "x.size() = " << x.size() << "\n"; 58
  66. PRNG Example IV // grow vector `x` by 5 new

    consecutive realizations, // emplacing each one on the back // see: http://en.cppreference.com/w/cpp/container/vector/emplace_back for (std::size_t counter = 0; counter != 5; ++counter) x.push_back(z()); // print out vector `x` and its size std::cout << "x: \n"; for (auto x_m : x) std::cout << x_m << " "; endl(std::cout); std::cout << "x.size() = " << x.size() << "\n"; return 0; } 59
  67. C++11 <random> & Boost.Random — Learn More • Tutorial: http://isocpp.org/blog/2013/03/n3551-random-

    number-generation • Reference: • http://en.cppreference.com/w/cpp/numeric • http://en.cppreference.com/w/cpp/numeric/random • Boost.Random: http://boost.org/libs/random • Examples: • BH: http://gallery.rcpp.org/articles/using-boost-with-bh/ • http://theboostcpplibraries.com/boost.random • "C++ Primer" — Chapter 17, Section 17.4 (Random Numbers) 60
  68. C++11 <random> & Boost.Random — Caveats • Reproducibility: • implementations

    of PRNGs (e.g., std::mt19937) standardized — no problems 61
  69. C++11 <random> & Boost.Random — Caveats • Reproducibility: • implementations

    of PRNGs (e.g., std::mt19937) standardized — no problems • however, statistical distributions (e.g., std::normal_distribution) allowed algorithm choice 61
  70. C++11 <random> & Boost.Random — Caveats • Reproducibility: • implementations

    of PRNGs (e.g., std::mt19937) standardized — no problems • however, statistical distributions (e.g., std::normal_distribution) allowed algorithm choice • if reproducibility crucial, consider Boost.Random for identical implementation (i.e., that of Boost.Random) guarantee 61
  71. C++11 <random> & Boost.Random — Caveats • Reproducibility: • implementations

    of PRNGs (e.g., std::mt19937) standardized — no problems • however, statistical distributions (e.g., std::normal_distribution) allowed algorithm choice • if reproducibility crucial, consider Boost.Random for identical implementation (i.e., that of Boost.Random) guarantee • Seeding: http://www.pcg-random.org/posts/cpp-seeding-surprises.html 61
  72. C++11 <random> & Boost.Random — Caveats • Reproducibility: • implementations

    of PRNGs (e.g., std::mt19937) standardized — no problems • however, statistical distributions (e.g., std::normal_distribution) allowed algorithm choice • if reproducibility crucial, consider Boost.Random for identical implementation (i.e., that of Boost.Random) guarantee • Seeding: http://www.pcg-random.org/posts/cpp-seeding-surprises.html • Parallel computing: Random123 worth investigating — https://www.deshawresearch.com/resources_ random123.html 61
  73. Parallel Computing: OpenMP OpenMP (Open Multi-Processing) • Book: "Using OpenMP"

    • Tim Mattson's (2013) "Introduction to OpenMP" (Intel Corp.) • YouTube Playlist: http://tinyurl.com/OpenMP-Tutorial • Slides (PDF): A Hands-on Introduction to OpenMP • Exercise files: Mattson_OMP_exercises.zip • Compiler support: • MSVC: OpenMP 2.0 since Visual C++ 2005, editions: Community (free) and up • GCC: as of GCC 4.2 (Nov, 2005) the compiler implements version 2.5 of the OpenMP standard -- and as of GCC 4.7 (2011) it implements version 3.1 of the OpenMP standard • Clang: OpenMP support in Clang compiler is completed! http://blog.llvm.org/2015/05/openmp-support_22.html 62
  74. Parallel Computing: OpenMP: Hello World // [[Rcpp::plugins("cpp11")]] #include <omp.h> #include

    <iostream> // [[Rcpp::export]] void omp_hi() { #pragma omp parallel { const auto thread_id = omp_get_thread_num(); std::cout << "Hello World from thread " << thread_id << ".\n"; #pragma omp barrier if (thread_id == 0) { const auto threads_count = omp_get_num_threads(); std::cout << "There are " << threads_count << " threads.\n"; } } return; } 63
  75. Parallel Computing: OpenMP: Hello World: Execution > omp_hi() Hello World

    from thread Hello World from thread 31Hello 4. . Hello World from thread 11Hello World from thread . 9Hello World from thread . 10Hello World from thread . 2Hello World from thread . 6Hello World from thread . 5Hello World from thread . 8. Hello World from thread 7Hello World from thread . 0. There are 12 threads. 64
  76. Parallel Computing: Race Conditions Unsynchronized Shared Mutable State — Data

    Races Risks Can reduce / eliminate by: • synchronization: barrier, reduction — may cost performance1, may be necessary 1Main enemies: SLOW factors (Starvation-Latency-Overhead-Waiting) http://www.orau.gov/archII2011/presentations/sterling_t.pdf 66
  77. Parallel Computing: Race Conditions Unsynchronized Shared Mutable State — Data

    Races Risks Can reduce / eliminate by: • synchronization: barrier, reduction — may cost performance1, may be necessary • not sharing: use default(none) (always!), prefer private / thread-local state; even copying — but it may be costly 1Main enemies: SLOW factors (Starvation-Latency-Overhead-Waiting) http://www.orau.gov/archII2011/presentations/sterling_t.pdf 66
  78. Parallel Computing: Race Conditions Unsynchronized Shared Mutable State — Data

    Races Risks Can reduce / eliminate by: • synchronization: barrier, reduction — may cost performance1, may be necessary • not sharing: use default(none) (always!), prefer private / thread-local state; even copying — but it may be costly • immutability: const correctness is a life saver — safe to be shared 1Main enemies: SLOW factors (Starvation-Latency-Overhead-Waiting) http://www.orau.gov/archII2011/presentations/sterling_t.pdf 66
  79. Parallel Computing: Race Conditions Unsynchronized Shared Mutable State — Data

    Races Risks Can reduce / eliminate by: • synchronization: barrier, reduction — may cost performance1, may be necessary • not sharing: use default(none) (always!), prefer private / thread-local state; even copying — but it may be costly • immutability: const correctness is a life saver — safe to be shared • diagnosing: ThreadSanitizer 1Main enemies: SLOW factors (Starvation-Latency-Overhead-Waiting) http://www.orau.gov/archII2011/presentations/sterling_t.pdf 66
  80. Parallel Computing: OpenMP: MC Reduction I // [[Rcpp::plugins("cpp11")]] #include <omp.h>

    #include <cmath> // std::pow #include <cstddef> // std::size_t #include <functional> // std::bind, std::ref #include <random> 67
  81. Parallel Computing: OpenMP: MC Reduction II // [[Rcpp::export]] double MC_moment(double

    moment, std::size_t M) { using Engine = std::mt19937; const auto seed = 1; Engine omega(seed); using Normal = std::normal_distribution<>; const Normal Z(0, 1); auto z = std::bind(Z, std::ref(omega)); double sum = 0.0; for (std::size_t m = 0; m != M; ++m) sum += std::pow(z(), moment); const auto average = sum / M; return average; } 68
  82. Parallel Computing: OpenMP: MC Reduction III // [[Rcpp::export]] double MC_moment_omp_naive(const

    double moment, const std::size_t M) { using Engine = std::mt19937; const auto seed = 1; Engine omega(seed); using Normal = std::normal_distribution<>; const Normal Z(0, 1); auto z = std::bind(Z, omega); std::vector<double> zs(M); for (auto & zs_m : zs) zs_m = z(); double sum = 0.0; #pragma omp parallel for reduction(+ : sum) \ default(none) shared(zs) for (std::size_t m = 0; m < M; ++m) sum += std::pow(zs[m], moment); 69
  83. Parallel Computing: OpenMP: MC Reduction V // [[Rcpp::export]] double MC_moment_omp_inplace(const

    double moment, const std::size_t M) { using Engine = std::mt19937; using Normal = std::normal_distribution<>; double sum = 0.0; #pragma omp parallel default(none) reduction(+ : sum) { const auto seed = omp_get_thread_num(); Engine omega(seed); const Normal Z(0, 1); auto z = std::bind(Z, omega); #pragma omp for for (std::size_t m = 0; m < M; ++m) sum += std::pow(z(), moment); } const auto average = sum / M; 71
  84. Parallel Computing: OpenMP: MC Reduction VII MC_moment(moment = 4.0, M

    = 100L) MC_moment(moment = 4.0, M = 10000L) MC_moment(moment = 4.0, M = 1000000L) 3.315409 3.019765 2.992081 73
  85. Parallel Computing: OpenMP: MC Reduction VIII MC_moment_omp_naive(moment = 4.0, M

    = 100L) MC_moment_omp_naive(moment = 4.0, M = 10000L) MC_moment_omp_naive(moment = 4.0, M = 1000000L) 3.315409 3.019765 2.992081 74
  86. Parallel Computing: OpenMP: MC Reduction IX MC_moment_omp_inplace(moment = 4.0, M

    = 100L) MC_moment_omp_inplace(moment = 4.0, M = 10000L) MC_moment_omp_inplace(moment = 4.0, M = 1000000L) 2.724324 2.900593 2.995658 note: different data use order however: identical on each run (deterministic, reproducible) 75
  87. Parallel Computing: OpenMP: MC Reduction X require("microbenchmark") microbenchmark( MC_moment_omp_naive(moment =

    4.0, M = 1000000L), MC_moment_omp(moment = 4.0, M = 1000000L), MC_moment_omp_inplace(moment = 4.0, M = 1000000L), times = 10L ) Unit: milliseconds expr median neval MC_moment 108.57741 10 MC_moment_omp_naive 90.08847 10 MC_moment_omp_inplace 26.63601 10 76
  88. Parallel Computing: OpenMP: MC Reduction XI # identifying performance speed-up

    threshold benchM = 1000L # slower ... benchM = 10000L # faster microbenchmark( MC_moment(moment = 4.0, M = benchM), MC_moment_omp(moment = 4.0, M = benchM), MC_moment_omp_inplace(moment = 4.0, M = benchM), times = 100L ) 77
  89. Parallel Computing: OpenMP: MC Reduction XII // accounting for performance

    speed-up threshold #pragma omp parallel default(none) \ reduction(+ : sum) \ if (M >= 10000) • threads (creation/scheduling/management/destruction) incur overheads • avoid performance penalty by using multithreading only when it makes sense (e.g., enough data/work) • "when it makes sense"? what's "enough"? • initial answer: scaling theory — strong scaling (Amdahl’s Law) & weak scaling (Gustafson’s Law) • http://www.cs.columbia.edu/~martha/courses/4130/au12/ scaling-theory.pdf • follow-up: performance model — Work and Span, Speed-up and Parallelism 78
  90. Naturally Sweet Algorithms • Rich source of convenient one-liners for

    data manipulation: • http://en.cppreference.com/w/cpp/algorithm • http://www.boost.org/libs/range • https://github.com/ericniebler/range-v3 • "Range library for C++11/14/17. This code is the basis of a formal proposal to add range support to the C++ standard library." • http://ericniebler.com/ • @ben_deane's blog series: http://www.elbeno.com/blog/?p=1118 81
  91. Boost.Range: Algorithms I // [[Rcpp::plugins(cpp11)]] #include <Rcpp.h> // [[Rcpp::depends(BH)]] #include

    <boost/range/adaptor/transformed.hpp> #include <boost/range/numeric.hpp> #include <boost/range/algorithm.hpp> #include <boost/range/algorithm_ext.hpp> #include <algorithm> #include <chrono> #include <iostream> #include <numeric> #include <system_error> #include <thread> #include <vector> 82
  92. Boost.Range: Algorithms II // `meanC` based on: http://adv-r.had.co.nz/Rcpp.html#rcpp-sugar // [[Rcpp::export]]

    double meanC(Rcpp::NumericVector x) { const auto size = x.size(); auto sum = 0.0; for (int i = 0; i < size; ++i) sum += x[i]; const auto mean = sum / size; return mean; } // [[Rcpp::export]] double meanCb(Rcpp::NumericVector x) { const auto size = x.size(); const auto sum = boost::accumulate(x, 0.); const auto mean = sum / size; return mean; } 83
  93. Boost.Range: Algorithms III /*** R require("microbenchmark") x = runif(1e5) microbenchmark(

    mean(x), meanC(x), meanCb(x) ) Unit: microseconds expr mean median mean(x) 266.00357 261.004 meanC(x) 66.40360 64.674 meanCb(x) 65.73123 64.161 */ 84
  94. Boost.Range and Boost.Phoenix Demo I // [[Rcpp::plugins("cpp11")]] #include <algorithm> #include

    <cstddef> #include <cmath> #include <iostream> #include <numeric> #include <vector> // [[Rcpp::depends(BH)]] #include <boost/range/adaptor/transformed.hpp> #include <boost/range/numeric.hpp> #include <boost/range/algorithm.hpp> #include <boost/range/algorithm_ext.hpp> #include <boost/phoenix/core.hpp> #include <boost/phoenix/operator.hpp> #include <boost/phoenix/stl/cmath.hpp> 85
  95. Boost.Range and Boost.Phoenix Demo II // [[Rcpp::export]] void log_returns_example() {

    using vector = std::vector<double>; const std::size_t prices_count = 9; vector prices(prices_count); // 9 consecutive numbers starting from 10.0 boost::iota(prices, 10.); std::cout << "prices: "; for (auto p : prices) std::cout << p << ' '; endl(std::cout); // prices: 10 11 12 13 14 15 16 17 18 // not very interesting 86
  96. Boost.Range and Boost.Phoenix Demo III // let's rearrange them //

    note: not very random, std::shuffle better boost::random_shuffle(prices); std::cout << "prices: "; for (auto p : prices) std::cout << p << ' '; endl(std::cout); // prices: 16 13 10 14 15 17 11 18 12 // compute log-returns vector returns_(prices_count); using namespace boost::adaptors; using namespace boost::phoenix::arg_names; // "Range Adaptors are to algorithms what algorithms are to containers" // http://boost.org/doc/libs/master/libs/range/doc/html/range/reference/a boost::adjacent_difference(prices | transformed(log(arg1)), begin(returns_)); 87
  97. Boost.Range and Boost.Phoenix Demo IV std::cout << "returns?: "; for

    (auto r : returns_) std::cout << r << ' '; endl(std::cout); // returns?: // 2.77259 -0.207639 -0.262364 0.336472 0.0689929 0.125163 -0.435318 0.49 // need to skip the first one (undefined) const auto returns = boost::make_iterator_range( next(begin(returns_)), end(returns_)); std::cout << "returns: "; for (auto r : returns) std::cout << r << ' '; endl(std::cout); // returns: // -0.207639 -0.262364 0.336472 0.0689929 0.125163 -0.435318 0.492476 -0. 88
  98. Boost.Range and Boost.Phoenix Demo V const auto returns_sum = boost::accumulate(returns,

    0.); const auto returns_count = boost::size(returns); const auto mean = returns_sum / returns_count; std::cout << "prices_count = " << prices_count << '\n'; std::cout << "returns_count = " << returns_count << '\n'; std::cout << "mean = " << mean << '\n'; // prices_count = 9 // returns_count = 8 // mean = -0.0359603 const auto returns_demeaned = returns | transformed(arg1 - mean); std::cout << "returns_demeaned: "; for (auto r : returns_demeaned) std::cout << r << ' '; endl(std::cout); // returns_demeaned: // -0.171679 -0.226404 0.372432 0.104953 0.161123 -0.399358 0.528437 -0.3 89
  99. Boost.Range and Boost.Phoenix Demo VI const auto norm2 = boost::inner_product(returns_demeaned,

    returns_demeaned, 0.); std::cout << "norm2 = " << norm2 << '\n'; const auto variance = norm2 / (returns_count - 1); const auto standard_deviation = std::sqrt(variance); std::cout << "variance = " << variance << '\n'; std::cout << "standard_deviation = " << standard_deviation << '\n'; const auto iterator_max_return = boost::max_element(returns); std::cout << "max_return = " << *iterator_max_return << '\n'; const auto min_return = *boost::min_element(returns); std::cout << "min_return = " << min_return << '\n'; } 90
  100. Boost.Range and Boost.Phoenix Demo VII /*** R log_returns_example() */ mean

    = -0.0359603 norm2 = 0.83168 variance = 0.118811 standard_deviation = 0.34469 max_return = 0.492476 min_return = -0.435318 91
  101. More: Boost.Accumulators http://boost.org/libs/accumulators The Statistical Accumulators Library defines accumulators for

    incremental statistical computations: http://boost.org/doc/libs/master/doc/html/accumulators/user_s_gui Running (on-line) mean, max, median, quantiles, more! 92
  102. Recommendations • Recommendations: • data wrangling: C++11 algorithms, lambdas, and

    Boost (Phoenix, Range, StringAlgorithms, Tokenizer) • numerics & statistical analysis: Boost.Math (e.g., Statistical Distributions - moments, quantiles, statistical hypothesis testing) • linear algebra: http://eigen.tuxfamily.org/ • timing code: http://en.cppreference.com/w/cpp/chrono • useful containers in C++11 and Boost • http://en.cppreference.com/w/cpp/container • http://boost.org/libs/bimap • http://boost.org/libs/container - e.g., flat_(multi)map/set associative containers • Resources: • http://boost.org/doc/libs/?view=categorized • http://theboostcpplibraries.com/ 93
  103. Boost.Math: Univariate Minimization & Root-Finding I // [[Rcpp::plugins("cpp11")]] // [[Rcpp::depends(BH)]]

    #include <Rcpp.h> #include <cmath> #include <iostream> #include <tuple> // Boost.Math // Locating Function Minima: Brent's algorithm // http://www.boost.org/doc/libs/release/libs/math/doc/html/math_toolkit/in #include <boost/math/tools/minima.hpp> double f(double x, double a) { return (x - a) * (x - a); } 94
  104. Boost.Math: Univariate Minimization & Root-Finding II // http://www.boost.org/doc/libs/release/libs/math/doc/html/math_toolkit/po // [[Rcpp::export]]

    int maximum_feasible_digits() { using T = double; using boost::math::policies::digits; using boost::math::policies::policy; // at most one half of the bits in type T return digits<T, policy<>>() / 2; } // [[Rcpp::export]] double feasible_tolerance(int bits) { using T = double; const auto tolerance = static_cast<T>(std::ldexp(1.0, 1 - bits)); return tolerance; } 95
  105. Boost.Math: Univariate Minimization & Root-Finding III // [[Rcpp::export]] Rcpp::List minimize_example(double

    interval_min, double interval_max, int digits) { const auto a = 1. / 3.; // http://en.cppreference.com/w/cpp/language/lambda const auto univariate_f = [a](double x) { return f(x, a); }; using boost::math::tools::brent_find_minima; const auto result = brent_find_minima(univariate_f, interval_min, interval_max, digits); auto minimum = 0.0, objective = 0.0; // http://en.cppreference.com/w/cpp/utility/tuple/tie std::tie(minimum, objective) = result; std::cout << '(' << minimum << ", " << objective << ")\n"; 96
  106. Boost.Math: Univariate Minimization & Root-Finding IV using Rcpp::Named; return Rcpp::List::create(

    Named("minimum") = minimum, Named("objective") = objective); } 97
  107. Boost.Math: Univariate Minimization & Root-Finding V In R: interval_min =

    0.0 interval_max = 1.0 digits = 7 minimize_example(interval_min, interval_max, digits) # (0.333333, 0) # $minimum # [1] 0.3333333 # $objective # [1] 0 98
  108. C++14 • https://isocpp.org/wiki/faq/cpp14 http://herbsutter.com/elements-of-modern-c-style/ http://herbsutter.com/gotw/ • https://isocpp.org/wiki/faq/cpp14-language • https://isocpp.org/wiki/faq/cpp14-language#generalized-return auto

    f() { return 42; } • https://isocpp.org/wiki/faq/cpp14-language#generic-lambdas auto size = [](const auto m) { return m.size(); }; • https://isocpp.org/wiki/faq/cpp14-language#variable-templates template<typename T> constexpr T pi = T(3.14159265358979323846); • https://isocpp.org/wiki/faq/cpp14-language#extended-constexpr • https://isocpp.org/wiki/faq/cpp14-library • https://isocpp.org/wiki/faq/cpp14-library#make-unique std::make_unique - worth considering as a replacement for new 100
  109. C++ in 2017 As of June 29, 2015 (trip report

    following the May 2015 meeting): "5 Technical Specifications that were published or potentially publishable following this meeting. These are all now all possible C++17 candidates. They are: Library Fundamentals, Parallelism, Concurrency (still needs final ballot), Transactional Memory, Concepts (still needs final telecon call). All have been approved to publish or with provisions for a final pass. This is in addition to File Systems which was approved previously." Source: Michael Wong: "On Games (SG14) and Transactional Memory (SG5) from The View at the May 2015 C++ Standard meeting in Lenexa" 102
  110. Resources: What's new right now — Trip Reports I •

    Botond Ballo: C++ Standards Meeting in Lenexa, May 2015 https://botondballo.wordpress.com/2015/06/05/ trip-report-c-standards-meeting-in-lenexa-may-2015/ • Herb Sutter: Spring ISO C++ meeting https://isocpp.org/blog/2015/06/ trip-report-spring-iso-cpp-meeting • Jason Merrill: Lenexa C++ Meeting Report (Core Language) https://developerblog.redhat.com/2015/06/10/ lenexa-c-meeting-report-core-language/ • Torvald Riegel: Red Hat at the ISO C++ Standards Meeting (Parallelism and Concurrency) https://developerblog.redhat.com/2015/06/16/ red-hat-at-the-iso-c-standards-meeting-may-2015-parallelism-a 104
  111. Resources: What's new right now — Trip Reports II •

    Michael Wong: On Games (SG14) and Transactional Memory (SG5) from The View at the May 2015 C++ Standard meeting in Lenexa https://www.ibm.com/developerworks/community/blogs/ 5894415f-be62-4bc0-81c5-3956e82276f3/entry/The_view_from_ the_May_2015_C_Standard_meeting 105
  112. Resources: What's next • Standard C++ — Current Status: https://isocpp.org/std/status

    • Thoughts About C++17 — Bjarne Stroustrup: https://isocpp.org/blog/2015/04/d4492 106
  113. Resources: Where to learn more • Start here: https://isocpp.org/get-started •

    Books: http://stackoverflow.com/questions/388242/ the-definitive-c-book-guide-and-list • https://isocpp.org/wiki/faq • https://isocpp.org/std/the-standard • http://cppreference.com/ • http://cppsamples.com/ • Best Practices: • http://cppbestpractices.com/ • Examples Of Best Practice For C++ 11/14 Libraries: https://svn. boost.org/trac/boost/wiki/BestPracticeHandbook • Exception-Safe Coding: http://exceptionsafecode.com/ • Performance: http://agner.org/optimize/#manuals 107
  114. Resources: How to stay up to date News • https://isocpp.org/

    • http://cppcast.com/ • https://reddit.com/r/cpp/ 109
  115. Resources: How to stay up to date Conferences • CppCon:

    http://cppcon.org/ • Videos: https://channel9.msdn.com/Events/CPP/ • C++Now, BoostCon: https://github.com/boostcon/ • GoingNative: https://channel9.msdn.com/events/GoingNative/ • Meeting C++: http://meetingcpp.com/ https://youtube.com/user/MeetingCPP • User Groups: http://meetingcpp.com/index.php/user-groups.html • NDC Conferences: https://vimeo.com/ndcconferences/collections 110
  116. Compilers Support Status GCC, libstdc++ https://gcc.gnu.org/projects/cxx1y.html https://gcc.gnu.org/onlinedocs/libstdc++/manual/ status.html C++11/14/17 Features

    In VS 2015 RTM http://blogs.msdn.com/b/vcblog/archive/2015/06/ 19/c-11-14-17-features-in-vs-2015-rtm.aspx Clang, libc++ http://clang.llvm.org/cxx_status.html http://libcxx.llvm.org/cxx1z_status.html http://libcxx.llvm.org/cxx1y_status.html http://libcxx.llvm.org/ts1z_status.html 111
  117. Resources: Parallel Computing I • Herb Sutter (2004) "The Free

    Lunch Is Over: A Fundamental Turn Toward Concurrency in Software" • Article (2009 Update): http: //www.gotw.ca/publications/concurrency-ddj.htm • Slides: http://www.gotw.ca/resources/Software%20and% 20Concurrency%20-%20OGDC.pdf • "C++11 Concurrency" - Hands-on Tutorial by Bartosz Milewski • YouTube Playlist: http://tinyurl.com/cpp11-thread • Herb Sutter (2012) "Welcome to the Jungle" • Article: http://herbsutter.com/welcome-to-the-jungle/ 113
  118. Resources: Parallel Computing II • Anthony Williams (2012) "C++ Concurrency

    in Action: Practical Multithreading" • Chapter 1: Hello, world of concurrency in C++! http://www.manning.com/williams/sample_ch01_CCiA.pdf • Chapter 2: Managing threads http://www.manning.com/williams/sample_ch02_CCiA.pdf • Concurrency, Parallelism, Multithreading: http://en.cppreference.com/w/cpp/thread 114
  119. Resources: Parallel Computing ∞ James Mickens (2013) "The Slow Winter"

    ;login: / logout, September 2013, The USENIX Magazine https://www.usenix.org/system/files/1309_14-17_mickens.pdf 115