Squashed 'third_party/marl/' changes from d3b8558ce..59068ee4c 59068ee4c examples/fractal.cpp: Don't use rand() 7df53dd16 Add primes example ded37ceb8 Update README.md with build instructions b80c797df CMakeLists.txt: Don't default to building tests. d89fe34b6 CMakeLists.txt: Use explicit file lists 757566df0 Update README.md 8c98371e5 Presubmits: Enable warnings-as-errors. 0025389a1 CMakeLists: Add option to treat warnings as errors eeb070293 Fix all compiler warnings git-subtree-dir: third_party/marl git-subtree-split: 59068ee4cf1f5ff5e691ff010c8d83b5f862c4fa
diff --git a/CMakeLists.txt b/CMakeLists.txt index f9dd79f..f3ab6d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -21,14 +21,15 @@ ########################################################### # Options ########################################################### +option(MARL_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) option(MARL_BUILD_EXAMPLES "Build example applications" OFF) -option(MARL_BUILD_TESTS "Build tests" ON) +option(MARL_BUILD_TESTS "Build tests" OFF) option(MARL_ASAN "Build marl with address sanitizer" OFF) option(MARL_TSAN "Build marl with thread sanitizer" OFF) -if (MARL_ASAN AND MARL_TSAN) +if(MARL_ASAN AND MARL_TSAN) message(FATAL_ERROR "MARL_ASAN and MARL_TSAN are mutually exclusive") -endif (MARL_ASAN AND MARL_TSAN) +endif(MARL_ASAN AND MARL_TSAN) ########################################################### # Directories @@ -52,21 +53,40 @@ ########################################################### # File lists ########################################################### -file(GLOB MARL_FULL_LIST - ${MARL_SRC_DIR}/*.cpp - ${MARL_SRC_DIR}/*.h - ${MARL_SRC_DIR}/*.c +file(GLOB MARL_LIST + ${MARL_SRC_DIR}/debug.cpp + ${MARL_SRC_DIR}/scheduler.cpp + ${MARL_SRC_DIR}/thread.cpp + ${MARL_SRC_DIR}/trace.cpp + ${MARL_SRC_DIR}/osfiber_aarch64.c + ${MARL_SRC_DIR}/osfiber_arm.c + ${MARL_SRC_DIR}/osfiber_ppc64.c + ${MARL_SRC_DIR}/osfiber_x64.c + ${MARL_SRC_DIR}/osfiber_x86.c ) - -if (NOT MSVC) - file(GLOB MARL_ASSEMBLY_LIST ${MARL_SRC_DIR}/*.S) - list(APPEND MARL_FULL_LIST ${MARL_ASSEMBLY_LIST}) +if(NOT MSVC) + list(APPEND MARL_LIST + ${MARL_SRC_DIR}/osfiber_asm_aarch64.S + ${MARL_SRC_DIR}/osfiber_asm_arm.S + ${MARL_SRC_DIR}/osfiber_asm_ppc64.S + ${MARL_SRC_DIR}/osfiber_asm_x64.S + ${MARL_SRC_DIR}/osfiber_asm_x86.S + ) endif(NOT MSVC) -set(MARL_LIST ${MARL_FULL_LIST}) -set(MARL_TEST_LIST ${MARL_FULL_LIST}) -list(FILTER MARL_LIST EXCLUDE REGEX ".*_test\\..*") -list(FILTER MARL_TEST_LIST INCLUDE REGEX ".*_test\\..*") +file(GLOB MARL_TEST_LIST + ${MARL_SRC_DIR}/blockingcall_test.cpp + ${MARL_SRC_DIR}/conditionvariable_test.cpp + ${MARL_SRC_DIR}/containers_test.cpp + ${MARL_SRC_DIR}/defer_test.cpp + ${MARL_SRC_DIR}/marl_test.cpp + ${MARL_SRC_DIR}/marl_test.h + ${MARL_SRC_DIR}/osfiber_test.cpp + ${MARL_SRC_DIR}/pool_test.cpp + ${MARL_SRC_DIR}/scheduler_test.cpp + ${MARL_SRC_DIR}/ticket_test.cpp + ${MARL_SRC_DIR}/waitgroup_test.cpp +) ########################################################### # OS libraries @@ -80,23 +100,51 @@ endif() ########################################################### +# Functions +########################################################### + +function(marl_set_target_options target) + # Enable all warnings + if(NOT MSVC) + target_compile_options(${target} PRIVATE "-Wall") + endif() + + # Disable specific, pedantic warnings + if(MSVC) + target_compile_options(${target} PRIVATE "-D_CRT_SECURE_NO_WARNINGS") + endif() + + # Treat all warnings as errors + if(MARL_WARNINGS_AS_ERRORS) + if(MSVC) + target_compile_options(${target} PRIVATE "/WX") + else() + target_compile_options(${target} PRIVATE "-Werror") + endif() + endif(MARL_WARNINGS_AS_ERRORS) + + if(MARL_ASAN) + target_compile_options(${target} PUBLIC "-fsanitize=address") + target_link_libraries(${target} "-fsanitize=address") + elseif(MARL_MSAN) + target_compile_options(${target} PUBLIC "-fsanitize=memory") + target_link_libraries(${target} "-fsanitize=memory") + endif() + + target_include_directories(${target} PRIVATE ${MARL_INCLUDE_DIR}) +endfunction(marl_set_target_options) + +########################################################### # Targets ########################################################### # marl add_library(marl STATIC ${MARL_LIST}) set_target_properties(marl PROPERTIES - INCLUDE_DIRECTORIES "${MARL_INCLUDE_DIR}" POSITION_INDEPENDENT_CODE 1 ) -if (MARL_ASAN) - target_compile_options(marl PUBLIC "-fsanitize=address") - target_link_libraries(marl "-fsanitize=address") -elseif (MARL_MSAN) - target_compile_options(marl PUBLIC "-fsanitize=memory") - target_link_libraries(marl "-fsanitize=memory") -endif () +marl_set_target_options(marl) target_link_libraries(marl "${MARL_OS_LIBS}") @@ -111,7 +159,6 @@ ${GOOGLETEST_DIR}/googletest/include/ ${GOOGLETEST_DIR}/googlemock/include/ ${GOOGLETEST_DIR}/googletest/ - ${CMAKE_CURRENT_SOURCE_DIR}/include ) add_executable(marl-unittests ${MARL_TEST_LIST}) @@ -120,20 +167,24 @@ INCLUDE_DIRECTORIES "${MARL_TEST_INCLUDE_DIR}" FOLDER "Tests" ) + + marl_set_target_options(marl-unittests) + target_link_libraries(marl-unittests marl "${MARL_OS_LIBS}") endif(MARL_BUILD_TESTS) # examples if(MARL_BUILD_EXAMPLES) - function(BUILD_EXAMPLE name) - add_executable(${name} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${name}.cpp") - set_target_properties(${name} PROPERTIES - INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include" + function(build_example target) + add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp") + set_target_properties(${target} PROPERTIES FOLDER "Examples" ) - target_link_libraries(${name} marl "${MARL_OS_LIBS}") - endfunction(BUILD_EXAMPLE) + marl_set_target_options(${target}) + target_link_libraries(${target} marl "${MARL_OS_LIBS}") + endfunction(build_example) - BUILD_EXAMPLE(fractal) + build_example(fractal) + build_example(primes) endif(MARL_BUILD_EXAMPLES)
diff --git a/README.md b/README.md index 1cbaf9f..3641f79 100644 --- a/README.md +++ b/README.md
@@ -10,12 +10,59 @@ Marl supports Windows, macOS, Linux, Fuchsia and Android (arm, aarch64, ppc64 (ELFv2), x86 and x64). -Marl has no dependencies on other libraries (with exception on googletest fo building the optional unit tests). +Marl has no dependencies on other libraries (with exception on googletest for building the optional unit tests). Marl is in early development and will have breaking API changes. - **More documentation and examples coming soon.** - Note: This is not an officially supported Google product + +## Building + +Marl contains a number of unit tests and examples which can be built using CMake. + +Unit tests require fetching the `googletest` external project, which can be done by typing the following in your terminal: + +```bash +cd <path-to-marl> +git submodule update --init +``` + +### Linux and macOS + +To build the unit tests and examples, type the following in your terminal: + +```bash +cd <path-to-marl> +mkdir build +cd build +cmake .. -DMARL_BUILD_EXAMPLES=1 -DMARL_BUILD_TESTS=1 +make +``` + +The resulting binaries will be found in `<path-to-marl>/build` + +### Windows + +Marl can be build using [Visual Studio 2019's CMake integration](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019). + +### Using Marl in your CMake project + +You can build and link Marl using `add_subdirectory()` in your project's `CMakeLists.txt` file: +```cmake +set(MARL_DIR <path-to-marl>) # example <path-to-marl>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/marl" +add_subdirectory(${MARL_DIR}) +``` + +This will define the `marl` library target, which you can pass to `target_link_libraries()`: + +```cmake +target_link_libraries(<target> marl) # replace <target> with the name of your project's target +``` + +You will also want to add the `marl` public headers to your project's include search paths so you can `#include` the marl headers: + +```cmake +target_include_directories($<target> PRIVATE "${MARL_DIR}/include") # replace <target> with the name of your project's target +```
diff --git a/examples/fractal.cpp b/examples/fractal.cpp index 88c1ac6..bf74f27 100644 --- a/examples/fractal.cpp +++ b/examples/fractal.cpp
@@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -// This is an example application that uses Marl to parallelize the calculation of -// a Julia fractal. +// This is an example application that uses Marl to parallelize the calculation +// of a Julia fractal. -#include <marl/defer.h> -#include <marl/scheduler.h> -#include <marl/thread.h> -#include <marl/waitgroup.h> +#include "marl/defer.h" +#include "marl/scheduler.h" +#include "marl/thread.h" +#include "marl/waitgroup.h" #include <fstream> @@ -64,10 +64,9 @@ // julia calculates the Julia-set fractal value for the given coordinate and // constant. See https://en.wikipedia.org/wiki/Julia_set for more information. Color<float> julia(float x, float y, float cx, float cy) { - int iteration = 0; for (int i = 0; i < 1000; i++) { if (x * x + y * y > 4) { - return colorize(sqrt(i)); + return colorize(sqrtf(static_cast<float>(i))); } auto xtemp = x * x - y * y; @@ -99,7 +98,6 @@ const uint32_t padding = -(3 * width) & 3U; // in bytes const uint32_t stride = 3 * width + padding; // in bytes const uint32_t offset = 54; - const uint32_t size = offset + stride * height * 3; // Bitmap file header put1('B'); // header field @@ -128,7 +126,7 @@ put1(texel.g); put1(texel.r); } - for (int i = 0; i < padding; i++) { + for (uint32_t i = 0; i < padding; i++) { put1(0); } } @@ -139,7 +137,8 @@ // Constants used for rendering the fractal. constexpr uint32_t imageWidth = 2048; constexpr uint32_t imageHeight = 2048; -constexpr int samplesPerPixel = 8; +constexpr int samplesPerPixelW = 3; +constexpr int samplesPerPixelH = 3; constexpr float windowMinX = -0.5f; constexpr float windowMaxX = +0.5f; constexpr float windowMinY = -0.5f; @@ -165,7 +164,7 @@ marl::WaitGroup wg(imageHeight); // For each line of the image... - for (int y = 0; y < imageHeight; y++) { + for (uint32_t y = 0; y < imageHeight; y++) { // Schedule a task to calculate the image for this line. // These may run concurrently across hardware threads. marl::schedule([=] { @@ -173,18 +172,20 @@ // This is used to indicate that the task is done. defer(wg.done()); - for (int x = 0; x < imageWidth; x++) { + for (uint32_t x = 0; x < imageWidth; x++) { // Calculate the fractal pixel color. Color<float> color = {}; - for (int sample = 0; sample < samplesPerPixel; sample++) { - auto fx = float(x) + (rand() / float(RAND_MAX)); - auto fy = float(y) + (rand() / float(RAND_MAX)); - auto dx = float(fx) / float(imageWidth); - auto dy = float(fy) / float(imageHeight); - color += julia(lerp(dx, windowMinX, windowMaxX), - lerp(dy, windowMinY, windowMaxY), cx, cy); + for (int sy = 0; sy < samplesPerPixelH; sy++) { + auto fy = float(y) + (sy / float(samplesPerPixelH)); + for (int sx = 0; sx < samplesPerPixelW; sx++) { + auto fx = float(x) + (sx / float(samplesPerPixelW)); + auto dx = float(fx) / float(imageWidth); + auto dy = float(fy) / float(imageHeight); + color += julia(lerp(dx, windowMinX, windowMaxX), + lerp(dy, windowMinY, windowMaxY), cx, cy); + } } - color /= samplesPerPixel; + color /= samplesPerPixelW * samplesPerPixelH; pixels[x + y * imageWidth] = {static_cast<uint8_t>(color.r * 255), static_cast<uint8_t>(color.g * 255), static_cast<uint8_t>(color.b * 255)};
diff --git a/examples/primes.cpp b/examples/primes.cpp new file mode 100644 index 0000000..9f77631 --- /dev/null +++ b/examples/primes.cpp
@@ -0,0 +1,89 @@ +// Copyright 2019 The Marl Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This is an example application that uses Marl to find and print all the prime +// numbers in the range 1 to 10000000. + +#include "marl/defer.h" +#include "marl/scheduler.h" +#include "marl/thread.h" +#include "marl/ticket.h" + +// searchMax defines the upper limit on primes to find. +constexpr int searchMax = 10000000; + +// searchChunkSize is the number of numbers searched, per task, for primes. +constexpr int searchChunkSize = 10000; + +// isPrime returns true if i is prime. +bool isPrime(int i) { + auto c = static_cast<int>(sqrt(i)); + for (int j = 2; j <= c; j++) { + if (i % j == 0) { + return false; + } + } + return true; +} + +int main(int argc, const char** argv) { + // Create a marl scheduler using the full number of logical cpus. + // Bind this scheduler to the main thread so we can call marl::schedule() + marl::Scheduler scheduler; + scheduler.setWorkerThreadCount(marl::Thread::numLogicalCPUs()); + scheduler.bind(); + defer(scheduler.unbind()); // unbind before destructing the scheduler. + + // Create a ticket queue. This will be used to ensure that the primes are + // printed in ascending order. + marl::Ticket::Queue queue; + + // Iterate over the range [1, searchMax] in steps of searchChunkSize. + for (int searchBase = 1; searchBase <= searchMax; + searchBase += searchChunkSize) { + // Take a ticket from the ticket queue for this task. + auto ticket = queue.take(); + + // Schedule the task. + marl::schedule([=] { + // Find all the primes in [searchBase, searchBase+searchChunkSize-1]. + // Note this is run in parallel with the other scheduled tasks. + std::vector<int> primes; + for (int i = searchBase; i < searchBase + searchChunkSize; i++) { + if (isPrime(i)) { + primes.push_back(i); + } + } + + // Wait until the ticket is called. This ensures that the primes are + // printed in ascending order. This may cause this task to yield and allow + // other tasks to be executed while waiting for this ticket to be called. + ticket.wait(); + + // Print the primes. + for (auto prime : primes) { + printf("%d is prime\n", prime); + } + + // Call the next ticket so that those primes can be printed. + ticket.done(); + }); + } + + // take a ticket and wait on it to ensure that all the primes have been + // calculated and printed. + queue.take().wait(); + + return 0; +}
diff --git a/kokoro/linux/presubmit.sh b/kokoro/linux/presubmit.sh index c156f0b..ae166f4 100755 --- a/kokoro/linux/presubmit.sh +++ b/kokoro/linux/presubmit.sh
@@ -11,7 +11,7 @@ cd build build_and_run() { - cmake .. -DMARL_BUILD_EXAMPLES=1 $1 + cmake .. -DMARL_BUILD_EXAMPLES=1 -DMARL_BUILD_TESTS=1 -DMARL_WARNINGS_AS_ERRORS=1 $1 make --jobs=$(nproc) ./marl-unittests
diff --git a/kokoro/macos/presubmit.sh b/kokoro/macos/presubmit.sh index f26d18a..3d1b775 100755 --- a/kokoro/macos/presubmit.sh +++ b/kokoro/macos/presubmit.sh
@@ -9,7 +9,7 @@ mkdir build cd build -cmake .. -DMARL_BUILD_EXAMPLES=1 +cmake .. -DMARL_BUILD_EXAMPLES=1 -DMARL_BUILD_TESTS=1 -DMARL_WARNINGS_AS_ERRORS=1 make -j$(sysctl -n hw.logicalcpu) ./marl-unittests
diff --git a/kokoro/windows/presubmit.bat b/kokoro/windows/presubmit.bat index 8390780..504ad8c 100644 --- a/kokoro/windows/presubmit.bat +++ b/kokoro/windows/presubmit.bat
@@ -18,7 +18,7 @@ cd %SRC%\build if !ERRORLEVEL! neq 0 exit /b !ERRORLEVEL! -cmake .. -G "Visual Studio 15 2017 Win64" -Thost=x64 "-DMARL_BUILD_EXAMPLES=1" +cmake .. -G "Visual Studio 15 2017 Win64" -Thost=x64 "-DMARL_BUILD_TESTS=1" "-DMARL_BUILD_EXAMPLES=1" "-DMARL_WARNINGS_AS_ERRORS=1" if !ERRORLEVEL! neq 0 exit /b !ERRORLEVEL! %MSBUILD% /p:Configuration=%CONFIG% Marl.sln
diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 0ccd569..18723d0 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp
@@ -476,7 +476,7 @@ } Scheduler::Fiber* Scheduler::Worker::createWorkerFiber() { - auto id = workerFibers.size() + 1; + auto id = static_cast<int32_t>(workerFibers.size() + 1); auto fiber = Fiber::create(id, FiberStackSize, [&] { run(); }); workerFibers.push_back(std::unique_ptr<Fiber>(fiber)); return fiber;