diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..793361c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "oss-internship-2020/pffft/master"] + path = oss-internship-2020/pffft/master + url = https://bitbucket.org/jpommier/pffft/src/master/ diff --git a/oss-internship-2020/pffft/.gitignore b/oss-internship-2020/pffft/.gitignore new file mode 100644 index 0000000..eb6d948 --- /dev/null +++ b/oss-internship-2020/pffft/.gitignore @@ -0,0 +1,3 @@ +*.o +*.a +pffft_main diff --git a/oss-internship-2020/pffft/CMakeLists.txt b/oss-internship-2020/pffft/CMakeLists.txt new file mode 100644 index 0000000..af3bab4 --- /dev/null +++ b/oss-internship-2020/pffft/CMakeLists.txt @@ -0,0 +1,104 @@ +# Copyright 2020 Google LLC +# +# 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 +# +# http://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. + +cmake_minimum_required(VERSION 3.10) + +project(pffft CXX C) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_library(pffft STATIC + master/pffft.c + master/pffft.h + master/fftpack.c + master/fftpack.h +) + +add_executable(pffft_main + master/test_pffft.c +) + +target_link_libraries(pffft_main PRIVATE + pffft +) + +set(MATH_LIBS "") +include(CheckLibraryExists) +check_library_exists(m sin "" LIBM) +if(LIBM) + list(APPEND MATH_LIBS "m") +endif() + +target_link_libraries(pffft PUBLIC ${MATH_LIBS}) + +# Adding dependencies +set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree") +# Then configure: +# mkdir -p build && cd build +# cmake .. -G Ninja -DSAPI_ROOT=$HOME/sapi_root + +set(SAPI_ENABLE_EXAMPLES OFF CACHE BOOL "") +set(SAPI_ENABLE_TESTS OFF CACHE BOOL "") +add_subdirectory("${SAPI_ROOT}" + "${CMAKE_BINARY_DIR}/sandboxed-api-build" + # Omit this to have the full Sandboxed API in IDE + EXCLUDE_FROM_ALL) + +add_sapi_library(pffft_sapi + FUNCTIONS pffft_new_setup + pffft_destroy_setup + pffft_transform + pffft_transform_ordered + pffft_zreorder + pffft_zconvolve_accumulate + pffft_aligned_malloc + pffft_aligned_free + pffft_simd_size + cffti + cfftf + cfftb + rffti + rfftf + rfftb + cosqi + cosqf + cosqb + costi + cost + sinqi + sinqb + sinqf + sinti + sint + + INPUTS master/pffft.h master/fftpack.h + LIBRARY pffft + LIBRARY_NAME Pffft + + NAMESPACE "" +) + +target_include_directories(pffft_sapi INTERFACE + "${PROJECT_BINARY_DIR}" +) + +add_executable(pffft_sandboxed + main_pffft_sandboxed.cc +) + +target_link_libraries(pffft_sandboxed PRIVATE + pffft_sapi + sapi::sapi +) diff --git a/oss-internship-2020/pffft/README.md b/oss-internship-2020/pffft/README.md new file mode 100644 index 0000000..18834fc --- /dev/null +++ b/oss-internship-2020/pffft/README.md @@ -0,0 +1,87 @@ +# Sandboxing PFFFT library + +Build System: CMake +OS: Linux + +### Check out the PFFFT library & CMake set up +``` +git submodule update --init --recursive + +mkdir -p build && cd build +cmake .. -G Ninja -DPFFFT_ROOT_DIR=$PWD +ninjas +``` +### For testing: +`cd build`, then `./pffft_sandboxed` + +### For debug: +display custom info with +`./pffft_sandboxed --logtostderr` + +## ***About the project*** +*PFFFT library is concerned with 1D Fast-Fourier Transformations finding a +compromise between accuracy and speed. It deals with real and complex +vectors, both cases being illustrated in the testing part (`test_pffft.c` +for initially and original version, `main_pffft_sandboxed.cc` for our +currently implemented sandboxed version). +The original files can be found at: https://bitbucket.org/jpommier/pffft/src.* + +*The purpose of sandboxing is to limit the permissions and capabilities of +library’s methods, in order to secure the usage of them. +After obtaining the sandbox, the functions will be called through an +Sandbox API (being called `api` in the current test) and so, the +operations, system calls or namspaces access may be controlled. +From both `pffft.h` and `fftpack.h` headers, useful methods are added to +sapi library builded with CMake. There is also a need to link math library +as the transformations made require mathematical operators. +Regarding the testing of the methods, one main is doing this job by +iterating through a set of values, that represents the accuracy of +transformations and print the speed for each value and type of +transformation. More specifically, the input length is the target for +accuracy (named as `n`) and it stands for the number of data points from +the series that calculate the result of transformation. It is also +important to mention that the `complex` variable stands for a boolean value +that tells the type of transformation (0 for REAL and 1 for COMPLEX) and +it is taken into account while testing. +In the end, the performance of PFFFT library it is outlined by the output. +There are two output formats available, from which you can choose through +`--output_format=` command-line flag. +Without using this type of argument when running, the output format is set +by default.* + +#### CMake observations resume: +* linking pffft and fftpack (which contains necessary functions for pffft) +* set math library + +#### Sandboxed main observations resume: +* containing two testing parts (fft / pffft benchmarks) +* showing the performance of the transformations implies + testing them through various FFT dimenstions. + Variable n, the input length, will take specific values + meaning the number of points to which it is set the calculus + (more details of mathematical purpose of n - https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm). +* output shows speed depending on the input length +* use `--output_format=0` or `--output_format=1` arguments to choose between output formats. + `0` is for a detailed output, while `1` is only displaying each transformation process speed. + +### Bugs history +1. [Solved] pffft benchmark bug: "Sandbox not active" + + n = 64, status OK, `pffft_transform` generates error + n > 64, status not OK + Problem on initialising `sapi::StatusOr s;` the memory that stays + for s is not the same with the address passed in `pffft_transform` function. + (`sapi::v::GenericPtr` - to be changed) + + Temporary solution: change the generated files to accept + `uintptr_t` instead of `PFFFT_Setup` + + Solution: using `sapi::v::RemotePtr` instead of `sapi::v::GenericPtr` + to access the memory of object `s` + +2. [Unresolved] compiling bug: "No space left on device" + + The building process creates some `embed` files that use lots of + memory, trying to write them on `/tmp`. + + Temporary solution: clean /tmp directory by `sudo rm -rf /tmp/*` diff --git a/oss-internship-2020/pffft/main_pffft_sandboxed.cc b/oss-internship-2020/pffft/main_pffft_sandboxed.cc new file mode 100644 index 0000000..78b4b97 --- /dev/null +++ b/oss-internship-2020/pffft/main_pffft_sandboxed.cc @@ -0,0 +1,207 @@ +// Copyright 2020 Google LLC +// +// 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 +// +// http://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. + +#include + +#include +#include +#include +#include +#include + +#include +#include "pffft_sapi.sapi.h" // NOLINT(build/include) +#include "sandboxed_api/util/flag.h" +#include "sandboxed_api/vars.h" + +ABSL_DECLARE_FLAG(string, sandbox2_danger_danger_permit_all); +ABSL_DECLARE_FLAG(string, sandbox2_danger_danger_permit_all_and_log); + +class PffftSapiSandbox : public PffftSandbox { + public: + std::unique_ptr ModifyPolicy(sandbox2::PolicyBuilder*) { + return sandbox2::PolicyBuilder() + .AllowStaticStartup() + .AllowOpen() + .AllowRead() + .AllowWrite() + .AllowSystemMalloc() + .AllowExit() + .AllowSyscalls({ + __NR_futex, + __NR_close, + __NR_getrusage, + }) + .BuildOrDie(); + } +}; + +// output_format flag determines whether the output shows information in detail +// or not. By default, the flag is set as 0, meaning an elaborate display +// (see ShowOutput method). +static bool ValidateFlag(const char* flagname, int32_t value) { + if (value >= 0 && value < 32768) { + return true; + } + + LOG(ERROR) << "Invalid value for --" << flagname << "."; + return false; +} + +DEFINE_int32(output_format, 0, "Value to specific the output format."); +DEFINE_validator(output_format, &ValidateFlag); + +double UclockSec() { return static_cast(clock()) / CLOCKS_PER_SEC; } + +void ShowOutput(const char* name, int n, int complex, float flops, float t0, + float t1, int max_iter) { + float mflops = flops / 1e6 / (t1 - t0 + 1e-16); + if (FLAGS_output_format) { + if (flops != -1) { + printf("|%9.0f ", mflops); + } else { + printf("| n/a "); + } + } else if (flops != -1) { + printf("n=%5d, %s %16s : %6.0f MFlops [t=%6.0f ns, %d runs]\n", n, + (complex ? "CPLX" : "REAL"), name, mflops, + (t1 - t0) / 2 / max_iter * 1e9, max_iter); + } + fflush(stdout); +} + +absl::Status PffftMain() { + LOG(INFO) << "Initializing sandbox...\n"; + + PffftSapiSandbox sandbox; + SAPI_RETURN_IF_ERROR(sandbox.Init()); + + PffftApi api(&sandbox); + + // kTransformSizes is a vector keeping the values by which iterates n, its + // value representing the input length. More concrete, n is the number of data + // points the caclulus is up to (determinating its accuracy). To show the + // performance of Fast-Fourier Transformations the program is testing for + // various values of n. + constexpr int kTransformSizes[] = { + 64, 96, 128, 160, 192, 256, 384, 5 * 96, 512, 5 * 128, + 3 * 256, 800, 1024, 2048, 2400, 4096, 8192, 9 * 1024, 16384, 32768}; + + for (int complex : {0, 1}) { + for (int n : kTransformSizes) { + const int n_float = n * (complex ? 2 : 1); + int n_bytes = n_float * sizeof(float); + + std::vector work(2 * n_float + 15, 0.0); + sapi::v::Array work_array(&work[0], work.size()); + + std::vector x(n_bytes, 0.0); + sapi::v::Array x_array(&x[0], x.size()); + + std::vector y(n_bytes, 0.0); + sapi::v::Array y_array(&y[0], y.size()); + + std::vector z(n_bytes, 0.0); + sapi::v::Array z_array(&z[0], z.size()); + + double t0; + double t1; + double flops; + + int max_iter = 5120000 / n * 4; + + for (int k = 0; k < n_float; ++k) { + x[k] = 0; + } + + // FFTPack benchmark + { + // SIMD_SZ == 4 (returning value of pffft_simd_size()) + int simd_size_iter = max_iter / 4; + + if (simd_size_iter == 0) simd_size_iter = 1; + if (complex) { + SAPI_RETURN_IF_ERROR(api.cffti(n, work_array.PtrBoth())) + } else { + SAPI_RETURN_IF_ERROR(api.rffti(n, work_array.PtrBoth())); + } + t0 = UclockSec(); + + for (int iter = 0; iter < simd_size_iter; ++iter) { + if (complex) { + SAPI_RETURN_IF_ERROR( + api.cfftf(n, x_array.PtrBoth(), work_array.PtrBoth())); + SAPI_RETURN_IF_ERROR( + api.cfftb(n, x_array.PtrBoth(), work_array.PtrBoth())); + } else { + SAPI_RETURN_IF_ERROR( + api.rfftf(n, x_array.PtrBoth(), work_array.PtrBoth())); + SAPI_RETURN_IF_ERROR( + api.rfftb(n, x_array.PtrBoth(), work_array.PtrBoth())); + } + } + t1 = UclockSec(); + + flops = (simd_size_iter * 2) * + ((complex ? 5 : 2.5) * static_cast(n) * + log(static_cast(n)) / M_LN2); + ShowOutput("FFTPack", n, complex, flops, t0, t1, simd_size_iter); + } + + // PFFFT benchmark + { + SAPI_ASSIGN_OR_RETURN( + PFFFT_Setup * s, + api.pffft_new_setup(n, complex ? PFFFT_COMPLEX : PFFFT_REAL)); + + sapi::v::RemotePtr s_reg(s); + + t0 = UclockSec(); + for (int iter = 0; iter < max_iter; ++iter) { + SAPI_RETURN_IF_ERROR( + api.pffft_transform(&s_reg, x_array.PtrBoth(), z_array.PtrBoth(), + y_array.PtrBoth(), PFFFT_FORWARD)); + SAPI_RETURN_IF_ERROR( + api.pffft_transform(&s_reg, x_array.PtrBoth(), z_array.PtrBoth(), + y_array.PtrBoth(), PFFFT_FORWARD)); + } + + t1 = UclockSec(); + SAPI_RETURN_IF_ERROR(api.pffft_destroy_setup(&s_reg)); + + flops = (max_iter * 2) * ((complex ? 5 : 2.5) * static_cast(n) * + log(static_cast(n)) / M_LN2); + ShowOutput("PFFFT", n, complex, flops, t0, t1, max_iter); + + LOG(INFO) << "n = " << n << " SUCCESSFULLY"; + } + } + } + + return absl::OkStatus(); +} + +int main(int argc, char* argv[]) { + // Initialize Google's logging library. + google::InitGoogleLogging(argv[0]); + + gflags::ParseCommandLineFlags(&argc, &argv, true); + + if (absl::Status status = PffftMain(); !status.ok()) { + LOG(ERROR) << "Initialization failed: " << status.ToString(); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +}