├── .bazelrc ├── .bazelversion ├── .clang-format ├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── AUTHORS ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── WORKSPACE ├── bazel ├── mesh-pldi19-powers.pdf ├── src ├── BUILD ├── CMakeLists.txt ├── bitmap.h ├── cheap_heap.h ├── common.h ├── copts.bzl ├── d_assert.cc ├── fbsd_wrapper.cc ├── fixed_array.h ├── global_heap.cc ├── global_heap.h ├── gnu_wrapper.cc ├── internal.h ├── libmesh.cc ├── mac_wrapper.cc ├── measure_rss.cc ├── measure_rss.h ├── meshable_arena.cc ├── meshable_arena.h ├── meshing.h ├── mini_heap.h ├── mmap_heap.h ├── one_way_mmap_heap.h ├── partitioned_heap.h ├── plasma │ └── mesh.h ├── real.cc ├── real.h ├── rng │ ├── mwc.h │ └── mwc64.h ├── rpl_printf.c ├── runtime.cc ├── runtime.h ├── shuffle_vector.h ├── size_classes.def ├── static │ ├── if.h │ └── log.h ├── testing │ ├── benchmark │ │ └── local_refill.cc │ ├── big-alloc.c │ ├── fragmenter.cc │ ├── global-large-stress.cc │ ├── local-alloc.c │ ├── meshing_benchmark.cc │ ├── thread.cc │ ├── unit │ │ ├── alignment.cc │ │ ├── bitmap_test.cc │ │ ├── concurrent_mesh_test.cc │ │ ├── mesh_test.cc │ │ ├── rng_test.cc │ │ ├── size_class_test.cc │ │ └── triple_mesh_test.cc │ └── userfaultfd-kernel-copy.cc ├── thread_local_heap.cc ├── thread_local_heap.h └── wrapper.cc ├── support ├── export_mesh.cmake ├── gen-size-classes ├── install_all_configs ├── remove_export_mesh.cmake └── update-bazelisk ├── theory ├── 32m80.png ├── 64m80ind.png ├── bound_comparison.py ├── bounds │ └── impdeg+1 ├── choose.py ├── common.py ├── compute_exp_Y.py ├── createRandomString.py ├── deg_bound_check.py ├── degcheck.py ├── dumps │ ├── 32,1,80,dumb.txt │ ├── 32,2,80,dumb.txt │ ├── 32,3,80,dumb.txt │ ├── 32,4,80,dumb.txt │ ├── 32,5,80,dumb.txt │ ├── 32,6,80,dumb.txt │ ├── 32,7,80,dumb.txt │ ├── 32,8,80,dumb.txt │ └── 32,9,80,dumb.txt ├── experiment.py ├── experiment_raw_results │ └── .gitignore ├── greedy_experiment.py ├── greedy_experiment_copy.py ├── greedy_experiment_q.py ├── makeGraph.py ├── manyreps.png ├── manystrings.png ├── match_vs_color_experiment.py ├── maxmatch_vs_E[Y].py ├── maxmatch_vs_greedymatch.py ├── maxvdeg+1imp++32,80.png ├── mesh_util.py ├── meshers.py ├── meshingBenchmark.py ├── occupancyComparison.py ├── randmatch_vs_greedymatch.py ├── randmatch_vs_greedymatch_q.py ├── randmatch_vs_greedymatch_time.py ├── read_mesh_dump.py └── test.py └── tools └── bazel /.bazelrc: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Mesh Authors. All rights reserved. 2 | # Use of this source code is governed by the Apache License, 3 | # Version 2.0, that can be found in the LICENSE file. 4 | 5 | build --cxxopt='-std=c++17' 6 | #build --crosstool_top=@llvm_toolchain//:toolchain --copt=-D_LIBCPP_ENABLE_NODISCARD 7 | build --strip=never 8 | 9 | # largely from sorbet 10 | build:debugsymbols --copt=-g3 --copt=-fstandalone-debug --copt=-fno-limit-debug-info 11 | build:debugsymbols --linkopt=-g3 --linkopt=-fstandalone-debug --linkopt=-fno-limit-debug-info 12 | build:debugsymbols --spawn_strategy=standalone 13 | build:debugsymbols --genrule_strategy=standalone 14 | 15 | build:disable-meshing --define disable_meshing=true 16 | build:disable-randomization --define disable_randomization=true 17 | build:shuffle-on-free --define shuffle_on_free=true 18 | 19 | build:modern-amd64 --copt=-mavx --copt=-march=westmere 20 | build:modern-amd64 --linkopt=-mavx --linkopt=-march=westmere 21 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 4.2.1 2 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 120 3 | AlignConsecutiveAssignments: false 4 | AlignConsecutiveDeclarations: false 5 | AllowShortFunctionsOnASingleLine: false 6 | AllowShortIfStatementsOnASingleLine: false 7 | AllowShortLoopsOnASingleLine: false 8 | IndentCaseLabels: false 9 | AlwaysBreakTemplateDeclarations: true 10 | AccessModifierOffset: -2 11 | Standard: Cpp11 12 | SortIncludes: false 13 | 14 | DerivePointerAlignment: false 15 | PointerAlignment: Right 16 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *~ 2 | /Dockerfile 3 | /build 4 | /config.mk 5 | /*.so 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [Makefile] 12 | indent_style = tab 13 | 14 | [*.{c,cc,cpp,h,hh}] 15 | indent_style = space 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /src/vendor linguist-vendored 2 | /theory linguist-vendored 3 | /support linguist-vendored 4 | 5 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | Ubuntu_cmake: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - name: Install required packages 9 | run: | 10 | sudo apt-get update 11 | sudo apt-get -y install git gcc g++ make lcov wget libpthread-stubs0-dev 12 | - name: Configure CMake 13 | run: | 14 | mkdir cmake 15 | cd cmake 16 | wget https://github.com/Kitware/CMake/releases/download/v3.16.5/cmake-3.16.5-Linux-x86_64.sh 17 | chmod +x cmake-3.16.5-Linux-x86_64.sh 18 | ./cmake-3.16.5-Linux-x86_64.sh --skip-license 19 | cd ../ 20 | mkdir cmake-cache 21 | cd cmake-cache 22 | ../cmake/bin/cmake -DGCOV=ON .. 23 | - name: Build mesh 24 | run: | 25 | cd cmake-cache 26 | make all 27 | - name: Run tests 28 | run: | 29 | cd cmake-cache 30 | make coverage_gcc 31 | 32 | Ubuntu_bazel: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v1 36 | - name: build in debug mode 37 | run: | 38 | ./bazel test -c dbg //src:unit-tests 39 | - name: build tests and binary 40 | run: | 41 | make -j1 42 | 43 | macOS_bazel: 44 | runs-on: macos-latest 45 | steps: 46 | - uses: actions/checkout@v1 47 | - name: build in debug mode 48 | run: | 49 | ./bazel test -c dbg //src:unit-tests --test_output=all --cache_test_results=no --runs_per_test=5 50 | - name: build tests and binary 51 | run: | 52 | make -j1 53 | 54 | #Windows_MinGW: 55 | # 56 | # runs-on: windows-latest 57 | # 58 | # steps: 59 | # - uses: actions/checkout@v1 60 | # - name: Install msys2/mingw64 61 | # #steps from https://github.com/msys2/MINGW-packages/blob/master/azure-pipelines.yml 62 | # run: | 63 | # git clone https://github.com/msys2/msys2-ci-base.git %CD:~0,2%\msys64 64 | # %CD:~0,2%\msys64\usr\bin\rm -rf %CD:~0,2%\msys64\.git 65 | # set PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem 66 | # %CD:~0,2%\msys64\usr\bin\pacman --noconfirm -Syyuu 67 | # set PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem 68 | # %CD:~0,2%\msys64\usr\bin\pacman --noconfirm --needed -S git base-devel 69 | # %CD:~0,2%\msys64\usr\bin\pacman --noconfirm -Scc 70 | # - name: Install required packages 71 | # run: | 72 | # set PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem 73 | # pacman -S --noconfirm mingw-w64-x86_64-binutils 74 | # pacman -S --noconfirm mingw-w64-x86_64-cmake 75 | # pacman -S --noconfirm mingw-w64-x86_64-gcc 76 | # pacman -S --noconfirm mingw-w64-x86_64-gcc-libs 77 | # pacman -S --noconfirm mingw-w64-x86_64-gcc-objc 78 | # pacman -S --noconfirm mingw-w64-x86_64-gdb 79 | # pacman -S --noconfirm mingw-w64-x86_64-ninja 80 | # pacman -S --noconfirm mingw-w64-x86_64-lcov 81 | # pacman -S --noconfirm mingw-w64-x86_64-winpthreads-git 82 | # pacman --noconfirm -Scc 83 | # - name: Configure CMake 84 | # run: | 85 | # set PATH=%CD:~0,2%\msys64\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem 86 | # mkdir cmake-cache 87 | # cd cmake-cache 88 | # cmake -DGCOV=ON -G"Ninja" .. 89 | # - name: Build 90 | # run: | 91 | # set PATH=%CD:~0,2%\msys64\mingw64\bin;%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem 92 | # cd cmake-cache 93 | # ninja 94 | # - name: Run tests 95 | # run: | 96 | # set PATH=%CD:~0,2%\msys64\mingw64\bin;%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem 97 | # cd cmake-cache 98 | # ninja coverage 99 | # - name: Run big alloc 100 | # run: cd cmake-cache && ninja run_big-alloc 101 | # - name: Run fork example 102 | # run: cd cmake-cache && ninja run_fork-example 103 | # - name: Run global-large-stress 104 | # run: cd cmake-cache && ninja run_global-large-stress 105 | # - name: Run local alloc 106 | # run: cd cmake-cache && ninja run_local-alloc 107 | # - name: Run thread 108 | # run: cd cmake-cache && ninja run_thread 109 | 110 | #Windows_Visual_Studio: #not working 111 | # 112 | # runs-on: windows-latest 113 | # 114 | # steps: 115 | # - uses: actions/checkout@v1 116 | # - name: Install required packages 117 | # run: | 118 | # ???? 119 | # - name: Configure CMake 120 | # run: | 121 | # %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" 122 | # mkdir cmake-cache 123 | # cd cmake-cache 124 | # cmake -DGCOV=ON -G"Ninja" .. 125 | # - name: Build 126 | # run: | 127 | # %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" 128 | # cd cmake-cache 129 | # ninja 130 | # - name: Run tests 131 | # run: | 132 | # cd cmake-cache 133 | # ninja coverage 134 | # - name: Run big alloc 135 | # run: cd cmake-cache && ninja run_big-alloc 136 | # - name: Run fork example 137 | # run: cd cmake-cache && ninja run_fork-example 138 | # - name: Run global-large-stress 139 | # run: cd cmake-cache && ninja run_global-large-stress 140 | # - name: Run local alloc 141 | # run: cd cmake-cache && ninja run_local-alloc 142 | # - name: Run thread 143 | # run: cd cmake-cache && ninja run_thread 144 | 145 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.d 3 | *.dylib 4 | *.o 5 | *.so 6 | *.pyc 7 | TAGS 8 | /src/config.h 9 | /src/test/fork-example 10 | /src/test/thread-example 11 | /unit.test 12 | /meshing-benchmark 13 | config.mk 14 | /build 15 | /doc 16 | /fragmenter 17 | /theory/env 18 | /frag.pdf 19 | /data 20 | /paper/latex.out/ 21 | /paper/mesh.pdf 22 | __pycache__ 23 | /perf.data 24 | /perf.data.old 25 | /src/test/local-alloc 26 | /src/test/local-alloc-glibc 27 | /src/test/big-alloc 28 | /src/test/big-alloc-glibc 29 | /src/test/big-alloc-hoard 30 | /src/test/local-alloc-hoard 31 | /src/test/local-alloc-tcmalloc 32 | /src/test/local-alloc-jemalloc 33 | /src/test/local-alloc-mesh 34 | /src/test/global-large-stress 35 | /test/docker/*/mstatgcc 36 | /test/docker/*/mstat 37 | /test/spec/data 38 | /coverage 39 | /src/test/larson-mesh 40 | /src/test/larson-glibc 41 | /src/test/larson-tcmalloc 42 | /src/test/larson-hoard 43 | /.idea 44 | /.clwb 45 | /cmake-build* 46 | /cmake-cache/ 47 | /bazel-bin 48 | /bazel-genfiles 49 | /bazel-mesh 50 | /bazel-out 51 | /bazel-testlogs 52 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | University of Massachusetts Amherst 2 | Bobby Powers 3 | David Tench 4 | Emery Berger 5 | Andrew McGregor 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at bobbypowers@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | 78 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # docker build -t bpowers/mesh . 2 | FROM ubuntu:18.04 as builder 3 | MAINTAINER Bobby Powers 4 | 5 | RUN apt-get update && apt-get install -y \ 6 | build-essential \ 7 | git \ 8 | python3 \ 9 | sudo \ 10 | libxml2 \ 11 | && rm -rf /var/lib/apt/lists/* \ 12 | && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 \ 13 | && rm -rf /usr/local/lib/python3.6 14 | 15 | WORKDIR /src 16 | 17 | COPY . . 18 | 19 | ENV PREFIX /usr/local 20 | 21 | RUN make test 22 | 23 | RUN support/install_all_configs 24 | 25 | 26 | FROM ubuntu:18.04 27 | 28 | COPY --from=builder /usr/lib/libmesh* /usr/local/lib/ 29 | 30 | RUN ldconfig 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Mesh Authors. All rights reserved. 2 | # Use of this source code is governed by the Apache License, 3 | # Version 2.0, that can be found in the LICENSE file. 4 | 5 | PREFIX = /usr 6 | BAZEL_CONFIG = --config=modern-amd64 7 | LIB_SUFFIX = 8 | 9 | UNAME_S = $(shell uname -s) 10 | ifeq ($(UNAME_S),Darwin) 11 | LIB_EXT = dylib 12 | BAZEL_PREFIX = darwin 13 | LDCONFIG = 14 | PREFIX = /usr/local 15 | else 16 | LIB_EXT = so 17 | BAZEL_PREFIX = k8 18 | LDCONFIG = ldconfig 19 | endif 20 | 21 | LIB = mesh 22 | FS_LIB = libmesh.so 23 | INSTALL_LIB = libmesh$(LIB_SUFFIX).$(LIB_EXT) 24 | 25 | COV_DIR = coverage 26 | CONFIG = Makefile 27 | 28 | # quiet output, but allow us to look at what commands are being 29 | # executed by passing 'V=1' to make, without requiring temporarily 30 | # editing the Makefile. 31 | ifneq ($V, 1) 32 | MAKEFLAGS += -s 33 | endif 34 | 35 | .SUFFIXES: 36 | .SUFFIXES: .cc .c .o .d .test 37 | 38 | all: test build 39 | 40 | build lib: 41 | ./bazel build $(BAZEL_CONFIG) -c opt //src:$(LIB) 42 | 43 | test check: 44 | ./bazel test $(BAZEL_CONFIG) //src:unit-tests --test_output=all --action_env="GTEST_COLOR=1" 45 | 46 | install: 47 | install -c -m 0755 bazel-out/$(BAZEL_PREFIX)-opt/bin/src/$(FS_LIB) $(PREFIX)/lib/$(INSTALL_LIB) 48 | $(LDCONFIG) 49 | mkdir -p $(PREFIX)/include/plasma 50 | install -c -m 0755 src/plasma/mesh.h $(PREFIX)/include/plasma/mesh.h 51 | 52 | clang-coverage: $(UNIT_BIN) $(LIB) $(CONFIG) 53 | mkdir -p "$(COV_DIR)" 54 | rm -f "$(COV_DIR)/unit.test.profdata" 55 | cd "$(COV_DIR)" && llvm-profdata merge -sparse ../default.profraw -o unit.test.profdata 56 | cd "$(COV_DIR)" && llvm-cov show -format=html -instr-profile=unit.test.profdata ../unit.test -ignore-filename-regex='.*(vendor|unit)/.*' >index.html 57 | cd "$(COV_DIR)" && llvm-cov report -instr-profile=unit.test.profdata ../unit.test -ignore-filename-regex='.*(vendor|unit)/.*' -use-color 58 | rm -f default.profraw 59 | 60 | benchmark: 61 | ./bazel build $(BAZEL_CONFIG) --config=disable-meshing --config=debugsymbols -c opt //src:local-refill-benchmark 62 | ./bazel-bin/src/local-refill-benchmark 63 | 64 | format: 65 | clang-format -i src/*.cc src/*.c src/*.h src/plasma/*.h src/rng/*.h src/static/*.h src/testing/unit/*.cc src/testing/*.cc src/testing/benchmark/*.cc 66 | 67 | clean: 68 | find . -name '*~' -print0 | xargs -0 rm -f 69 | ./bazel clean 70 | ./bazel shutdown 71 | 72 | 73 | distclean: clean 74 | ./bazel clean --expunge 75 | 76 | # double $$ in egrep pattern is because we're embedding this shell command in a Makefile 77 | TAGS: 78 | @echo " TAGS" 79 | find . -type f | egrep '\.(cpp|h|cc|hh)$$' | grep -v google | xargs etags -l c++ 80 | 81 | .PHONY: all clean distclean format test test_frag check build benchmark install TAGS 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mesh: Compacting Memory Management for C/C++ 2 | ============================================ 3 | 4 | Mesh is a drop in replacement for 5 | [malloc(3)](http://man7.org/linux/man-pages/man3/malloc.3.html) 6 | that can transparently recover from memory fragmentation without any changes 7 | to application code. 8 | 9 | Mesh is described in detail in a [paper (PDF)](https://github.com/plasma-umass/Mesh/raw/master/mesh-pldi19-powers.pdf) that appeared at PLDI 2019. 10 | 11 | Or watch this talk by Bobby Powers at Strange Loop: 12 | 13 | [![Compacting the Uncompactable](https://img.youtube.com/vi/c1UBJbfR-H0/0.jpg)](https://www.youtube.com/watch?v=c1UBJbfR-H0) 14 | 15 | Mesh runs on Linux and macOS. Windows is a work in progress. 16 | 17 | Mesh uses [bazel](https://bazel.build/) as a build system, but wraps it in a Makefile, and has no runtime dependencies 18 | other than libc: 19 | 20 | ``` 21 | $ git clone https://github.com/plasma-umass/mesh 22 | $ cd mesh 23 | $ make; sudo make install 24 | # example: run git with mesh as its allocator: 25 | $ LD_PRELOAD=libmesh.so git status 26 | ``` 27 | 28 | Please open an issue if you have questions (or issues)! 29 | 30 | 31 | But will it blend? 32 | ------------------ 33 | 34 | If you run a program linked against mesh (or with Mesh `LD_PRELOAD`ed), setting the variable `MALLOCSTATS=1` will instruct mesh to print a summary at exit: 35 | 36 | ``` 37 | $ MALLOCSTATS=1 ./bin/redis-server-mesh ./redis.conf 38 | 25216:C 11 Mar 20:27:12.050 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 39 | 25216:C 11 Mar 20:27:12.050 # Redis version=4.0.2, bits=64, commit=dfe0d212, modified=0, pid=25216, just started 40 | 25216:C 11 Mar 20:27:12.050 # Configuration loaded 41 | [...] 42 | ^C25216:signal-handler (1583983641) Received SIGINT scheduling shutdown... 43 | 25216:M 11 Mar 20:27:21.945 # User requested shutdown... 44 | 25216:M 11 Mar 20:27:21.945 * Removing the pid file. 45 | 25216:M 11 Mar 20:27:21.945 * Removing the unix socket file. 46 | 25216:M 11 Mar 20:27:21.945 # Redis is now ready to exit, bye bye... 47 | MESH COUNT: 25918 48 | Meshed MB (total): 101.2 49 | Meshed pages HWM: 25918 50 | Meshed MB HWM: 101.2 51 | MH Alloc Count: 56775 52 | MH Free Count: 17 53 | MH High Water Mark: 82687 54 | ``` 55 | 56 | Not all workloads experience fragmentation, so its possible that Mesh will have a small 'Meshed MB (total)' number! 57 | 58 | 59 | Implementation Overview 60 | ----------------------- 61 | 62 | Mesh is built on [Heap Layers](http://heaplayers.org/), an 63 | infrastructure for building high performance memory allocators in C++ 64 | (see the 65 | [paper](https://people.cs.umass.edu/~emery/pubs/berger-pldi2001.pdf) 66 | for details.) 67 | 68 | The entry point of the library is [`libmesh.cc`](src/libmesh.cc). 69 | This file is where `malloc`, `free` and the instantiations of the 70 | Heap used for allocating program memory lives. 71 | 72 | 73 | DEFINITIONS 74 | ----------- 75 | 76 | - **Page**: The smallest block of memory managed by the operating 77 | system, 4Kb on most architectures. Memory given to the allocator by 78 | the operating system is always in multiples of the page size, and 79 | aligned to the page size. 80 | - **Span**: A contiguous run of 1 or more pages. It is often larger 81 | than the page size to account for large allocations and amortize the 82 | cost of heap metadata. 83 | - **Arena**: A contiguous range of virtual address space we allocate 84 | out of. All allocations returned by 85 | [`malloc(3)`](http://man7.org/linux/man-pages/man3/malloc.3.html) 86 | reside within the arena. 87 | - [**GlobalHeap**](src/global_heap.h): The global heap carves out the 88 | Arena into Spans and performs meshing. 89 | - [**MiniHeap**](src/mini_heap.h): Metadata for a Span -- at any time 90 | a live Span has a single MiniHeap owner. For small objects, 91 | MiniHeaps have a bitmap to track whether an allocation is live or 92 | freed. 93 | - [**ThreadLocalHeap**](src/thread_local_heap.h): A collections of 94 | MiniHeaps and a ShuffleVector so that most allocations and 95 | `free(3)`s can be fast and lock-free. 96 | - [**ShuffleVector**](src/shuffle_vector.h): A novel data structure 97 | that enables randomized allocation with bump-pointer-like speed. 98 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Mesh Authors. All rights reserved. 2 | # Use of this source code is governed by the Apache License, 3 | # Version 2.0, that can be found in the LICENSE file. 4 | 5 | workspace(name = "org_libmesh") 6 | 7 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 8 | 9 | commit = { 10 | "rules_cc": "aa7ff810cf5ec99ca34f784743b33631b74c2d16", 11 | "googletest": "aa533abfd4232b01f9e57041d70114d5a77e6de0", 12 | "benchmark": "bc5651e54a7e178ca6b1e27e469a9be19cfa62c4", # 1.5.4 13 | "heap_layers": "f97c110c6a06982284e8927d17e52129736aa123", 14 | } 15 | 16 | http_archive( 17 | name = "rules_cc", 18 | sha256 = "21749f1be88e0965a1e70eed1a433d73c0ceaf485dfa50ff2d6e7c6944917673", 19 | strip_prefix = "rules_cc-{}".format(commit["rules_cc"]), 20 | urls = [ 21 | "https://github.com/bazelbuild/rules_cc/archive/{}.zip".format(commit["rules_cc"]), 22 | ], 23 | ) 24 | 25 | http_archive( 26 | name = "com_google_googletest", 27 | sha256 = "ab7d3be1f3781984102d7c12d7dc27ae2695a817c439f24db8ffc593840c1b17", 28 | strip_prefix = "googletest-{}".format(commit["googletest"]), 29 | urls = [ 30 | "https://github.com/google/googletest/archive/{}.zip".format(commit["googletest"]), 31 | ], 32 | ) 33 | 34 | http_archive( 35 | name = "com_google_benchmark", 36 | sha256 = "", 37 | strip_prefix = "benchmark-{}".format(commit["benchmark"]), 38 | urls = [ 39 | "https://github.com/google/benchmark/archive/{}.zip".format(commit["benchmark"]), 40 | ], 41 | ) 42 | 43 | http_archive( 44 | name = "org_heaplayers", 45 | sha256 = "ee2c351be580f2242bfccef6cc092166d009e773bc42154aa9af0fb1f1bd4fcf", 46 | strip_prefix = "Heap-Layers-{}".format(commit["heap_layers"]), 47 | urls = [ 48 | "https://github.com/emeryberger/Heap-Layers/archive/{}.zip".format(commit["heap_layers"]), 49 | ], 50 | ) 51 | -------------------------------------------------------------------------------- /mesh-pldi19-powers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plasma-umass/Mesh/d45d6deec627b7d46de98dfb5415c30b7c1db9c6/mesh-pldi19-powers.pdf -------------------------------------------------------------------------------- /src/BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Mesh Authors. All rights reserved. 2 | # Use of this source code is governed by the Apache License, 3 | # Version 2.0, that can be found in the LICENSE file. 4 | 5 | load("//src:copts.bzl", "MESH_DEFAULT_COPTS") 6 | 7 | package(default_visibility = ["//visibility:private"]) 8 | 9 | licenses(["notice"]) # Apache 2.0 10 | 11 | # used in copts.bzl to set llvm/gcc-only flags 12 | config_setting( 13 | name = "llvm", 14 | flag_values = { 15 | "@bazel_tools//tools/cpp:compiler": "llvm", 16 | }, 17 | visibility = ["//visibility:private"], 18 | ) 19 | 20 | config_setting( 21 | name = "disable_meshing", 22 | values = { 23 | "define": "disable_meshing=true", 24 | }, 25 | visibility = ["//visibility:private"], 26 | ) 27 | 28 | config_setting( 29 | name = "disable_randomization", 30 | values = { 31 | "define": "disable_randomization=true", 32 | }, 33 | visibility = ["//visibility:private"], 34 | ) 35 | 36 | config_setting( 37 | name = "shuffle_on_free", 38 | values = { 39 | "define": "shuffle_on_free=true", 40 | }, 41 | visibility = ["//visibility:private"], 42 | ) 43 | 44 | NO_BUILTIN_MALLOC = [ 45 | "-fno-builtin-malloc", 46 | "-fno-builtin-free", 47 | ] 48 | 49 | COMMON_DEFINES = [] + select({ 50 | ":disable_meshing": ["MESHING_ENABLED=0"], 51 | "//conditions:default": ["MESHING_ENABLED=1"], 52 | }) + select({ 53 | ":disable_randomization": ["SHUFFLE_ON_INIT=0"], 54 | "//conditions:default": ["SHUFFLE_ON_INIT=1"], 55 | }) + select({ 56 | ":shuffle_on_free": ["SHUFFLE_ON_FREE=1"], 57 | "//conditions:default": ["SHUFFLE_ON_FREE=0"], 58 | }) + select({ 59 | "@bazel_tools//src/conditions:linux_x86_64": [ 60 | # "_FORTIFY_SOURCE=2", # TODO: only in release; but I think Bazel takes care of this? 61 | "_DEFAULT_SOURCE", 62 | "_BSD_SOURCE", 63 | "_XOPEN_SOURCE=700", 64 | ], 65 | "//conditions:default": [], 66 | }) 67 | 68 | COMMON_LINKOPTS = [ 69 | "-lm", 70 | "-lpthread", 71 | "-ldl", 72 | ] + select({ 73 | "@bazel_tools//src/conditions:linux_x86_64": [ 74 | "-Wl,--no-as-needed", 75 | "-Wl,--no-add-needed", 76 | "-Wl,--sort-common", 77 | "-Wl,--hash-style=both", 78 | "-Wl,--no-undefined", 79 | "-Wl,-Bsymbolic-functions", 80 | "-Wl,-z,now,-z,relro", 81 | "-Wl,--exclude-libs=libc++.a", 82 | "-Wl,--exclude-libs=libc++abi.a", 83 | "-Wl,--exclude-libs=libunwind.a", 84 | ], 85 | "//conditions:default": [], 86 | }) 87 | 88 | cc_library( 89 | name = "mesh-core", 90 | srcs = [ 91 | "d_assert.cc", 92 | "global_heap.cc", 93 | "measure_rss.cc", 94 | "meshable_arena.cc", 95 | "real.cc", 96 | "runtime.cc", 97 | "thread_local_heap.cc", 98 | ], 99 | hdrs = glob([ 100 | "*.h", 101 | "plasma/*.h", 102 | "rng/*.h", 103 | "gnu_wrapper.cc", 104 | "mac_wrapper.cc", 105 | "rpl_printf.c", 106 | "static/*.h", 107 | "size_classes.def", 108 | ]), 109 | copts = NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS, 110 | defines = COMMON_DEFINES, 111 | linkopts = COMMON_LINKOPTS, 112 | linkstatic = True, 113 | visibility = ["//visibility:private"], 114 | deps = [ 115 | "@org_heaplayers", 116 | ], 117 | ) 118 | 119 | cc_library( 120 | name = "mesh-lib", 121 | srcs = [ 122 | "libmesh.cc", 123 | ], 124 | hdrs = glob([ 125 | "*.h", 126 | "plasma/*.h", 127 | "rng/*.h", 128 | "gnu_wrapper.cc", 129 | "mac_wrapper.cc", 130 | "static/*.h", 131 | "size_classes.def", 132 | "wrapper.cc", 133 | ]), 134 | # TODO: config options here too 135 | copts = NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS, 136 | defines = COMMON_DEFINES, 137 | linkopts = COMMON_LINKOPTS, 138 | linkstatic = True, 139 | visibility = ["//visibility:private"], 140 | deps = [ 141 | "@org_heaplayers", 142 | ], 143 | ) 144 | 145 | cc_library( 146 | name = "mesh", 147 | srcs = [ 148 | "d_assert.cc", 149 | "global_heap.cc", 150 | "libmesh.cc", 151 | "measure_rss.cc", 152 | "meshable_arena.cc", 153 | "real.cc", 154 | "runtime.cc", 155 | "thread_local_heap.cc", 156 | ], 157 | hdrs = glob([ 158 | "*.h", 159 | "plasma/*.h", 160 | "rng/*.h", 161 | "gnu_wrapper.cc", 162 | "mac_wrapper.cc", 163 | "wrapper.cc", 164 | "rpl_printf.c", 165 | "static/*.h", 166 | "size_classes.def", 167 | ]), 168 | copts = NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS, 169 | defines = COMMON_DEFINES, 170 | linkopts = COMMON_LINKOPTS + select({ 171 | "@bazel_tools//src/conditions:darwin": [ 172 | "-install_name /usr/local/lib/libmesh.dylib", 173 | "-compatibility_version 1", 174 | "-current_version 1", 175 | "-ldl", 176 | ], 177 | "//conditions:default": [], 178 | }), 179 | linkstatic = False, 180 | visibility = ["//visibility:public"], 181 | # disabled, as we directly specify the source files above. 182 | # this is to work around a bazel bug on macOS where if we 183 | # specify only deps, it produces an empty dylib 184 | # deps = [ 185 | # ":mesh-core", 186 | # ":mesh-lib", 187 | # ], 188 | deps = [ 189 | "@org_heaplayers", 190 | ], 191 | # only valid for cc_library 192 | alwayslink = 1, 193 | ) 194 | 195 | cc_test( 196 | name = "unit-tests", 197 | srcs = glob([ 198 | "testing/unit/*.cc", 199 | ]), 200 | copts = [ 201 | "-Isrc", 202 | ] + MESH_DEFAULT_COPTS, 203 | linkopts = COMMON_LINKOPTS, 204 | deps = [ 205 | ":mesh-core", 206 | "@com_google_googletest//:gtest_main", 207 | ], 208 | ) 209 | 210 | cc_test( 211 | name = "local-refill-benchmark", 212 | srcs = ["testing/benchmark/local_refill.cc"], 213 | copts = [ 214 | "-Isrc", 215 | ] + NO_BUILTIN_MALLOC + MESH_DEFAULT_COPTS, 216 | linkopts = COMMON_LINKOPTS, 217 | deps = [ 218 | ":mesh-core", 219 | "@com_google_benchmark//:benchmark", 220 | "@com_google_googletest//:gtest_main", 221 | ], 222 | ) 223 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Include current folder to guarantee headers are found by files 2 | include_directories(./) 3 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/../heap_layers-src) 4 | # include_directories(./vendor/Heap-Layers) 5 | 6 | #Create a common set of source files 7 | set(common_src 8 | d_assert.cc 9 | global_heap.cc 10 | runtime.cc 11 | real.cc 12 | meshable_arena.cc 13 | measure_rss.cc 14 | thread_local_heap.cc 15 | ) 16 | 17 | #Create the set of source files for the mesh library 18 | set(mesh_src 19 | ${common_src} 20 | libmesh.cc 21 | ) 22 | #Add a target for the mesh shared library 23 | add_library(mesh SHARED ${mesh_src}) 24 | target_link_libraries(mesh PRIVATE -pthread -ldl) 25 | if(CMAKE_BUILD_TYPE STREQUAL "Release") 26 | include(CheckIPOSupported) 27 | check_ipo_supported(RESULT HAS_IPO) 28 | if(${HAS_IPO}) 29 | set_property(TARGET mesh PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) 30 | endif() 31 | target_compile_definitions(mesh PRIVATE -D_FORTIFY_SOURCE=2) 32 | endif() 33 | 34 | #Create a set of source files for the unit tests 35 | set(unit_src 36 | ${common_src} 37 | ${google_src} 38 | testing/unit/alignment.cc 39 | testing/unit/bitmap_test.cc 40 | testing/unit/concurrent_mesh_test.cc 41 | testing/unit/mesh_test.cc 42 | testing/unit/rng_test.cc 43 | testing/unit/size_class_test.cc 44 | testing/unit/triple_mesh_test.cc 45 | ) 46 | #Add a target to the unit tests executable 47 | add_executable(unit.test ${unit_src}) 48 | target_link_libraries(unit.test PRIVATE -ldl -pthread ) 49 | 50 | set(GTEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/../googletest-src/googletest) 51 | 52 | ExternalProject_Get_Property(googletest binary_dir) 53 | set(GTEST_LIBRARY_PATH ${binary_dir}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}gtest.a) 54 | target_link_libraries(unit.test PRIVATE ${GTEST_LIBRARY_PATH}) 55 | 56 | set(GTEST_MAIN_LIBRARY_PATH ${binary_dir}/lib/${CMAKE_FIND_LIBRARY_PREFIXES}gtest_main.a) 57 | target_link_libraries(unit.test PRIVATE ${GTEST_MAIN_LIBRARY_PATH}) 58 | 59 | #Include header file paths to the unit tests 60 | target_include_directories(unit.test SYSTEM PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../googletest-src/googletest/include) 61 | 62 | #Set specific compiler flags for the unit tests 63 | target_compile_definitions(unit.test PRIVATE -DGTEST_HAS_PTHREAD=1) 64 | target_compile_options(unit.test PRIVATE -Wno-unused-const-variable -Wno-unused-variable) 65 | 66 | #Deal with coverage 67 | if(${GCOV}) 68 | add_custom_target(coverage_gcc 69 | COMMAND cd ${CMAKE_BINARY_DIR}/src/CMakeFiles/unit.test.dir/ && ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit.test 70 | COMMAND lcov -o app.info -c --directory ${CMAKE_BINARY_DIR}/src/CMakeFiles/unit.test.dir/ 71 | COMMAND genhtml app.info 72 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/coverage/ 73 | DEPENDS unit.test) 74 | endif() 75 | 76 | #todo 77 | if(${CLANGCOV}) 78 | add_custom_target(coverage_clang 79 | COMMAND cd ${CMAKE_BINARY_DIR}/src/CMakeFiles/unit.test.dir/ && ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit.test 80 | COMMAND lcov -o app.info -c --directory ${CMAKE_BINARY_DIR}/src/CMakeFiles/unit.test.dir/ 81 | COMMAND genhtml app.info 82 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/coverage/ 83 | DEPENDS unit.test) 84 | #add_custom_target(coverage_clang 85 | # COMMAND rm -f unit.test.profdata 86 | # COMMAND LLVM_PROFILE_FILE=default.profraw ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit.test 87 | # COMMAND llvm-profdata merge -sparse default.profraw -o unit.test.profdata 88 | # COMMAND "llvm-cov show -format=html -instr-profile=unit.test.profdata ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit.test -ignore-filename-regex=\\'.*(vendor|unit)/.*\\' >index.html" 89 | # COMMAND "llvm-cov report -instr-profile=unit.test.profdata ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unit.test -ignore-filename-regex='.*(vendor|unit)/.*' -use-color" 90 | # COMMAND rm -f default.profraw 91 | # WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/coverage/ 92 | # DEPENDS unit.test) 93 | endif() 94 | -------------------------------------------------------------------------------- /src/cheap_heap.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_CHEAP_HEAP_H 8 | #define MESH_CHEAP_HEAP_H 9 | 10 | #include "internal.h" 11 | #include "one_way_mmap_heap.h" 12 | 13 | namespace mesh { 14 | 15 | // Fast allocation for a single size-class 16 | template 17 | class CheapHeap : public OneWayMmapHeap { 18 | private: 19 | DISALLOW_COPY_AND_ASSIGN(CheapHeap); 20 | typedef OneWayMmapHeap SuperHeap; 21 | 22 | static_assert(maxCount <= (1 << 30), "expected maxCount <= 2^30"); 23 | static_assert(allocSize % 2 == 0, "expected allocSize to be even"); 24 | 25 | public: 26 | // cacheline-sized alignment 27 | enum { Alignment = 64 }; 28 | 29 | CheapHeap() : SuperHeap() { 30 | // TODO: check allocSize + maxCount doesn't overflow? 31 | _arena = reinterpret_cast(SuperHeap::malloc(allocSize * maxCount)); 32 | _freelist = reinterpret_cast(SuperHeap::malloc(maxCount * sizeof(void *))); 33 | hard_assert(_arena != nullptr); 34 | hard_assert(_freelist != nullptr); 35 | d_assert(reinterpret_cast(_arena) % Alignment == 0); 36 | d_assert(reinterpret_cast(_freelist) % Alignment == 0); 37 | } 38 | 39 | inline void *alloc() { 40 | if (likely(_freelistOff >= 0)) { 41 | const auto ptr = _freelist[_freelistOff]; 42 | _freelistOff--; 43 | return ptr; 44 | } 45 | 46 | const auto off = _arenaOff++; 47 | const auto ptr = ptrFromOffset(off); 48 | hard_assert(ptr < arenaEnd()); 49 | return ptr; 50 | } 51 | 52 | constexpr size_t getSize(void *ATTRIBUTE_UNUSED ptr) const { 53 | return allocSize; 54 | } 55 | 56 | inline void free(void *ptr) { 57 | d_assert(ptr >= _arena); 58 | d_assert(ptr < arenaEnd()); 59 | 60 | _freelistOff++; 61 | _freelist[_freelistOff] = ptr; 62 | } 63 | 64 | inline char *arenaBegin() const { 65 | return _arena; 66 | } 67 | 68 | inline uint32_t offsetFor(const void *ptr) const { 69 | const uintptr_t ptrval = reinterpret_cast(ptr); 70 | const uintptr_t arena = reinterpret_cast(_arena); 71 | d_assert(ptrval >= arena); 72 | return (ptrval - arena) / allocSize; 73 | } 74 | 75 | inline char *ptrFromOffset(size_t off) const { 76 | d_assert(off < _arenaOff); 77 | return _arena + off * allocSize; 78 | } 79 | 80 | inline char *arenaEnd() const { 81 | return _arena + allocSize * maxCount; 82 | } 83 | 84 | protected: 85 | char *_arena{nullptr}; 86 | void **_freelist{nullptr}; 87 | size_t _arenaOff{1}; 88 | ssize_t _freelistOff{-1}; 89 | }; 90 | 91 | class DynCheapHeap : public OneWayMmapHeap { 92 | private: 93 | DISALLOW_COPY_AND_ASSIGN(DynCheapHeap); 94 | typedef OneWayMmapHeap SuperHeap; 95 | 96 | public: 97 | // cacheline-sized alignment 98 | enum { Alignment = 64 }; 99 | 100 | DynCheapHeap() : SuperHeap() { 101 | d_assert(_allocSize % 2 == 0); 102 | } 103 | 104 | inline void init(size_t allocSize, size_t maxCount, char *arena, void **freelist) { 105 | _arena = arena; 106 | _freelist = freelist; 107 | _allocSize = allocSize; 108 | _maxCount = maxCount; 109 | hard_assert(_arena != nullptr); 110 | hard_assert(_freelist != nullptr); 111 | hard_assert(reinterpret_cast(_arena) % Alignment == 0); 112 | hard_assert(reinterpret_cast(_freelist) % Alignment == 0); 113 | } 114 | 115 | inline void *alloc() { 116 | if (likely(_freelistOff >= 0)) { 117 | const auto ptr = _freelist[_freelistOff]; 118 | _freelistOff--; 119 | return ptr; 120 | } 121 | 122 | const auto off = _arenaOff++; 123 | const auto ptr = ptrFromOffset(off); 124 | hard_assert(ptr < arenaEnd()); 125 | return ptr; 126 | } 127 | 128 | size_t getSize(void *ATTRIBUTE_UNUSED ptr) const { 129 | return _allocSize; 130 | } 131 | 132 | inline void free(void *ptr) { 133 | d_assert(ptr >= _arena); 134 | d_assert(ptr < arenaEnd()); 135 | 136 | _freelistOff++; 137 | _freelist[_freelistOff] = ptr; 138 | } 139 | 140 | inline char *arenaBegin() const { 141 | return _arena; 142 | } 143 | 144 | inline uint32_t offsetFor(const void *ptr) const { 145 | const uintptr_t ptrval = reinterpret_cast(ptr); 146 | const uintptr_t arena = reinterpret_cast(_arena); 147 | d_assert(ptrval >= arena); 148 | return (ptrval - arena) / _allocSize; 149 | } 150 | 151 | inline char *ptrFromOffset(size_t off) const { 152 | d_assert(off < _arenaOff); 153 | return _arena + off * _allocSize; 154 | } 155 | 156 | inline char *arenaEnd() const { 157 | return _arena + _allocSize * _maxCount; 158 | } 159 | 160 | protected: 161 | char *_arena{nullptr}; 162 | void **_freelist{nullptr}; 163 | size_t _arenaOff{1}; 164 | ssize_t _freelistOff{-1}; 165 | size_t _allocSize{0}; 166 | size_t _maxCount{0}; 167 | }; 168 | } // namespace mesh 169 | 170 | #endif // MESH_CHEAP_HEAP_H 171 | -------------------------------------------------------------------------------- /src/copts.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Mesh Authors. All rights reserved. 2 | # Use of this source code is governed by the Apache License, 3 | # Version 2.0, that can be found in the LICENSE file. 4 | 5 | COMMON_FLAGS = [ 6 | # warn on lots of stuff; this is cargo-culted from the old Make build system 7 | "-Wall", 8 | "-Wextra", 9 | "-Werror=pointer-arith", 10 | "-pedantic", 11 | "-Wno-unused-parameter", 12 | "-Wno-unused-variable", 13 | "-Woverloaded-virtual", 14 | "-Werror=return-type", 15 | "-Wtype-limits", 16 | "-Wempty-body", 17 | "-Winvalid-offsetof", 18 | "-Wvariadic-macros", 19 | "-Wcast-align", 20 | ] 21 | 22 | MESH_LLVM_FLAGS = [] 23 | 24 | MESH_GCC_FLAGS = [ 25 | "-Wa,--noexecstack", 26 | ] 27 | 28 | MESH_DEFAULT_COPTS = select({ 29 | "//src:llvm": COMMON_FLAGS + MESH_LLVM_FLAGS, 30 | "//conditions:default": COMMON_FLAGS + MESH_GCC_FLAGS, 31 | }) 32 | -------------------------------------------------------------------------------- /src/d_assert.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | #include // for abort 8 | 9 | #include 10 | 11 | #include "common.h" 12 | 13 | #include "rpl_printf.c" 14 | 15 | // mutex protecting debug and __mesh_assert_fail to avoid concurrent 16 | // use of static buffers by multiple threads 17 | inline static mutex *getAssertMutex(void) { 18 | static char assertBuf[sizeof(std::mutex)]; 19 | static mutex *assertMutex = new (assertBuf) mutex(); 20 | 21 | return assertMutex; 22 | } 23 | 24 | // threadsafe printf-like debug statements safe for use in an 25 | // allocator (it will never call into malloc or free to allocate 26 | // memory) 27 | void mesh::debug(const char *fmt, ...) { 28 | constexpr size_t buf_len = 4096; 29 | static char buf[buf_len]; 30 | std::lock_guard lock(*getAssertMutex()); 31 | 32 | va_list args; 33 | 34 | va_start(args, fmt); 35 | int len = rpl_vsnprintf(buf, buf_len - 1, fmt, args); 36 | va_end(args); 37 | 38 | buf[buf_len - 1] = 0; 39 | if (len > 0) { 40 | auto _ __attribute__((unused)) = write(STDERR_FILENO, buf, len); 41 | // ensure a trailing newline is written out 42 | if (buf[len - 1] != '\n') 43 | _ = write(STDERR_FILENO, "\n", 1); 44 | } 45 | } 46 | 47 | // out-of-line function called to report an error and exit the program 48 | // when an assertion failed. 49 | void mesh::internal::__mesh_assert_fail(const char *assertion, const char *file, const char *func, int line, 50 | const char *fmt, ...) { 51 | constexpr size_t buf_len = 4096; 52 | constexpr size_t usr_len = 512; 53 | static char buf[buf_len]; 54 | static char usr[usr_len]; 55 | std::lock_guard lock(*getAssertMutex()); 56 | 57 | va_list args; 58 | 59 | va_start(args, fmt); 60 | (void)rpl_vsnprintf(usr, usr_len - 1, fmt, args); 61 | va_end(args); 62 | 63 | usr[usr_len - 1] = 0; 64 | 65 | int len = rpl_snprintf(buf, buf_len - 1, "%s:%d:%s: ASSERTION '%s' FAILED: %s\n", file, line, func, assertion, usr); 66 | if (len > 0) { 67 | auto _ __attribute__((unused)) = write(STDERR_FILENO, buf, len); 68 | } 69 | 70 | // void *array[32]; 71 | // size_t size = backtrace(array, 10); 72 | // backtrace_symbols_fd(array, size, STDERR_FILENO); 73 | 74 | abort(); 75 | } 76 | -------------------------------------------------------------------------------- /src/fbsd_wrapper.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2024 The Heap-Layers and Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | To use this library, 16 | you only need to define the following allocation functions: 17 | 18 | - xxmalloc 19 | - xxfree 20 | - xxmalloc_usable_size 21 | - xxmalloc_lock 22 | - xxmalloc_unlock 23 | 24 | See the extern "C" block below for function prototypes and more 25 | details. YOU SHOULD NOT NEED TO MODIFY ANY OF THE CODE HERE TO 26 | SUPPORT ANY ALLOCATOR. 27 | 28 | 29 | LIMITATIONS: 30 | 31 | - This wrapper assumes that the underlying allocator will do "the 32 | right thing" when xxfree() is called with a pointer internal to an 33 | allocated object. Header-based allocators, for example, need not 34 | apply. 35 | 36 | */ 37 | 38 | #define WEAK(x) __attribute__((weak, alias(#x))) 39 | #ifndef __THROW 40 | #define __THROW 41 | #endif 42 | 43 | #ifndef CACHELINE_ALIGNED_FN 44 | #define ATTRIBUTE_ALIGNED(s) __attribute__((aligned(s))) 45 | #define CACHELINE_SIZE 64 46 | #define CACHELINE_ALIGNED_FN ATTRIBUTE_ALIGNED(CACHELINE_SIZE) 47 | #endif 48 | 49 | #define CUSTOM_PREFIX(x) mesh_##x 50 | 51 | #define WEAK_REDEF1(type, fname, arg1) MESH_EXPORT type fname(arg1) __THROW WEAK(mesh_##fname) 52 | #define WEAK_REDEF2(type, fname, arg1, arg2) MESH_EXPORT type fname(arg1, arg2) __THROW WEAK(mesh_##fname) 53 | #define WEAK_REDEF3(type, fname, arg1, arg2, arg3) MESH_EXPORT type fname(arg1, arg2, arg3) __THROW WEAK(mesh_##fname) 54 | 55 | extern "C" { 56 | WEAK_REDEF1(void *, malloc, size_t); 57 | WEAK_REDEF1(void, free, void *); 58 | WEAK_REDEF1(void, cfree, void *); 59 | WEAK_REDEF2(void *, calloc, size_t, size_t); 60 | WEAK_REDEF2(void *, realloc, void *, size_t); 61 | WEAK_REDEF3(void *, reallocarray, void *, size_t, size_t); 62 | WEAK_REDEF2(void *, memalign, size_t, size_t); 63 | WEAK_REDEF3(int, posix_memalign, void **, size_t, size_t); 64 | WEAK_REDEF2(void *, aligned_alloc, size_t, size_t); 65 | WEAK_REDEF1(size_t, malloc_usable_size, const void *); 66 | } 67 | 68 | #include "wrapper.cc" 69 | -------------------------------------------------------------------------------- /src/fixed_array.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_FIXED_ARRAY_H 8 | #define MESH_FIXED_ARRAY_H 9 | 10 | #include 11 | 12 | namespace mesh { 13 | 14 | // enables iteration through the miniheaps in an array 15 | template 16 | class FixedArrayIter : public std::iterator { 17 | public: 18 | FixedArrayIter(const FixedArray &a, const uint32_t i) : _i(i), _array(a) { 19 | } 20 | FixedArrayIter &operator++() { 21 | if (unlikely(_i + 1 >= _array.size())) { 22 | _i = _array.size(); 23 | return *this; 24 | } 25 | 26 | _i++; 27 | return *this; 28 | } 29 | bool operator==(const FixedArrayIter &rhs) const { 30 | return &_array == &rhs._array && _i == rhs._i; 31 | } 32 | bool operator!=(const FixedArrayIter &rhs) const { 33 | return &_array != &rhs._array || _i != rhs._i; 34 | } 35 | typename FixedArray::value_type operator*() { 36 | return _array[_i]; 37 | } 38 | 39 | private: 40 | uint32_t _i; 41 | const FixedArray &_array; 42 | }; 43 | 44 | template 45 | class FixedArray { 46 | private: 47 | DISALLOW_COPY_AND_ASSIGN(FixedArray); 48 | 49 | public: 50 | typedef T *value_type; 51 | typedef FixedArrayIter> iterator; 52 | typedef FixedArrayIter> const const_iterator; 53 | 54 | FixedArray() { 55 | d_assert(_size == 0); 56 | #ifndef NDEBUG 57 | for (uint32_t i = 0; i < Capacity; i++) { 58 | d_assert(_objects[i] == nullptr); 59 | } 60 | #endif 61 | } 62 | 63 | ~FixedArray() { 64 | clear(); 65 | } 66 | 67 | uint32_t size() const { 68 | return _size; 69 | } 70 | 71 | bool full() const { 72 | return _size == Capacity; 73 | } 74 | 75 | void clear() { 76 | memset(_objects, 0, Capacity * sizeof(T *)); 77 | _size = 0; 78 | } 79 | 80 | void append(T *obj) { 81 | hard_assert(_size < Capacity); 82 | _objects[_size] = obj; 83 | _size++; 84 | } 85 | 86 | T *operator[](uint32_t i) const { 87 | // d_assert(i < _size); 88 | return _objects[i]; 89 | } 90 | 91 | T **array_begin() { 92 | return &_objects[0]; 93 | } 94 | T **array_end() { 95 | return &_objects[size()]; 96 | } 97 | 98 | iterator begin() { 99 | return iterator(*this, 0); 100 | } 101 | iterator end() { 102 | return iterator(*this, size()); 103 | } 104 | const_iterator begin() const { 105 | return iterator(*this, 0); 106 | } 107 | const_iterator end() const { 108 | return iterator(*this, size()); 109 | } 110 | const_iterator cbegin() const { 111 | return iterator(*this, 0); 112 | } 113 | const_iterator cend() const { 114 | return iterator(*this, size()); 115 | } 116 | 117 | private: 118 | T *_objects[Capacity]{}; 119 | uint32_t _size{0}; 120 | }; 121 | 122 | } // namespace mesh 123 | 124 | #endif // MESH_FIXED_ARRAY_H 125 | -------------------------------------------------------------------------------- /src/gnu_wrapper.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Heap-Layers and Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #ifndef __GNUC__ 7 | #error "This file requires the GNU compiler." 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /* 19 | To use this library, 20 | you only need to define the following allocation functions: 21 | 22 | - xxmalloc 23 | - xxfree 24 | - xxmalloc_usable_size 25 | - xxmalloc_lock 26 | - xxmalloc_unlock 27 | 28 | See the extern "C" block below for function prototypes and more 29 | details. YOU SHOULD NOT NEED TO MODIFY ANY OF THE CODE HERE TO 30 | SUPPORT ANY ALLOCATOR. 31 | 32 | 33 | LIMITATIONS: 34 | 35 | - This wrapper assumes that the underlying allocator will do "the 36 | right thing" when xxfree() is called with a pointer internal to an 37 | allocated object. Header-based allocators, for example, need not 38 | apply. 39 | 40 | */ 41 | 42 | #define WEAK(x) __attribute__((weak, alias(#x))) 43 | #ifndef __THROW 44 | #define __THROW 45 | #endif 46 | 47 | #ifndef CACHELINE_ALIGNED_FN 48 | #define ATTRIBUTE_ALIGNED(s) __attribute__((aligned(s))) 49 | #define CACHELINE_SIZE 64 50 | #define CACHELINE_ALIGNED_FN ATTRIBUTE_ALIGNED(CACHELINE_SIZE) 51 | #endif 52 | 53 | #define CUSTOM_PREFIX(x) mesh_##x 54 | 55 | #define WEAK_REDEF1(type, fname, arg1) MESH_EXPORT type fname(arg1) __THROW WEAK(mesh_##fname) 56 | #define WEAK_REDEF2(type, fname, arg1, arg2) MESH_EXPORT type fname(arg1, arg2) __THROW WEAK(mesh_##fname) 57 | #define WEAK_REDEF3(type, fname, arg1, arg2, arg3) MESH_EXPORT type fname(arg1, arg2, arg3) __THROW WEAK(mesh_##fname) 58 | 59 | extern "C" { 60 | WEAK_REDEF1(void *, malloc, size_t); 61 | WEAK_REDEF1(void, free, void *); 62 | WEAK_REDEF1(void, cfree, void *); 63 | WEAK_REDEF2(void *, calloc, size_t, size_t); 64 | WEAK_REDEF2(void *, realloc, void *, size_t); 65 | WEAK_REDEF2(void *, memalign, size_t, size_t); 66 | WEAK_REDEF3(int, posix_memalign, void **, size_t, size_t); 67 | WEAK_REDEF2(void *, aligned_alloc, size_t, size_t); 68 | WEAK_REDEF1(size_t, malloc_usable_size, void *); 69 | } 70 | 71 | #include "wrapper.cc" 72 | -------------------------------------------------------------------------------- /src/measure_rss.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | #include 8 | 9 | #if defined(__linux__) 10 | #include 11 | #include 12 | #include 13 | #elif defined(__APPLE__) 14 | #include 15 | #include 16 | #elif defined(__FreeBSD__) 17 | #include 18 | #include 19 | #include 20 | #endif 21 | #include 22 | 23 | #include "measure_rss.h" 24 | 25 | extern "C" int get_rss_kb() { 26 | #if defined(__linux__) 27 | constexpr size_t bufLen = 4096; 28 | char buf[bufLen] = {0}; 29 | 30 | int fd = open("/proc/self/status", O_RDONLY | O_CLOEXEC); 31 | if (fd < 0) 32 | return -1; 33 | 34 | ssize_t bytesRead = read(fd, buf, bufLen - 1); 35 | close(fd); 36 | 37 | if (bytesRead == -1) 38 | return -1; 39 | 40 | for (char *line = buf; line != nullptr && *line != 0; line = strchr(line, '\n')) { 41 | if (*line == '\n') 42 | line++; 43 | if (strncmp(line, "VmRSS:", strlen("VmRSS:")) != 0) { 44 | continue; 45 | } 46 | 47 | char *rssString = line + strlen("VmRSS:"); 48 | return atoi(rssString); 49 | } 50 | 51 | return -1; 52 | #elif defined(__APPLE__) 53 | struct task_basic_info info; 54 | task_t curtask = MACH_PORT_NULL; 55 | mach_msg_type_number_t cnt = TASK_BASIC_INFO_COUNT; 56 | int pid = getpid(); 57 | 58 | if (task_for_pid(current_task(), pid, &curtask) != KERN_SUCCESS) 59 | return -1; 60 | 61 | if (task_info(curtask, TASK_BASIC_INFO, reinterpret_cast(&info), &cnt) != KERN_SUCCESS) 62 | return -1; 63 | 64 | return static_cast(info.resident_size); 65 | #elif defined(__FreeBSD__) 66 | struct kinfo_proc info; 67 | size_t len = sizeof(info); 68 | int pid = getpid(); 69 | int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 70 | 71 | if (sysctl(mib, 4, &info, &len, nullptr, 0) != 0) 72 | return -1; 73 | 74 | return static_cast(info.ki_rssize); 75 | #endif 76 | } 77 | -------------------------------------------------------------------------------- /src/measure_rss.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_MEASURE_RSS_H 8 | #define MESH_MEASURE_RSS_H 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | int get_rss_kb(void); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif // MESH_MEASURE_RSS_H 21 | -------------------------------------------------------------------------------- /src/meshing.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_MESHING_H 8 | #define MESH_MESHING_H 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "bitmap.h" 15 | #include "common.h" 16 | #include "internal.h" 17 | #include "mini_heap.h" 18 | 19 | namespace mesh { 20 | 21 | using internal::Bitmap; 22 | 23 | inline bool bitmapsMeshable(const Bitmap::word_t *__restrict__ bitmap1, const Bitmap::word_t *__restrict__ bitmap2, 24 | size_t byteLen) noexcept { 25 | d_assert(reinterpret_cast(bitmap1) % 16 == 0); 26 | d_assert(reinterpret_cast(bitmap2) % 16 == 0); 27 | d_assert(byteLen >= 8); 28 | d_assert(byteLen % 8 == 0); 29 | 30 | // because we hold the global lock (so no miniheap can transition 31 | // from 'free' to 'attached'), the only possible data race we have 32 | // here is our read with a write changing a bit from 1 -> 0. That 33 | // makes this race benign - we may have a false positive if an 34 | // allocation is freed (it may cause 2 bitmaps to look like they 35 | // overlap when _now_ they don't actually), but it won't cause 36 | // correctness issues. 37 | const auto bitmapL = (const uint64_t *)__builtin_assume_aligned(bitmap1, 16); 38 | const auto bitmapR = (const uint64_t *)__builtin_assume_aligned(bitmap2, 16); 39 | 40 | uint64_t result = 0; 41 | 42 | for (size_t i = 0; i < byteLen / sizeof(size_t); i++) { 43 | result |= bitmapL[i] & bitmapR[i]; 44 | } 45 | 46 | return result == 0; 47 | } 48 | 49 | namespace method { 50 | 51 | // split miniheaps into two lists in a random order 52 | void halfSplit(MWC &prng, MiniHeapListEntry *miniheaps, SplitArray &left, size_t &leftSize, SplitArray &right, 53 | size_t &rightSize) noexcept; 54 | 55 | void shiftedSplitting(MWC &prng, MiniHeapListEntry *miniheaps, SplitArray &left, SplitArray &right, 56 | const function &&)> &meshFound) noexcept; 57 | } // namespace method 58 | } // namespace mesh 59 | 60 | #endif // MESH_MESHING_H 61 | -------------------------------------------------------------------------------- /src/mmap_heap.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Heap-Layers and Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_MMAP_HEAP_H 8 | #define MESH_MMAP_HEAP_H 9 | 10 | #if defined(_WIN32) 11 | #error "TODO" 12 | #include 13 | #else 14 | // UNIX 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #endif 23 | 24 | #include "internal.h" 25 | #include "one_way_mmap_heap.h" 26 | 27 | namespace mesh { 28 | 29 | // MmapHeap extends OneWayMmapHeap to track allocated address space 30 | // and will free memory with calls to munmap. 31 | class MmapHeap : public OneWayMmapHeap { 32 | private: 33 | DISALLOW_COPY_AND_ASSIGN(MmapHeap); 34 | typedef OneWayMmapHeap SuperHeap; 35 | 36 | public: 37 | enum { Alignment = MmapWrapper::Alignment }; 38 | 39 | MmapHeap() : SuperHeap() { 40 | } 41 | 42 | inline void *malloc(size_t sz) { 43 | auto ptr = map(sz, MAP_PRIVATE | MAP_ANONYMOUS, -1); 44 | 45 | d_assert(_vmaMap.find(ptr) == _vmaMap.end()); 46 | _vmaMap[ptr] = sz; 47 | d_assert(_vmaMap.find(ptr) != _vmaMap.end()); 48 | d_assert(_vmaMap[ptr] == sz); 49 | 50 | return ptr; 51 | } 52 | 53 | inline size_t getSize(void *ptr) const { 54 | auto entry = _vmaMap.find(ptr); 55 | if (unlikely(entry == _vmaMap.end())) { 56 | debug("mmap: invalid getSize: %p", ptr); 57 | abort(); 58 | return 0; 59 | } 60 | return entry->second; 61 | } 62 | 63 | inline bool inBounds(void *ptr) const { 64 | auto entry = _vmaMap.find(ptr); 65 | if (unlikely(entry == _vmaMap.end())) { 66 | return false; 67 | } 68 | // FIXME: this isn't right -- we want inclusion not exact match 69 | return true; 70 | } 71 | 72 | inline void free(void *ptr) { 73 | auto entry = _vmaMap.find(ptr); 74 | if (unlikely(entry == _vmaMap.end())) { 75 | debug("mmap: invalid free, possibly from memalign: %p", ptr); 76 | // abort(); 77 | return; 78 | } 79 | 80 | auto sz = entry->second; 81 | 82 | munmap(ptr, sz); 83 | // madvise(ptr, sz, MADV_DONTNEED); 84 | // mprotect(ptr, sz, PROT_NONE); 85 | 86 | _vmaMap.erase(entry); 87 | d_assert(_vmaMap.find(ptr) == _vmaMap.end()); 88 | } 89 | 90 | // return the sum of the sizes of all large allocations 91 | size_t arenaSize() const { 92 | size_t sz = 0; 93 | for (auto it = _vmaMap.begin(); it != _vmaMap.end(); it++) { 94 | sz += it->second; 95 | } 96 | return sz; 97 | } 98 | 99 | protected: 100 | internal::unordered_map _vmaMap{}; 101 | }; 102 | } // namespace mesh 103 | 104 | #endif // MESH_MESH_MMAP_H 105 | -------------------------------------------------------------------------------- /src/one_way_mmap_heap.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Heap-Layers and Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_ONE_WAY_MMAP_HEAP_H 8 | #define MESH_ONE_WAY_MMAP_HEAP_H 9 | 10 | #if defined(_WIN32) 11 | #error "TODO" 12 | #include 13 | #else 14 | // UNIX 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #endif 22 | 23 | #include "common.h" 24 | 25 | #ifndef HL_MMAP_PROTECTION_MASK 26 | #error "define HL_MMAP_PROTECTION_MASK before including mmapheap.h" 27 | #endif 28 | 29 | #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) 30 | #define MAP_ANONYMOUS MAP_ANON 31 | #endif 32 | 33 | namespace mesh { 34 | 35 | // OneWayMmapHeap allocates address space through calls to mmap and 36 | // will never unmap address space. 37 | class OneWayMmapHeap { 38 | private: 39 | DISALLOW_COPY_AND_ASSIGN(OneWayMmapHeap); 40 | 41 | public: 42 | enum { Alignment = MmapWrapper::Alignment }; 43 | 44 | OneWayMmapHeap() { 45 | } 46 | 47 | inline void *map(size_t sz, int flags, int fd = -1) { 48 | if (sz == 0) 49 | return nullptr; 50 | 51 | // Round up to the size of a page. 52 | sz = (sz + kPageSize - 1) & (size_t) ~(kPageSize - 1); 53 | 54 | void *ptr = mmap(nullptr, sz, HL_MMAP_PROTECTION_MASK, flags, fd, 0); 55 | if (ptr == MAP_FAILED) 56 | abort(); 57 | 58 | d_assert(reinterpret_cast(ptr) % Alignment == 0); 59 | 60 | return ptr; 61 | } 62 | 63 | inline void *malloc(size_t sz) { 64 | return map(sz, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1); 65 | } 66 | 67 | inline size_t getSize(void *ATTRIBUTE_UNUSED ptr) const { 68 | return 0; 69 | } 70 | 71 | inline void free(void *ATTRIBUTE_UNUSED ptr) { 72 | } 73 | }; 74 | 75 | } // namespace mesh 76 | 77 | #endif // MESH_ONE_WAY_MMAP_HEAP_H 78 | -------------------------------------------------------------------------------- /src/partitioned_heap.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_PARTITIONED_HEAP_H 8 | #define MESH_PARTITIONED_HEAP_H 9 | 10 | // we can only include "common.h", as the partitioned heap is used as 11 | // the allocator-internal heap in "internal.h" 12 | #include "common.h" 13 | 14 | #include "cheap_heap.h" 15 | #include "one_way_mmap_heap.h" 16 | 17 | namespace mesh { 18 | 19 | static constexpr int kPartitionedHeapNBins = 16; 20 | static constexpr int kPartitionedHeapArenaSize = 512 * 1024 * 1024; // 512 MB 21 | static constexpr int kPartitionedHeapSizePer = kPartitionedHeapArenaSize / kPartitionedHeapNBins; 22 | 23 | // Fast allocation for multiple size classes 24 | class PartitionedHeap : public OneWayMmapHeap { 25 | private: 26 | DISALLOW_COPY_AND_ASSIGN(PartitionedHeap); 27 | typedef OneWayMmapHeap SuperHeap; 28 | 29 | public: 30 | PartitionedHeap() : SuperHeap() { 31 | _smallArena = reinterpret_cast(SuperHeap::malloc(kPartitionedHeapArenaSize)); 32 | hard_assert(_smallArena != nullptr); 33 | _smallArenaEnd = _smallArena + kPartitionedHeapArenaSize; 34 | 35 | auto freelist = reinterpret_cast(SuperHeap::malloc(kPartitionedHeapArenaSize)); 36 | hard_assert(freelist != nullptr); 37 | 38 | for (size_t i = 0; i < kPartitionedHeapNBins; ++i) { 39 | auto arenaStart = _smallArena + i * kPartitionedHeapSizePer; 40 | auto freelistStart = freelist + i * kPartitionedHeapSizePer; 41 | 42 | const auto allocSize = powerOfTwo::ByteSizeForClass(i); 43 | const auto maxCount = kPartitionedHeapSizePer / allocSize; 44 | 45 | // mesh::debug("internal heap of allocSize %zu\n", allocSize); 46 | 47 | _smallHeaps[i].init(allocSize, maxCount, arenaStart, reinterpret_cast(freelistStart)); 48 | } 49 | 50 | // TODO: calc freelist + arena offsets 51 | } 52 | 53 | inline void *malloc(size_t sz) { 54 | const auto sizeClass = powerOfTwo::ClassForByteSize(sz); 55 | 56 | if (unlikely(sizeClass >= kPartitionedHeapNBins)) { 57 | auto res = _bigHeap.malloc(sz); 58 | // debug("internalHeap::malloc(%zu): %p (big)\n", sz, res); 59 | return res; 60 | } 61 | 62 | auto res = _smallHeaps[sizeClass].alloc(); 63 | // debug("internalHeap::malloc(%zu): %p\n", sz, res); 64 | return res; 65 | } 66 | 67 | inline void free(void *ptr) { 68 | // debug("internalHeap::free(): %p\n", ptr); 69 | if (unlikely(!contains(ptr))) { 70 | _bigHeap.free(ptr); 71 | return; 72 | } 73 | 74 | const auto sizeClass = getSizeClass(ptr); 75 | d_assert(sizeClass >= 0); 76 | d_assert(sizeClass < kPartitionedHeapNBins); 77 | 78 | return _smallHeaps[sizeClass].free(ptr); 79 | } 80 | 81 | inline size_t getSize(void *ptr) { 82 | if (unlikely(!contains(ptr))) { 83 | return _bigHeap.getSize(ptr); 84 | } 85 | 86 | return powerOfTwo::ByteSizeForClass(getSizeClass(ptr)); 87 | } 88 | 89 | // must be protected with a contains() 90 | inline int getSizeClass(void *ptr) const { 91 | const char *ptrval = reinterpret_cast(ptr); 92 | const auto sizeClass = (ptrval - _smallArena) / kPartitionedHeapSizePer; 93 | d_assert(sizeClass >= 0); 94 | d_assert(sizeClass < kPartitionedHeapNBins); 95 | return sizeClass; 96 | } 97 | 98 | inline bool contains(void *ptr) const { 99 | const auto ptrval = reinterpret_cast(ptr); 100 | return ptrval >= _smallArena && ptrval < _smallArenaEnd; 101 | } 102 | 103 | private: 104 | char *_smallArena{nullptr}; 105 | char *_smallArenaEnd{nullptr}; 106 | DynCheapHeap _smallHeaps[kPartitionedHeapNBins]{}; 107 | HL::MmapHeap _bigHeap{}; 108 | }; 109 | } // namespace mesh 110 | 111 | #endif // MESH_PARTITIONED_HEAP_H 112 | -------------------------------------------------------------------------------- /src/plasma/mesh.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef PLASMA__MESH_H 8 | #define PLASMA__MESH_H 9 | 10 | #include 11 | 12 | #define MESH_VERSION_MAJOR 1 13 | #define MESH_VERSION_MINOR 0 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | // Same API as je_mallctl, allows a program to query stats and set 20 | // allocator-related options. 21 | int mesh_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen); 22 | 23 | // 0 if not in bounds, 1 if is. 24 | int mesh_in_bounds(void *ptr); 25 | 26 | // returns the usable size of an allocation 27 | size_t mesh_usable_size(void *ptr); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif /* PLASMA__MESH_H */ 34 | -------------------------------------------------------------------------------- /src/real.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | 8 | #include "real.h" 9 | 10 | #include "common.h" 11 | 12 | #define DEFINE_REAL(name) decltype(::name) *name 13 | #define INIT_REAL(name, handle) \ 14 | do { \ 15 | name = reinterpret_cast(dlsym(handle, #name)); \ 16 | hard_assert_msg(name != nullptr, "mesh::real: expected %s", #name); \ 17 | } while (false) 18 | 19 | namespace mesh { 20 | namespace real { 21 | #ifdef __linux__ 22 | DEFINE_REAL(epoll_pwait); 23 | DEFINE_REAL(epoll_wait); 24 | DEFINE_REAL(recv); 25 | DEFINE_REAL(recvmsg); 26 | #endif 27 | 28 | DEFINE_REAL(pthread_create); 29 | DEFINE_REAL(pthread_exit); 30 | 31 | DEFINE_REAL(sigaction); 32 | DEFINE_REAL(sigprocmask); 33 | 34 | void init() { 35 | static mutex initLock; 36 | static bool initialized; 37 | 38 | lock_guard lock(initLock); 39 | if (initialized) 40 | return; 41 | initialized = true; 42 | #ifdef __linux__ 43 | INIT_REAL(epoll_pwait, RTLD_NEXT); 44 | INIT_REAL(epoll_wait, RTLD_NEXT); 45 | INIT_REAL(recv, RTLD_NEXT); 46 | INIT_REAL(recvmsg, RTLD_NEXT); 47 | #endif 48 | 49 | INIT_REAL(pthread_create, RTLD_NEXT); 50 | INIT_REAL(pthread_exit, RTLD_NEXT); 51 | 52 | INIT_REAL(sigaction, RTLD_NEXT); 53 | INIT_REAL(sigprocmask, RTLD_NEXT); 54 | } 55 | } // namespace real 56 | } // namespace mesh 57 | -------------------------------------------------------------------------------- /src/real.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | #include 8 | 9 | #ifdef __linux__ 10 | #include 11 | #include 12 | #include 13 | #endif 14 | 15 | #pragma once 16 | #ifndef MESH_REAL_H 17 | #define MESH_REAL_H 18 | 19 | #define DECLARE_REAL(name) extern decltype(::name) *name 20 | 21 | namespace mesh { 22 | namespace real { 23 | void init(); 24 | 25 | #ifdef __linux__ 26 | DECLARE_REAL(epoll_pwait); 27 | DECLARE_REAL(epoll_wait); 28 | DECLARE_REAL(recv); 29 | DECLARE_REAL(recvmsg); 30 | #endif 31 | 32 | DECLARE_REAL(pthread_create); 33 | DECLARE_REAL(pthread_exit); 34 | 35 | DECLARE_REAL(sigaction); 36 | DECLARE_REAL(sigprocmask); 37 | } // namespace real 38 | } // namespace mesh 39 | 40 | #endif // MESH_REAL_H 41 | -------------------------------------------------------------------------------- /src/rng/mwc64.h: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | 3 | #ifndef MESH_MWC64_H 4 | #define MESH_MWC64_H 5 | 6 | #include 7 | 8 | #include "../common.h" 9 | 10 | class MWC64 { 11 | inline void ATTRIBUTE_ALWAYS_INLINE init(uint64_t seed1, uint64_t seed2) { 12 | _x = seed1; 13 | _x <<= 32; 14 | _x += seed2; 15 | _c = 123456123456123456ULL; 16 | _index = 2; 17 | } 18 | 19 | inline uint64_t ATTRIBUTE_ALWAYS_INLINE MWC() { 20 | _t = (_x << 58) + _c; 21 | _c = _x >> 6; 22 | _x += _t; 23 | _c += (_x < _t); 24 | return _x; 25 | } 26 | 27 | uint64_t _x; 28 | uint64_t _c; 29 | uint64_t _t; 30 | uint64_t _value; 31 | int _index; 32 | 33 | public: 34 | MWC64() { 35 | auto a = mesh::internal::seed(); 36 | auto b = mesh::internal::seed(); 37 | init(a, b); 38 | } 39 | 40 | MWC64(uint64_t seed1, uint64_t seed2) { 41 | init(seed1, seed2); 42 | } 43 | 44 | inline uint64_t ATTRIBUTE_ALWAYS_INLINE next() { 45 | if (_index == 2) { 46 | _value = MWC(); 47 | _index = 0; 48 | } 49 | // grab either the top or bottom 32-bits of the 64-bit _value 50 | uint32_t v = ((uint32_t *)&_value)[_index]; 51 | _index++; 52 | return v; 53 | } 54 | }; 55 | 56 | static_assert(sizeof(MWC64) == 40, "MWC64 not expected size!"); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/runtime.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #pragma once 7 | #ifndef MESH_RUNTIME_H 8 | #define MESH_RUNTIME_H 9 | 10 | #include 11 | #include // for stack_t 12 | 13 | #ifdef __FreeBSD__ 14 | #include 15 | extern "C" { 16 | static pid_t gettid(void) { 17 | return static_cast(pthread_getthreadid_np()); 18 | } 19 | } 20 | #endif 21 | 22 | #include "internal.h" 23 | 24 | #include "real.h" 25 | 26 | #include "global_heap.h" 27 | #include "mmap_heap.h" 28 | 29 | #include "heaplayers.h" 30 | 31 | namespace mesh { 32 | 33 | // function passed to pthread_create 34 | typedef void *(*PthreadFn)(void *); 35 | 36 | class Runtime { 37 | private: 38 | DISALLOW_COPY_AND_ASSIGN(Runtime); 39 | 40 | // ensure we don't mistakenly create additional runtime instances 41 | explicit Runtime(); 42 | 43 | public: 44 | void lock(); 45 | void unlock(); 46 | 47 | inline GlobalHeap &heap() { 48 | return _heap; 49 | } 50 | 51 | void startBgThread(); 52 | void initMaxMapCount(); 53 | 54 | // we need to wrap pthread_create and pthread_exit so that we can 55 | // install our segfault handler and cleanup thread-local heaps. 56 | int createThread(pthread_t *thread, const pthread_attr_t *attr, mesh::PthreadFn startRoutine, void *arg); 57 | void ATTRIBUTE_NORETURN exitThread(void *retval); 58 | 59 | void setMeshPeriodMs(std::chrono::milliseconds period) { 60 | _heap.setMeshPeriodMs(period); 61 | } 62 | 63 | #ifdef __linux__ 64 | int epollWait(int __epfd, struct epoll_event *__events, int __maxevents, int __timeout); 65 | int epollPwait(int __epfd, struct epoll_event *__events, int __maxevents, int __timeout, const __sigset_t *__ss); 66 | ssize_t recv(int sockfd, void *buf, size_t len, int flags); 67 | ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); 68 | #endif 69 | 70 | int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 71 | int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 72 | 73 | struct StartThreadArgs { 74 | explicit StartThreadArgs(Runtime *runtime_, PthreadFn startRoutine_, void *arg_) 75 | : runtime(runtime_), startRoutine(startRoutine_), arg(arg_) { 76 | } 77 | 78 | Runtime *runtime; 79 | PthreadFn startRoutine; 80 | void *arg; 81 | }; 82 | 83 | static void *startThread(StartThreadArgs *threadArgs); 84 | 85 | // so we can call from the libmesh init function 86 | void createSignalFd(); 87 | void installSegfaultHandler(); 88 | 89 | void updatePid() { 90 | _pid = getpid(); 91 | } 92 | 93 | pid_t pid() const { 94 | return _pid; 95 | } 96 | 97 | private: 98 | // initialize our pointer to libc's pthread_create, etc. This 99 | // happens lazily, as the dynamic linker's dlopen calls into malloc 100 | // for memory allocation, so if we try to do this in MeshHeaps's 101 | // constructor we deadlock before main even runs. 102 | void initThreads(); 103 | 104 | static void segfaultHandler(int sig, siginfo_t *siginfo, void *context); 105 | 106 | static void *bgThread(void *arg); 107 | 108 | friend Runtime &runtime(); 109 | 110 | mutex _mutex{}; 111 | int _signalFd{-2}; 112 | pid_t _pid{}; 113 | GlobalHeap _heap{}; 114 | }; 115 | 116 | // get a reference to the Runtime singleton 117 | inline Runtime &runtime() { 118 | // force alignment by using a buffer of doubles. 119 | static double buf[(sizeof(Runtime) + sizeof(double) - 1) / sizeof(double)]; 120 | static Runtime *runtimePtr = new (buf) Runtime{}; 121 | return *runtimePtr; 122 | } 123 | } // namespace mesh 124 | 125 | #endif // MESH_RUNTIME_H 126 | -------------------------------------------------------------------------------- /src/static/if.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | 3 | /** 4 | * @file staticif.h 5 | * @brief Statically returns a VALUE based on a conditional. 6 | * @author Emery Berger 7 | * @note Copyright (C) 2005 by Emery Berger, University of Massachusetts Amherst. 8 | */ 9 | 10 | #pragma once 11 | #ifndef MESH_STATIC__IF_H 12 | #define MESH_STATIC__IF_H 13 | 14 | template 15 | TYPE constexpr staticif(bool v, TYPE a, TYPE b) { 16 | return (v ? a : b); 17 | } 18 | 19 | #else 20 | 21 | template 22 | class StaticIf; 23 | 24 | template 25 | class StaticIf { 26 | public: 27 | enum { VALUE = a }; 28 | }; 29 | 30 | template 31 | class StaticIf { 32 | public: 33 | enum { VALUE = b }; 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/static/log.h: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | 3 | /** 4 | * @file staticlog.h 5 | * @brief Statically returns the log (base 2) of a value. 6 | * @author Emery Berger 7 | * @note Copyright (C) 2005 by Emery Berger, University of Massachusetts Amherst. 8 | */ 9 | 10 | #pragma once 11 | #ifndef MESH_STATIC__LOG_H 12 | #define MESH_STATIC__LOG_H 13 | 14 | #include "if.h" 15 | 16 | int constexpr staticlog(int v) { 17 | return ((v == 1) ? 0 : (v == 2) ? 1 : (v > 1) ? staticlog(v / 2) + 1 : 0); 18 | } 19 | 20 | #else 21 | 22 | template 23 | class StaticLog; 24 | 25 | template <> 26 | class StaticLog<1> { 27 | public: 28 | enum { VALUE = 0 }; 29 | }; 30 | 31 | template <> 32 | class StaticLog<2> { 33 | public: 34 | enum { VALUE = 1 }; 35 | }; 36 | 37 | template 38 | class StaticLog { 39 | public: 40 | enum { VALUE = StaticIf<(Number > 1), StaticLog::VALUE + 1, 0>::VALUE }; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/testing/benchmark/local_refill.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2020 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "internal.h" 12 | #include "global_heap.h" 13 | #include "runtime.h" 14 | #include "shuffle_vector.h" 15 | 16 | using namespace mesh; 17 | 18 | static constexpr size_t kMiniHeapCount = 2 << 18; 19 | static constexpr uint32_t kObjSize = 16; 20 | static constexpr uint32_t kObjCount = 256; 21 | 22 | static void ATTRIBUTE_NEVER_INLINE initializeMiniHeaps(FixedArray &miniheaps) { 23 | // https://xkcd.com/221/ ; ensure results are repeatable (each benchmark sees the same bits set) 24 | static constexpr std::mt19937::result_type kSeed{3852235742}; 25 | auto rng = std::bind(std::uniform_int_distribution{}, std::mt19937{kSeed}); 26 | 27 | for (size_t i = 0; i < kMiniHeapCount; i++) { 28 | MiniHeap *mh = miniheaps[i]; 29 | 30 | // this is way way faster than setting each individual bit 31 | auto bits = mh->writableBitmap().mut_bits(); 32 | for (size_t j = 0; j < 4; j++) { 33 | bits[j] = rng(); 34 | } 35 | } 36 | } 37 | 38 | static void ATTRIBUTE_NEVER_INLINE initAndRefill(FixedArray &array, size_t &n, 39 | ShuffleVector &sv) { 40 | { 41 | FixedArray &miniheaps = sv.miniheaps(); 42 | miniheaps.clear(); 43 | for (size_t i = 0; i < kMaxMiniheapsPerShuffleVector; i++) { 44 | miniheaps.append(array[n++]); 45 | hard_assert(n < kMiniHeapCount); 46 | } 47 | } 48 | sv.reinit(); 49 | 50 | bool cont = true; 51 | // it may take a few iterations to pull the available offsets from our miniheaps 52 | while (likely(cont)) { 53 | while (likely(!sv.isExhausted())) { 54 | char *ptr = reinterpret_cast(sv.malloc()); 55 | hard_assert(ptr != nullptr); 56 | ptr[0] = 'x'; 57 | } 58 | 59 | cont = sv.localRefill(); 60 | } 61 | } 62 | 63 | static void BM_LocalRefill1(benchmark::State &state) { 64 | mesh::debug("local refill test!\n"); 65 | const auto tid = gettid(); 66 | GlobalHeap &gheap = runtime().heap(); 67 | 68 | // disable automatic meshing for this test 69 | gheap.setMeshPeriodMs(kZeroMs); 70 | 71 | static FixedArray array{}; 72 | if (array.size() == 0) { 73 | const size_t initialAllocCount = gheap.getAllocatedMiniheapCount(); 74 | hard_assert_msg(initialAllocCount == 0UL, "expected 0 initial MHs, not %zu", initialAllocCount); 75 | 76 | // allocate two miniheaps for the same object size from our global heap 77 | gheap.allocSmallMiniheaps(SizeMap::SizeClass(kObjSize), kObjSize, array, tid); 78 | mesh::debug("initializing the miniheaps\n"); 79 | } 80 | 81 | // always reinitialize the bitmaps (will be same pattern each time) 82 | initializeMiniHeaps(array); 83 | 84 | ShuffleVector sv{}; 85 | sv.initialInit(gheap.arenaBegin(), kObjSize); 86 | 87 | size_t n = 0; 88 | 89 | for (auto _ : state) { 90 | initAndRefill(array, n, sv); 91 | } 92 | 93 | // for (size_t i = 0; i < kMiniHeapCount; i++) { 94 | // MiniHeap *mh = array[i]; 95 | // gheap.freeMiniheap(mh); 96 | // } 97 | // array.clear(); 98 | 99 | sv.miniheaps().clear(); 100 | } 101 | BENCHMARK(BM_LocalRefill1); 102 | 103 | BENCHMARK_MAIN(); 104 | -------------------------------------------------------------------------------- /src/testing/big-alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define SZ 32816 6 | #define NOBJS 40 7 | 8 | volatile char *volatile var; 9 | 10 | int main() { 11 | char *objects[NOBJS]; 12 | memset(objects, 0, sizeof(*objects) * NOBJS); 13 | 14 | for (size_t i = 0; i < 1000000; i++) { 15 | var = malloc(SZ); 16 | memset((char *)var, 0x55, SZ); 17 | size_t off = i % NOBJS; 18 | if (objects[off] != NULL) 19 | free(objects[off]); 20 | objects[off] = (char *)var; 21 | var = NULL; 22 | } 23 | 24 | for (size_t i = 0; i < NOBJS; i++) { 25 | if (objects[i] != NULL) 26 | free(objects[i]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/testing/fragmenter.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "measure_rss.h" 15 | 16 | void print_rss() { 17 | printf("\trss:\t%10.3f MB\n", get_rss_kb() / 1024.0); 18 | } 19 | 20 | extern "C" { 21 | int mesh_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen); 22 | } 23 | 24 | using std::string; 25 | 26 | // use as (voidptr)ptr -- disgusting, but useful to minimize the clutter 27 | // around using volatile. 28 | #define voidptr void *)(intptr_t 29 | #define NOINLINE __attribute__((noinline)) 30 | #define MESH_MARKER (7305126540297948313) 31 | #define MB (1024 * 1024) 32 | #define GB (1024 * 1024 * 1024) 33 | 34 | void *NOINLINE bench_alloc(size_t n) { 35 | volatile void *ptr = malloc(n); 36 | for (size_t i = 0; i < n; i++) { 37 | ((char *)ptr)[i] = lrand48() & 0xff; 38 | } 39 | 40 | return (voidptr)ptr; 41 | } 42 | 43 | void NOINLINE bench_free(void *ptr) { 44 | free(ptr); 45 | } 46 | 47 | // Fig 4. "A Basic Strategy" from http://www.ece.iastate.edu/~morris/papers/10/ieeetc10.pdf 48 | void NOINLINE basic_fragment(int64_t n, size_t m_total) { 49 | int64_t ci = 1; 50 | int64_t m_avail = m_total; 51 | size_t m_usage = 0; // S_t in the paper 52 | size_t retained_off = 0; 53 | 54 | const size_t ptr_table_len = 2 * m_total / (ci * n); 55 | // fprintf(stdout, "ptr_table_len: %zu\n", ptr_table_len); 56 | volatile char *volatile *ptr_table = 57 | reinterpret_cast(bench_alloc(ptr_table_len * sizeof(char *))); 58 | volatile char *volatile *retained_table = 59 | reinterpret_cast(bench_alloc(ptr_table_len * sizeof(char *))); 60 | memset((voidptr)retained_table, 0, ptr_table_len * sizeof(char *)); 61 | 62 | // show how much RSS we just burned through for the table of 63 | // pointers we just allocated 64 | print_rss(); 65 | 66 | for (int64_t i = 1; m_avail >= 2 * ci * n; i++) { 67 | ci *= 2; 68 | // number of allocation pairs in this iteration 69 | const size_t pi = m_avail / (2 * ci * n); 70 | 71 | fprintf(stderr, "i:%4lld, ci:%5lld, n:%5lld, pi:%7zu (osize:%5lld)\n", (long long int)i, (long long int)ci, 72 | (long long int)n, pi, (long long int)(ci * n)); 73 | 74 | // allocate two objects 75 | for (size_t k = 0; k < pi; k++) { 76 | ptr_table[2 * k + 0] = reinterpret_cast(bench_alloc(ci * n)); 77 | ptr_table[2 * k + 1] = reinterpret_cast(bench_alloc(ci * n)); 78 | } 79 | 80 | m_usage += ci * n * pi; 81 | 82 | // now free every other object 83 | for (size_t k = 0; k < pi; k++) { 84 | retained_table[retained_off++] = ptr_table[2 * k + 0]; 85 | bench_free((voidptr)ptr_table[2 * k + 1]); 86 | } 87 | 88 | m_avail -= ci * n * pi; 89 | } 90 | 91 | fprintf(stderr, "allocated (and not freed) %f MB\n", ((double)m_usage) / MB); 92 | 93 | print_rss(); 94 | 95 | // mesh_mallctl("mesh.scavenge", nullptr, nullptr, nullptr, 0); 96 | 97 | print_rss(); 98 | 99 | for (size_t i = 0; i < ptr_table_len; i++) { 100 | bench_free((voidptr)retained_table[i]); 101 | } 102 | 103 | print_rss(); 104 | 105 | bench_free((voidptr)ptr_table); 106 | bench_free((voidptr)retained_table); 107 | 108 | // mesh_mallctl("mesh.scavenge", nullptr, nullptr, nullptr, 0); 109 | } 110 | 111 | int main(int argc, char *argv[]) { 112 | print_rss(); 113 | 114 | basic_fragment(512, 128 * MB); 115 | 116 | print_rss(); 117 | // char *env = getenv("LD_PRELOAD"); 118 | // if (env && strstr(env, "libmesh.so") != NULL) { 119 | // fprintf(stderr, "meshing stuff\n"); 120 | // free((void *)MESH_MARKER); 121 | // } 122 | 123 | // print_rss(); 124 | 125 | // sleep(700); 126 | 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /src/testing/global-large-stress.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static constexpr size_t kMinAllocSz = 800000; 6 | static constexpr size_t kMaxAllocSz = 900000; 7 | static constexpr unsigned kMaxLiveAlloc = 128; // keep no more than 128 * kMaxAllocSz memory allocated. 8 | 9 | int main(void) { 10 | std::vector alloc(kMaxLiveAlloc, nullptr); 11 | for (size_t i = 0; i < kMaxLiveAlloc; i++) { 12 | if (alloc[i] != nullptr) { 13 | fprintf(stderr, "alloc not zero initialized!\n"); 14 | } 15 | } 16 | 17 | while (1) { 18 | const size_t ix = rand() % kMaxLiveAlloc; 19 | const size_t sz = (rand() % (kMaxAllocSz - kMinAllocSz)) + kMinAllocSz; 20 | 21 | free(alloc[ix]); 22 | alloc[ix] = malloc(sz); 23 | } 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/testing/local-alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define SZ 61 6 | 7 | volatile char *volatile var; 8 | 9 | int main() { 10 | for (size_t i = 0; i < 200000000; i++) { 11 | var = malloc(SZ); 12 | /* memset((char *)var, 0, SZ); */ 13 | free((void *)var); 14 | var = NULL; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/testing/meshing_benchmark.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(__APPLE__) 14 | #include 15 | #endif 16 | 17 | #include "internal.h" 18 | 19 | #include "bitmap.h" 20 | #include "meshing.h" 21 | 22 | using mesh::internal::Bitmap; 23 | using std::make_unique; 24 | using std::stoi; 25 | using std::string; 26 | using std::unique_ptr; 27 | using std::vector; 28 | 29 | // https://stackoverflow.com/questions/236129/split-a-string-in-c 30 | template 31 | void split(const std::string &s, char delim, Out result) { 32 | std::stringstream ss; 33 | ss.str(s); 34 | std::string item; 35 | while (std::getline(ss, item, delim)) { 36 | *(result++) = item; 37 | } 38 | } 39 | 40 | // https://stackoverflow.com/questions/236129/split-a-string-in-c 41 | std::vector split(const std::string &s, char delim) { 42 | std::vector elems; 43 | split(s, delim, std::back_inserter(elems)); 44 | return elems; 45 | } 46 | 47 | class MeshTestcase { 48 | public: 49 | explicit MeshTestcase(int length, int occupancy, int nStrings, string method) 50 | : length(static_cast(length)), 51 | occupancy(static_cast(occupancy)), 52 | nStrings(static_cast(nStrings)), 53 | method(method) { 54 | d_assert(length > 0); 55 | d_assert(occupancy > 0); 56 | d_assert(nStrings > 0); 57 | } 58 | vector bitmaps{}; 59 | vector strings{}; 60 | 61 | size_t length; // string length 62 | size_t occupancy; 63 | size_t nStrings; 64 | string method; 65 | 66 | ssize_t expectedResult{-1}; 67 | }; 68 | 69 | unique_ptr openTestcase(const char *path) { 70 | FILE *f = fopen(path, "r"); 71 | if (f == nullptr) { 72 | fprintf(stderr, "ERROR: couldn't open testcase %s: %s\n", path, strerror(errno)); 73 | exit(1); 74 | } 75 | 76 | auto *bname = basename((char *)path); 77 | auto *fname = strdup(bname); 78 | auto *dotPos = strrchr(fname, '.'); 79 | // remove the extension if it exists 80 | if (dotPos) { 81 | *dotPos = 0; 82 | } 83 | 84 | vector parts = split(fname, ','); 85 | if (parts.size() != 4) { 86 | fprintf(stderr, "ERROR: expected filename to have 4 parts before extension: %s\n", path); 87 | exit(1); 88 | } 89 | free(fname); 90 | 91 | auto testcase = make_unique(stoi(parts[0]), stoi(parts[1]), stoi(parts[2]), parts[3]); 92 | 93 | bool loop = true; 94 | while (loop) { 95 | char *line = nullptr; 96 | size_t n = 0; 97 | ssize_t len = getline(&line, &n, f); 98 | if ((len < 0 && !errno) || len == 0) { 99 | if (line) 100 | free(line); 101 | fprintf(stderr, "ERROR: unexpected end of testcase\n"); 102 | exit(1); 103 | } else if (len < 0) { 104 | fprintf(stderr, "ERROR: getline: %s\n", strerror(errno)); 105 | exit(1); 106 | } 107 | d_assert(line != nullptr); 108 | 109 | d_assert(len > 0); 110 | if (line[len - 1] == '\n') { 111 | line[len - 1] = 0; 112 | len--; 113 | } 114 | 115 | d_assert(len > 0); 116 | if (line[len - 1] == '\r') { 117 | line[len - 1] = 0; 118 | len--; 119 | } 120 | 121 | d_assert(len > 0); 122 | if (line[0] == '-') { 123 | testcase->expectedResult = stoi(&line[1]); 124 | loop = false; 125 | } else { 126 | mesh::internal::string sline{line}; 127 | d_assert(sline.length() == testcase->length); 128 | 129 | testcase->bitmaps.emplace_back(sline); 130 | testcase->strings.emplace_back(sline); 131 | 132 | d_assert_msg(sline == testcase->bitmaps.back().to_string(sline.length()), "expected roundtrip '%s' == '%s'", line, 133 | testcase->bitmaps.back().to_string().c_str()); 134 | } 135 | free(line); 136 | } 137 | fclose(f); 138 | 139 | d_assert_msg(testcase->strings.size() == testcase->nStrings, "%zu == %zu", testcase->strings.size(), 140 | testcase->nStrings); 141 | d_assert(testcase->expectedResult >= 0); 142 | 143 | return testcase; 144 | } 145 | 146 | bool validate(const unique_ptr &testcase) { 147 | ssize_t result = 0; 148 | if (testcase->method == "dumb") { 149 | // result = mesh::method::simple(testcase->bitmaps); 150 | printf("TODO: implement new shifted splitting method here\n"); 151 | if (result == testcase->expectedResult) 152 | return true; 153 | } else { 154 | printf("ERROR: unimplemented method '%s'\n", testcase->method.c_str()); 155 | return false; 156 | } 157 | 158 | if (result != testcase->expectedResult) { 159 | printf("ERROR: unexpected result %zu (expected %zu) for %s method\n", result, testcase->expectedResult, 160 | testcase->method.c_str()); 161 | } 162 | return false; 163 | } 164 | 165 | int main(int argc, char *argv[]) { 166 | if (argc > 1 && ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0))) { 167 | fprintf(stderr, "Reads in string dumps and attempts to mesh.\n\n"); 168 | fprintf(stderr, "USAGE: %s DUMP_FILE\n", basename(argv[0])); 169 | exit(0); 170 | } 171 | 172 | if (argc <= 1) { 173 | fprintf(stderr, "ERROR: expected at least one filename pointing to a dump.\n"); 174 | exit(1); 175 | } 176 | 177 | for (auto i = 1; i < argc; ++i) { 178 | printf("meshing strings from %s\n", argv[i]); 179 | 180 | auto testcase = openTestcase(argv[i]); 181 | 182 | if (!validate(testcase)) { 183 | printf("%s: failed to validate.\n", argv[i]); 184 | exit(1); 185 | } 186 | } 187 | 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /src/testing/thread.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int g_i = 0; 6 | std::mutex g_i_mutex; // protects g_i 7 | 8 | volatile __thread char *c; 9 | 10 | void safe_increment() { 11 | std::lock_guard lock(g_i_mutex); 12 | ++g_i; 13 | 14 | for (size_t i = 0; i < 100; ++i) { 15 | c = new char[16]; 16 | delete [] c; 17 | } 18 | 19 | std::cout << std::this_thread::get_id() << ": " << g_i << '\n'; 20 | 21 | // g_i_mutex is automatically released when lock 22 | // goes out of scope 23 | } 24 | 25 | int main() { 26 | std::cout << __func__ << ": " << g_i << '\n'; 27 | 28 | std::thread t1(safe_increment); 29 | std::thread t2(safe_increment); 30 | 31 | t1.join(); 32 | t2.join(); 33 | 34 | std::cout << __func__ << ": " << g_i << '\n'; 35 | } 36 | -------------------------------------------------------------------------------- /src/testing/unit/alignment.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2017 University of Massachusetts, Amherst 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "gtest/gtest.h" 9 | 10 | #include "common.h" 11 | #include "internal.h" 12 | #include "thread_local_heap.h" 13 | 14 | using namespace mesh; 15 | 16 | TEST(Alignment, NaturalAlignment) { 17 | auto heap = ThreadLocalHeap::GetHeap(); 18 | 19 | void **ptrs = reinterpret_cast(calloc(256, sizeof(void *))); 20 | for (size_t size = 0; size < 4096; size += 4) { 21 | for (size_t alignment = 2; alignment <= 4096; alignment *= 2) { 22 | // debug("size: %zu align: %zu\n", size, alignment); 23 | bool logged = false; 24 | for (size_t i = 0; i <= 256; i++) { 25 | void *ptr = heap->memalign(alignment, size); 26 | if (!logged) { 27 | size_t actual = heap->getSize(ptr); 28 | // debug("%10zu %10zu %10zu %10p\n", size, actual, alignment, ptr); 29 | logged = true; 30 | } 31 | const auto ptrval = reinterpret_cast(ptr); 32 | ASSERT_EQ(ptrval % alignment, 0UL); 33 | ptrs[i] = ptr; 34 | } 35 | for (size_t i = 0; i <= 256; i++) { 36 | heap->free(ptrs[i]); 37 | } 38 | } 39 | } 40 | heap->releaseAll(); 41 | mesh::runtime().heap().flushAllBins(); 42 | memset(ptrs, 0, 256 * sizeof(void *)); 43 | free(ptrs); 44 | } 45 | 46 | TEST(Alignment, NonOverlapping) { 47 | auto heap = ThreadLocalHeap::GetHeap(); 48 | 49 | const auto a = heap->malloc(-8); 50 | const auto b = heap->malloc(-8); 51 | 52 | // we should return nullptr for crazy allocations like this. 53 | // Fixes #62 54 | ASSERT_EQ(a, nullptr); 55 | ASSERT_EQ(b, nullptr); 56 | } 57 | -------------------------------------------------------------------------------- /src/testing/unit/concurrent_mesh_test.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2017 University of Massachusetts, Amherst 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "gtest/gtest.h" 13 | 14 | #include "internal.h" 15 | #include "meshing.h" 16 | #include "runtime.h" 17 | #include "shuffle_vector.h" 18 | 19 | using namespace std; 20 | using namespace mesh; 21 | 22 | static constexpr uint32_t StrLen = 128; 23 | static constexpr uint32_t ObjCount = 32; 24 | 25 | static char *s1; 26 | static char *s2; 27 | 28 | static atomic ShouldExit; 29 | static atomic ShouldContinueTest; 30 | 31 | // we need to wrap pthread_create so that we can safely implement a 32 | // stop-the-world quiescent period for the copy/mremap phase of 33 | // meshing -- copied from libmesh.cc 34 | 35 | #if defined(__APPLE__) || defined(__FreeBSD__) 36 | #define PTHREAD_CREATE_THROW 37 | #else 38 | #define PTHREAD_CREATE_THROW throw() 39 | #endif 40 | 41 | extern "C" int pthread_create(pthread_t *thread, const pthread_attr_t *attr, mesh::PthreadFn startRoutine, 42 | void *arg) PTHREAD_CREATE_THROW { 43 | return mesh::runtime().createThread(thread, attr, startRoutine, arg); 44 | } 45 | 46 | static void writerThread() { 47 | ShouldContinueTest = 1; 48 | 49 | for (size_t i = 1; i < numeric_limits::max(); i++) { 50 | if (i % 1000000 == 0 && ShouldExit) 51 | return; 52 | 53 | s1[0] = 'A'; 54 | s2[0] = 'Z'; 55 | } 56 | 57 | debug("loop ended before ShouldExit\n"); 58 | } 59 | 60 | // shows up in strace logs, but otherwise does nothing 61 | static inline void note(const char *note) { 62 | int _ __attribute__((unused)) = write(-1, note, strlen(note)); 63 | } 64 | 65 | static void meshTestConcurrentWrite(bool invert) { 66 | if (!kMeshingEnabled) { 67 | GTEST_SKIP(); 68 | } 69 | 70 | const auto tid = gettid(); 71 | GlobalHeap &gheap = runtime().heap(); 72 | 73 | // disable automatic meshing for this test 74 | gheap.setMeshPeriodMs(kZeroMs); 75 | 76 | ASSERT_EQ(gheap.getAllocatedMiniheapCount(), 0UL); 77 | 78 | FixedArray array{}; 79 | 80 | // allocate three miniheaps for the same object size from our global heap 81 | gheap.allocSmallMiniheaps(SizeMap::SizeClass(StrLen), StrLen, array, tid); 82 | MiniHeap *mh1 = array[0]; 83 | array.clear(); 84 | 85 | gheap.allocSmallMiniheaps(SizeMap::SizeClass(StrLen), StrLen, array, tid); 86 | MiniHeap *mh2 = array[0]; 87 | array.clear(); 88 | 89 | ASSERT_EQ(gheap.getAllocatedMiniheapCount(), 2UL); 90 | 91 | // sanity checks 92 | ASSERT_TRUE(mh1 != mh2); 93 | ASSERT_EQ(mh1->maxCount(), mh2->maxCount()); 94 | ASSERT_EQ(mh1->maxCount(), ObjCount); 95 | 96 | // allocate two c strings, one from each miniheap at different offsets 97 | s1 = reinterpret_cast(mh1->mallocAt(gheap.arenaBegin(), 0)); 98 | s2 = reinterpret_cast(mh2->mallocAt(gheap.arenaBegin(), ObjCount - 1)); 99 | 100 | ASSERT_TRUE(s1 != nullptr); 101 | ASSERT_TRUE(s2 != nullptr); 102 | 103 | // fill in the strings, set the trailing null byte 104 | memset(s1, 'A', StrLen); 105 | memset(s2, 'Z', StrLen); 106 | s1[StrLen - 1] = 0; 107 | s2[StrLen - 1] = 0; 108 | 109 | // copy these strings so we can check the contents after meshing 110 | char *v1 = strdup(s1); 111 | char *v2 = strdup(s2); 112 | ASSERT_STREQ(s1, v1); 113 | ASSERT_STREQ(s2, v2); 114 | 115 | ASSERT_EQ(mh1->inUseCount(), 1UL); 116 | ASSERT_EQ(mh2->inUseCount(), 1UL); 117 | 118 | if (invert) { 119 | MiniHeap *tmp = mh1; 120 | mh1 = mh2; 121 | mh2 = tmp; 122 | } 123 | 124 | thread writer(writerThread); 125 | 126 | while (ShouldContinueTest != 1) 127 | sched_yield(); 128 | 129 | const auto bitmap1 = mh1->bitmap().bits(); 130 | const auto bitmap2 = mh2->bitmap().bits(); 131 | const auto len = mh1->bitmap().byteCount(); 132 | ASSERT_EQ(len, mh2->bitmap().byteCount()); 133 | 134 | ASSERT_TRUE(mesh::bitmapsMeshable(bitmap1, bitmap2, len)); 135 | 136 | note("ABOUT TO MESH"); 137 | // mesh the two miniheaps together 138 | gheap.meshLocked(mh1, mh2); 139 | note("DONE MESHING"); 140 | 141 | // ensure the count of set bits looks right 142 | ASSERT_EQ(mh1->inUseCount(), 2UL); 143 | 144 | // check that our two allocated objects still look right 145 | ASSERT_STREQ(s1, v1); 146 | ASSERT_STREQ(s2, v2); 147 | 148 | // get an aliased pointer to the second string by pointer arithmetic 149 | // on the first string 150 | char *s3 = s1 + (ObjCount - 1) * StrLen; 151 | ASSERT_STREQ(s2, s3); 152 | 153 | ShouldExit = 1; 154 | writer.join(); 155 | 156 | // modify the second string, ensure the modification shows up on 157 | // string 3 (would fail if the two miniheaps weren't meshed) 158 | s2[0] = 'b'; 159 | ASSERT_EQ(s3[0], 'b'); 160 | 161 | // now free the objects by going through the global heap -- it 162 | // should redirect both objects to the same miniheap 163 | gheap.free(s1); 164 | ASSERT_TRUE(!mh1->isEmpty()); 165 | gheap.free(s2); 166 | ASSERT_TRUE(mh1->isEmpty()); // safe because mh1 isn't "done" 167 | 168 | note("ABOUT TO FREE"); 169 | gheap.freeMiniheap(mh1); 170 | note("DONE FREE"); 171 | 172 | note("ABOUT TO SCAVENGE"); 173 | gheap.scavenge(true); 174 | note("DONE SCAVENGE"); 175 | 176 | ASSERT_EQ(gheap.getAllocatedMiniheapCount(), 0UL); 177 | } 178 | 179 | TEST(ConcurrentMeshTest, TryMesh) { 180 | meshTestConcurrentWrite(false); 181 | } 182 | 183 | TEST(ConcurrentMeshTest, TryMeshInverse) { 184 | meshTestConcurrentWrite(true); 185 | } 186 | -------------------------------------------------------------------------------- /src/testing/unit/mesh_test.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2017 University of Massachusetts, Amherst 3 | 4 | #include 5 | #include 6 | 7 | #include "gtest/gtest.h" 8 | 9 | #include "internal.h" 10 | #include "meshing.h" 11 | #include "runtime.h" 12 | 13 | using namespace mesh; 14 | 15 | static constexpr uint32_t StrLen = 128; 16 | static constexpr uint32_t ObjCount = 32; 17 | 18 | // shows up in strace logs, but otherwise does nothing 19 | static inline void note(const char *note) { 20 | int _ __attribute__((unused)) = write(-1, note, strlen(note)); 21 | } 22 | 23 | static void meshTest(bool invert) { 24 | if (!kMeshingEnabled) { 25 | GTEST_SKIP(); 26 | } 27 | 28 | const auto tid = gettid(); 29 | GlobalHeap &gheap = runtime().heap(); 30 | 31 | // disable automatic meshing for this test 32 | gheap.setMeshPeriodMs(kZeroMs); 33 | 34 | ASSERT_EQ(gheap.getAllocatedMiniheapCount(), 0UL); 35 | 36 | FixedArray array{}; 37 | 38 | // allocate two miniheaps for the same object size from our global heap 39 | gheap.allocSmallMiniheaps(SizeMap::SizeClass(StrLen), StrLen, array, tid); 40 | MiniHeap *mh1 = array[0]; 41 | array.clear(); 42 | 43 | gheap.allocSmallMiniheaps(SizeMap::SizeClass(StrLen), StrLen, array, tid); 44 | MiniHeap *mh2 = array[0]; 45 | array.clear(); 46 | 47 | ASSERT_EQ(gheap.getAllocatedMiniheapCount(), 2UL); 48 | 49 | // sanity checks 50 | ASSERT_TRUE(mh1 != mh2); 51 | ASSERT_EQ(mh1->maxCount(), mh2->maxCount()); 52 | ASSERT_EQ(mh1->maxCount(), ObjCount); 53 | 54 | ASSERT_EQ(mh1->bitmap().inUseCount(), 0UL); 55 | ASSERT_EQ(mh2->bitmap().inUseCount(), 0UL); 56 | 57 | // allocate two c strings, one from each miniheap at different offsets 58 | char *s1 = reinterpret_cast(mh1->mallocAt(gheap.arenaBegin(), 0)); 59 | char *s2 = reinterpret_cast(mh2->mallocAt(gheap.arenaBegin(), ObjCount - 1)); 60 | 61 | ASSERT_TRUE(s1 != nullptr); 62 | ASSERT_TRUE(s2 != nullptr); 63 | 64 | // fill in the strings, set the trailing null byte 65 | memset(s1, 'A', StrLen); 66 | memset(s2, 'Z', StrLen); 67 | s1[StrLen - 1] = 0; 68 | s2[StrLen - 1] = 0; 69 | 70 | // copy these strings so we can check the contents after meshing 71 | char *v1 = strdup(s1); 72 | char *v2 = strdup(s2); 73 | ASSERT_STREQ(s1, v1); 74 | ASSERT_STREQ(s2, v2); 75 | 76 | ASSERT_EQ(mh1->inUseCount(), 1UL); 77 | ASSERT_EQ(mh2->inUseCount(), 1UL); 78 | 79 | ASSERT_EQ(mh1->bitmap().inUseCount(), 1UL); 80 | ASSERT_EQ(mh2->bitmap().inUseCount(), 1UL); 81 | 82 | if (invert) { 83 | MiniHeap *tmp = mh1; 84 | mh1 = mh2; 85 | mh2 = tmp; 86 | } 87 | 88 | const auto bitmap1 = mh1->bitmap().bits(); 89 | const auto bitmap2 = mh2->bitmap().bits(); 90 | const auto len = mh1->bitmap().byteCount(); 91 | ASSERT_EQ(len, mh2->bitmap().byteCount()); 92 | 93 | ASSERT_TRUE(mesh::bitmapsMeshable(bitmap1, bitmap2, len)); 94 | 95 | note("ABOUT TO MESH"); 96 | // mesh the two miniheaps together 97 | gheap.meshLocked(mh1, mh2); 98 | note("DONE MESHING"); 99 | 100 | // ensure the count of set bits looks right 101 | ASSERT_EQ(mh1->inUseCount(), 2UL); 102 | 103 | // check that our two allocated objects still look right 104 | ASSERT_STREQ(s1, v1); 105 | ASSERT_STREQ(s2, v2); 106 | 107 | // get an aliased pointer to the second string by pointer arithmetic 108 | // on the first string 109 | char *s3 = s1 + (ObjCount - 1) * StrLen; 110 | ASSERT_STREQ(s2, s3); 111 | 112 | // modify the second string, ensure the modification shows up on 113 | // string 3 (would fail if the two miniheaps weren't meshed) 114 | s2[0] = 'b'; 115 | ASSERT_EQ(s3[0], 'b'); 116 | 117 | ASSERT_EQ(mh1->meshCount(), 2ULL); 118 | 119 | // now free the objects by going through the global heap -- it 120 | // should redirect both objects to the same miniheap 121 | gheap.free(s1); 122 | ASSERT_TRUE(!mh1->isEmpty()); 123 | gheap.free(s2); 124 | ASSERT_TRUE(mh1->isEmpty()); // safe because mh1 isn't "done" 125 | 126 | note("ABOUT TO FREE"); 127 | gheap.freeMiniheap(mh1); 128 | note("DONE FREE"); 129 | 130 | note("ABOUT TO SCAVENGE"); 131 | gheap.scavenge(true); 132 | note("DONE SCAVENGE"); 133 | 134 | ASSERT_EQ(gheap.getAllocatedMiniheapCount(), 0UL); 135 | } 136 | 137 | TEST(MeshTest, TryMesh) { 138 | meshTest(false); 139 | } 140 | 141 | TEST(MeshTest, TryMeshInverse) { 142 | meshTest(true); 143 | } 144 | -------------------------------------------------------------------------------- /src/testing/unit/rng_test.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2017 University of Massachusetts, Amherst 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "gtest/gtest.h" 9 | 10 | #include "rng/mwc.h" 11 | 12 | using namespace mesh; 13 | 14 | TEST(RNG, MWCRange) { 15 | MWC mwc{internal::seed(), internal::seed()}; 16 | for (size_t i = 0; i < 1000; i++) { 17 | size_t n = mwc.inRange(0, 1); 18 | if (n != 0 && n != 1) { 19 | ASSERT_TRUE(false); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/testing/unit/size_class_test.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2017 University of Massachusetts, Amherst 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "gtest/gtest.h" 9 | 10 | #include "common.h" 11 | #include "internal.h" 12 | 13 | using namespace mesh; 14 | 15 | #define roundtrip(n) ASSERT_TRUE(n == SizeMap::ByteSizeForClass(SizeMap::SizeClass(n))) 16 | #define rt_debug(n) \ 17 | debug("%d c2s: %zu s2c: %d", n, SizeMap::ByteSizeForClass(SizeMap::SizeClass(n)), SizeMap::SizeClass(n)) 18 | 19 | #define pow2Roundtrip(n) ASSERT_TRUE(n == powerOfTwo::ByteSizeForClass(powerOfTwo::ClassForByteSize(n))) 20 | 21 | TEST(SizeClass, MinObjectSize) { 22 | ASSERT_EQ(alignof(max_align_t), kMinObjectSize); 23 | 24 | ASSERT_EQ(kMinObjectSize, 16UL); 25 | 26 | ASSERT_EQ(staticlog(kMinObjectSize), 4); 27 | } 28 | 29 | TEST(SizeClass, SmallClasses) { 30 | roundtrip(16); 31 | // rt_debug(16); 32 | 33 | // ASSERT_TRUE(size2Class(8) >= 0); 34 | // roundtrip(8); 35 | // rt_debug(8); 36 | 37 | roundtrip(32); 38 | } 39 | 40 | TEST(SizeClass, PowerOfTwo) { 41 | ASSERT_TRUE(powerOfTwo::kMinObjectSize == 8); 42 | ASSERT_TRUE(powerOfTwo::ClassForByteSize(8) >= 0); 43 | 44 | pow2Roundtrip(8); 45 | pow2Roundtrip(16); 46 | pow2Roundtrip(32); 47 | } 48 | 49 | TEST(SizeClass, Reciprocal) { 50 | for (size_t i = 0; i < kClassSizesMax; i++) { 51 | volatile const size_t objectSize = SizeMap::class_to_size(i); 52 | // volatile to avoid the compiler compiling it away 53 | volatile const float recip = 1.0 / (float)objectSize; 54 | 55 | for (size_t j = 0; j <= kPageSize; j += 8) { 56 | // we depend on this floating point calcuation always being 57 | // equivalent to the integer division operation 58 | volatile const size_t off = j * recip; 59 | volatile const size_t off2 = j / objectSize; 60 | ASSERT_TRUE(off == off2); 61 | } 62 | 63 | const size_t newObjectSize = __builtin_roundf(1 / recip); 64 | ASSERT_TRUE(newObjectSize == objectSize); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/testing/userfaultfd-kernel-copy.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | constexpr size_t kPageSize = 4096; 14 | constexpr size_t kDataLen = 128; 15 | constexpr size_t kArenaSize = kPageSize * 2; 16 | 17 | void __attribute__((noreturn)) die(const char *fmt, ...) { 18 | va_list args; 19 | 20 | va_start(args, fmt); 21 | vfprintf(stderr, fmt, args); 22 | va_end(args); 23 | 24 | exit(EXIT_FAILURE); 25 | } 26 | 27 | void kernelCopy(int tmpFd, char *addr, char const *knownGood) { 28 | errno = 0; 29 | ssize_t len = read(tmpFd, addr, kDataLen); 30 | if (len != kDataLen) { 31 | fprintf(stderr, "read: %s (len: %zd)\n", strerror(errno), len); 32 | return; 33 | } 34 | 35 | if (memcmp(knownGood, addr, kDataLen) != 0) { 36 | die("data read from file doesn't match knownGood\n"); 37 | } 38 | 39 | printf("read from file went as expected; data looks good.\n"); 40 | } 41 | 42 | void setupPageTrap(int faultFd, char *arenaBegin, size_t pageOff, size_t len) { 43 | char *mappingBegin = arenaBegin + pageOff * kPageSize; 44 | 45 | // step 0: register the range with userfaultfd 46 | struct uffdio_register ufRegister = {}; 47 | ufRegister.range.start = reinterpret_cast(mappingBegin); 48 | ufRegister.range.len = len; 49 | ufRegister.mode = UFFDIO_REGISTER_MODE_WP; 50 | if (ioctl(faultFd, UFFDIO_REGISTER, &ufRegister) == -1) { 51 | die("ioctl(UFFDIO_REGISTER): %s\n", strerror(errno)); 52 | } 53 | 54 | // step 1: use userfaultfd (rather than mprotect) to remap the pages read-only 55 | struct uffdio_writeprotect ufWriteProtect = {}; 56 | ufWriteProtect.range.start = reinterpret_cast(mappingBegin); 57 | ufWriteProtect.range.len = len; 58 | ufWriteProtect.mode = UFFDIO_WRITEPROTECT_MODE_WP; 59 | if (ioctl(faultFd, UFFDIO_WRITEPROTECT, &ufWriteProtect) == -1) { 60 | die("ioctl(UFFDIO_WRITEPROTECT): %s\n", strerror(errno)); 61 | } 62 | } 63 | 64 | int main() { 65 | // get some random bytes + write it to a temp file 66 | char data[kDataLen] = {}; 67 | if (getentropy(data, kDataLen) == -1) { 68 | die("getentropy: %s\n", strerror(errno)); 69 | } 70 | 71 | char tmpFilename[] = "/tmp/userfaultfd-test-XXXXXX"; 72 | int tmpFd = mkstemp(tmpFilename); 73 | if (tmpFd == -1) { 74 | die("mkstemp: %s\n", strerror(errno)); 75 | } 76 | if (unlink(tmpFilename) == -1) { 77 | die("unlink: %s\n", strerror(errno)); 78 | } 79 | 80 | size_t len = write(tmpFd, data, kDataLen); 81 | if (len < kDataLen) { 82 | die("write: partial write of %d\n", len); 83 | } 84 | if (lseek(tmpFd, 0, SEEK_SET) != 0) { 85 | die("lseek failed\n"); 86 | } 87 | 88 | // the write-protection patchset doesn't yet support non-anonymous (or hugepage) VMAs 89 | char *arenaBegin = reinterpret_cast( 90 | mmap(nullptr, kArenaSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0)); 91 | if (arenaBegin == MAP_FAILED) { 92 | die("mmap: %s\n", strerror(errno)); 93 | } 94 | 95 | // pre-fault things in; the write protection stuff below doesn't seem to work 96 | // well if the pages aren't already faulted in. in a real allocator/GC the pages 97 | // we're dealing with will be faulted in so this seems fine. 98 | for (size_t i = 0; i < kArenaSize; i += kPageSize) { 99 | arenaBegin[i] = 0; 100 | } 101 | 102 | int faultFd = static_cast(syscall(__NR_userfaultfd, O_CLOEXEC)); 103 | if (faultFd == -1) { 104 | die("userfaultfd: %s\n", strerror(errno)); 105 | } 106 | 107 | // the only feature we care about is write-protection faults -- this will fail 108 | // on a mainline kernel as the patchset isn't upstream yet. 109 | struct uffdio_api ufApi = {}; 110 | ufApi.api = UFFD_API; 111 | ufApi.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP; 112 | if (ioctl(faultFd, UFFDIO_API, &ufApi) == -1) { 113 | die("ioctl(UFFDIO_API): %s\n", strerror(errno)); 114 | } 115 | if (ufApi.api != UFFD_API) { 116 | die("ioctl(UFFDIO_API) API version mismatch %lld != %lld\n", ufApi.api, UFFD_API); 117 | } 118 | 119 | // prefault in the entire arena to match the fact that the heap will already 120 | // be faulted in when meshing pages together 121 | for (size_t i = 0; i < kArenaSize; i += kPageSize) { 122 | arenaBegin[i] = 0; 123 | } 124 | 125 | // write-protect part of the arena 126 | setupPageTrap(faultFd, arenaBegin, 0, kPageSize); 127 | 128 | printf("part of the arena is read-only now and writes raise an event on our userfaultfd\n"); 129 | 130 | // even works with kernelspace; +57 is to verify that non-page-aligned access work 131 | std::thread t1(kernelCopy, tmpFd, arenaBegin + 57, data); 132 | 133 | // now suspend until we receive an event from the kernelCopy thread 134 | struct uffd_msg msg = {}; 135 | int nRead = read(faultFd, &msg, sizeof(msg)); 136 | if (nRead == 0) { 137 | die("EOF on userfaultfd!\n"); 138 | } else if (nRead == -1) { 139 | die("read: %s\n", strerror(errno)); 140 | } 141 | 142 | if (msg.event != UFFD_EVENT_PAGEFAULT) { 143 | die("Unexpected event on userfaultfd\n"); 144 | } 145 | 146 | printf(" UFFD_EVENT_PAGEFAULT event: "); 147 | printf("flags = %llx; ", msg.arg.pagefault.flags); 148 | printf("address = %llx\n", msg.arg.pagefault.address); 149 | 150 | sleep(2); 151 | 152 | printf("removing write protection and waking up other thread\n"); 153 | 154 | // turn off write-protection; implicitly wakes the other thread up 155 | struct uffdio_writeprotect ufWriteProtect = {}; 156 | ufWriteProtect.range.start = msg.arg.pagefault.address; 157 | ufWriteProtect.range.len = kPageSize; 158 | ufWriteProtect.mode = 0; 159 | if (ioctl(faultFd, UFFDIO_WRITEPROTECT, &ufWriteProtect) == -1) { 160 | die("ioctl(UFFDIO_WRITEPROTECT): %s\n", strerror(errno)); 161 | } 162 | 163 | t1.join(); 164 | } 165 | -------------------------------------------------------------------------------- /src/thread_local_heap.cc: -------------------------------------------------------------------------------- 1 | // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*- 2 | // Copyright 2019 The Mesh Authors. All rights reserved. 3 | // Use of this source code is governed by the Apache License, 4 | // Version 2.0, that can be found in the LICENSE file. 5 | 6 | #include "thread_local_heap.h" 7 | 8 | namespace mesh { 9 | 10 | #ifdef MESH_HAVE_TLS 11 | __thread ThreadLocalHeap *ThreadLocalHeap::_threadLocalHeap ATTR_INITIAL_EXEC CACHELINE_ALIGNED; 12 | #endif 13 | ThreadLocalHeap *ThreadLocalHeap::_threadLocalHeaps{nullptr}; 14 | bool ThreadLocalHeap::_tlhInitialized{false}; 15 | pthread_key_t ThreadLocalHeap::_heapKey{0}; 16 | 17 | void ThreadLocalHeap::DestroyThreadLocalHeap(void *ptr) { 18 | if (ptr != nullptr) { 19 | #ifdef MESH_HAVE_TLS 20 | _threadLocalHeap = nullptr; 21 | #endif 22 | DeleteHeap(reinterpret_cast(ptr)); 23 | } 24 | } 25 | 26 | // similar to what TCMalloc does; the constructor initializes our 27 | // pthread key and does this in such a way that we aren't recursively 28 | // calling into pthread code (and deadlocking). 29 | class MeshMallocGuard { 30 | public: 31 | MeshMallocGuard() { 32 | ThreadLocalHeap::InitTLH(); 33 | } 34 | }; 35 | 36 | static MeshMallocGuard module_initialization_hook; 37 | 38 | void ThreadLocalHeap::InitTLH() { 39 | hard_assert(!_tlhInitialized); 40 | pthread_key_create(&_heapKey, DestroyThreadLocalHeap); 41 | _tlhInitialized = true; 42 | } 43 | 44 | ThreadLocalHeap *ThreadLocalHeap::NewHeap(pthread_t current) { 45 | // we just allocate out of our internal heap 46 | void *buf = mesh::internal::Heap().malloc(sizeof(ThreadLocalHeap)); 47 | static_assert(sizeof(ThreadLocalHeap) < 4096 * 8, "tlh should have a reasonable size"); 48 | hard_assert(buf != nullptr); 49 | hard_assert(reinterpret_cast(buf) % CACHELINE_SIZE == 0); 50 | 51 | auto heap = new (buf) ThreadLocalHeap(&mesh::runtime().heap(), current); 52 | 53 | heap->_prev = nullptr; 54 | heap->_next = _threadLocalHeaps; 55 | if (_threadLocalHeaps != nullptr) { 56 | _threadLocalHeaps->_prev = heap; 57 | } 58 | _threadLocalHeaps = heap; 59 | 60 | return heap; 61 | } 62 | 63 | ThreadLocalHeap *ThreadLocalHeap::CreateHeapIfNecessary() { 64 | #ifdef MESH_HAVE_TLS 65 | const bool maybeReentrant = !_tlhInitialized; 66 | // check to see if we really need to create the heap 67 | if (_tlhInitialized && _threadLocalHeap != nullptr) { 68 | return _threadLocalHeap; 69 | } 70 | #else 71 | const bool maybeReentrant = true; 72 | #endif 73 | 74 | ThreadLocalHeap *heap = nullptr; 75 | 76 | { 77 | std::lock_guard lock(mesh::runtime().heap()); 78 | 79 | const pthread_t current = pthread_self(); 80 | 81 | if (maybeReentrant) { 82 | for (ThreadLocalHeap *h = _threadLocalHeaps; h != nullptr; h = h->_next) { 83 | if (h->_pthreadCurrent == current) { 84 | heap = h; 85 | break; 86 | } 87 | } 88 | } 89 | 90 | if (heap == nullptr) { 91 | heap = NewHeap(current); 92 | } 93 | } 94 | 95 | if (!heap->_inSetSpecific && _tlhInitialized) { 96 | heap->_inSetSpecific = true; 97 | #ifdef MESH_HAVE_TLS 98 | _threadLocalHeap = heap; 99 | #endif 100 | pthread_setspecific(_heapKey, heap); 101 | heap->_inSetSpecific = false; 102 | } 103 | 104 | return heap; 105 | } 106 | 107 | void ThreadLocalHeap::DeleteHeap(ThreadLocalHeap *heap) { 108 | if (heap == nullptr) { 109 | return; 110 | } 111 | 112 | // manually invoke the destructor 113 | heap->ThreadLocalHeap::~ThreadLocalHeap(); 114 | 115 | if (heap->_next != nullptr) { 116 | heap->_next->_prev = heap->_prev; 117 | } 118 | if (heap->_prev != nullptr) { 119 | heap->_prev->_next = heap->_next; 120 | } 121 | if (_threadLocalHeaps == heap) { 122 | _threadLocalHeaps = heap->_next; 123 | } 124 | 125 | mesh::internal::Heap().free(reinterpret_cast(heap)); 126 | } 127 | 128 | void ThreadLocalHeap::releaseAll() { 129 | for (size_t i = 1; i < kNumBins; i++) { 130 | _shuffleVector[i].refillMiniheaps(); 131 | _global->releaseMiniheaps(_shuffleVector[i].miniheaps()); 132 | } 133 | } 134 | 135 | // we get here if the shuffleVector is exhausted 136 | void *CACHELINE_ALIGNED_FN ThreadLocalHeap::smallAllocSlowpath(size_t sizeClass) { 137 | ShuffleVector &shuffleVector = _shuffleVector[sizeClass]; 138 | 139 | // we grab multiple MiniHeaps at a time from the global heap. often 140 | // it is possible to refill the freelist from a not-yet-used 141 | // MiniHeap we already have, without global cross-thread 142 | // synchronization 143 | if (likely(shuffleVector.localRefill())) { 144 | return shuffleVector.malloc(); 145 | } 146 | 147 | return smallAllocGlobalRefill(shuffleVector, sizeClass); 148 | } 149 | 150 | void *CACHELINE_ALIGNED_FN ThreadLocalHeap::smallAllocGlobalRefill(ShuffleVector &shuffleVector, size_t sizeClass) { 151 | const size_t sizeMax = SizeMap::ByteSizeForClass(sizeClass); 152 | 153 | _global->allocSmallMiniheaps(sizeClass, sizeMax, shuffleVector.miniheaps(), _current); 154 | shuffleVector.reinit(); 155 | 156 | d_assert(!shuffleVector.isExhausted()); 157 | 158 | void *ptr = shuffleVector.malloc(); 159 | d_assert(ptr != nullptr); 160 | 161 | return ptr; 162 | } 163 | } // namespace mesh 164 | -------------------------------------------------------------------------------- /support/export_mesh.cmake: -------------------------------------------------------------------------------- 1 | #Edit configuration files to export mesh folder and auxiliary variables 2 | 3 | set(LINUX_BASH_FILE /home/${USER}/.bashrc) 4 | set(MAC_BASH_FILE /Users/${USER}/.bash_profile) 5 | 6 | if(SYSWIDE) 7 | #Install to system/public folders (exports the path to the library and refresh cache on Linux). Mac is weird, the library was completely ignored. 8 | if(WIN32) 9 | execute_process(COMMAND setx /M LIBMESH_PATH "${INSTALLATION_DIR}\\lib\\") 10 | elseif(APPLE) 11 | file(APPEND ${MAC_BASH_FILE} "export DYLD_FALLBACK_LIBRARY_PATH=\"${INSTALLATION_DIR}/lib/\"\nexport LIBMESH_PATH=\"${INSTALLATION_DIR}/lib/\"\n") 12 | else() 13 | #Linux 14 | file(WRITE "/etc/ld.so.conf.d/mesh.conf" "${INSTALLATION_DIR}/lib") #write path to libmesh 15 | execute_process(COMMAND ldconfig) # renew shared lib cache 16 | file(APPEND ${LINUX_BASH_FILE} "export LIBMESH_PATH=\"${INSTALLATION_DIR}/lib/\"\n") #not really necessary, but just to make things consistent 17 | endif() 18 | else() 19 | #Install to user profile (just exports a variable containing the path to the library) 20 | if(WIN32) 21 | execute_process(COMMAND setx LIBMESH_PATH "${INSTALLATION_DIR}\\lib\\") 22 | elseif(APPLE) 23 | file(APPEND ${MAC_BASH_FILE} "export LIBMESH_PATH=\"${INSTALLATION_DIR}/lib/\"\n") 24 | else() 25 | #Linux 26 | file(APPEND ${LINUX_BASH_FILE} "export LIBMESH_PATH=\"${INSTALLATION_DIR}/lib/\"\n") 27 | endif() 28 | endif() -------------------------------------------------------------------------------- /support/gen-size-classes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | # NO 8-byte size classes 6 | 7 | SIZE_CLASSES = [ 8 | 16, 9 | 16, 10 | 32, 11 | 48, 12 | 64, 13 | 80, 14 | 96, 15 | 112, 16 | 128, 17 | 160, 18 | 192, 19 | 224, 20 | 256, 21 | 320, 22 | 384, 23 | 448, 24 | 512, 25 | 640, 26 | 768, 27 | 896, 28 | 1024, 29 | 2048, 30 | 4096, 31 | 8192, 32 | 16384, 33 | ] 34 | 35 | SIZE_CLASS_INDICES = {x: i for i, x in enumerate(SIZE_CLASSES)} 36 | 37 | def get_next(i): 38 | for sz in SIZE_CLASSES: 39 | if i <= sz: 40 | return sz 41 | assert False 42 | 43 | def main(): 44 | print('// small size classes') 45 | for i in range(0, 1025, 8): 46 | size_class = get_next(i) 47 | print('\t%d,\t// %5d -> %5d' % (SIZE_CLASS_INDICES[size_class], i, size_class)) 48 | 49 | print('// large size classes') 50 | for i in range(1024, 16385, 128): 51 | if i == 1024: 52 | continue 53 | size_class = get_next(i) 54 | print('\t%d,\t// %5d -> %5d' % (SIZE_CLASS_INDICES[size_class], i, size_class)) 55 | 56 | if __name__ == '__main__': 57 | sys.exit(main()) 58 | -------------------------------------------------------------------------------- /support/install_all_configs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | for n in 0 1 2; do 5 | for meshing in y n; do 6 | suffix="${n}${meshing}" 7 | rand_config='' 8 | meshing_config='' 9 | case "$n" in 10 | 0) 11 | rand_config='--config=disable-randomization' 12 | ;; 13 | 1) 14 | # already the default 15 | ;; 16 | 2) 17 | rand_config='--config=shuffle-on-free' 18 | ;; 19 | esac 20 | case "$meshing" in 21 | n) 22 | meshing_config='--config=disable-meshing' 23 | ;; 24 | y) 25 | # already the default 26 | ;; 27 | esac 28 | bazel_config="$rand_config $meshing_config" 29 | echo building "'" "$bazel_config" "'" 30 | make V=1 BAZEL_CONFIG="$bazel_config" 31 | sudo V=1 make install LIB_SUFFIX="$suffix" 32 | done 33 | done 34 | -------------------------------------------------------------------------------- /support/remove_export_mesh.cmake: -------------------------------------------------------------------------------- 1 | #Edit configuration files to remove mesh exported configs and varaibles 2 | 3 | function(get_rid_of_path bash_config_contents path_to_get_rid bash_contents_updated) 4 | #Remove libmesh path export 5 | string(REPLACE "export LIBMESH_PATH=\"${path_to_get_rid}\"\n" "" bash_config_content_lines "${bash_config_contents}") 6 | #Remove DYLD_FALLBACK export 7 | string(REPLACE "export DYLD_FALLBACK_LIBRARY_PATH=\"${path_to_get_rid}\"\n" "" bash_config_contents "${bash_config_content_lines}") 8 | #Save results to the variable from the parent scope 9 | set(bash_contents_updated "${bash_config_contents}" PARENT_SCOPE) 10 | endfunction(get_rid_of_path) 11 | 12 | 13 | set(LINUX_BASH_FILE /home/${USER}/.bashrc) 14 | set(MAC_BASH_FILE /Users/${USER}/.bash_profile) 15 | 16 | if(SYSWIDE) 17 | #Install to system/public folders (exports the path to the library and refresh cache on Linux). Mac is weird, the library was completely ignored. 18 | if(WIN32) 19 | #Remove exported variables 20 | execute_process(COMMAND setx /M LIBMESH_PATH=) 21 | elseif(APPLE) 22 | else() 23 | #Linux 24 | file(REMOVE "/etc/ld.so.conf.d/mesh.conf") #write path to libmesh 25 | execute_process(COMMAND ldconfig) # renew shared lib cache 26 | endif() 27 | endif() 28 | 29 | #Install to user profile (just exports a variable containing the path to the library) 30 | if(WIN32) 31 | #Remove exported variable 32 | execute_process(COMMAND setx LIBMESH_PATH=) 33 | elseif(APPLE) 34 | #Remove exported variable 35 | file(COPY ${MAC_BASH_FILE} DESTINATION ${MAC_BASH_FILE}.bak) #backup before doing anything 36 | file(READ ${MAC_BASH_FILE} bash_contents) 37 | set(bash_contents_updated) 38 | get_rid_of_path("${bash_contents}" "${INSTALLATION_DIR}/lib/" bash_contents_updated) 39 | file(WRITE ${MAC_BASH_FILE} "${bash_contents_updated}") 40 | else() 41 | #Linux 42 | #Remove exported variable 43 | file(COPY ${LINUX_BASH_FILE} DESTINATION ${LINUX_BASH_FILE}.bak) #backup before doing anything 44 | file(READ ${LINUX_BASH_FILE} bash_contents) 45 | set(bash_contents_updated) 46 | get_rid_of_path("${bash_contents}" "${INSTALLATION_DIR}/lib/" bash_contents_updated) 47 | file(WRITE ${LINUX_BASH_FILE} "${bash_contents_updated}") 48 | endif() 49 | -------------------------------------------------------------------------------- /support/update-bazelisk: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 5 | target="${script_dir}/../bazel" 6 | 7 | curl https://raw.githubusercontent.com/bazelbuild/bazelisk/master/bazelisk.py >"$target" 8 | chmod 755 "$target" 9 | -------------------------------------------------------------------------------- /theory/32m80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plasma-umass/Mesh/d45d6deec627b7d46de98dfb5415c30b7c1db9c6/theory/32m80.png -------------------------------------------------------------------------------- /theory/64m80ind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plasma-umass/Mesh/d45d6deec627b7d46de98dfb5415c30b7c1db9c6/theory/64m80ind.png -------------------------------------------------------------------------------- /theory/bound_comparison.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Feb 01 19:18:42 2017 4 | 5 | @author: devd 6 | """ 7 | 8 | from __future__ import division 9 | from createRandomString import * 10 | from makeGraph import * 11 | import matplotlib.pyplot as plt 12 | import matplotlib.patches as mpatches 13 | import networkx as nx 14 | import numpy as np 15 | import time 16 | from compute_exp_Y import compute_exp_Y, compute_degree_bound, compute_isolated_edge_bound, compute_degreeplusone_bound 17 | from choose import compute_q 18 | 19 | def experiment(length, ones_range_min, ones_range_max, reps, numStrings): 20 | ones = [] 21 | y_estimate = [] 22 | deg_estimate = [] 23 | 24 | for numOnes in range(ones_range_min, ones_range_max+1): 25 | ones.append(numOnes) 26 | 27 | y = compute_exp_Y(length, numOnes, numStrings) 28 | yperc = (math.floor(y)/numStrings)*100 29 | y_estimate.append(yperc) 30 | 31 | deg = compute_degreeplusone_bound(length, numOnes, numStrings) 32 | degperc = (math.floor(deg)/numStrings)*100 33 | deg_estimate.append(degperc) 34 | 35 | return ones, y_estimate, deg_estimate 36 | 37 | def plot_it(length, ones_range_min, ones_range_max, reps, numStrings): 38 | ones, y_estimate, deg_estimate = experiment(length, ones_range_min, ones_range_max, reps, numStrings) 39 | 40 | plt.errorbar(np.asarray(ones), np.asarray(y_estimate), np.zeros(len(ones)), markersize = 3, lw=1, fmt='o') 41 | plt.errorbar(np.asarray(ones), np.asarray(deg_estimate), np.zeros(len(ones)), markersize = 3, lw=1, fmt='o') 42 | plt.ylim([0,60]) 43 | plt.ylabel('Percentage of pages freed') 44 | plt.xlabel('Number of objects per page') 45 | blue_patch = mpatches.Patch(color='blue', label='E[Y] bound') 46 | green_patch = mpatches.Patch(color = 'green', label = 'deg+1 bound') 47 | plt.legend(handles=[blue_patch, green_patch]) 48 | plt.title('E[Y] vs DEG+1 BOUND RESULTS \n{}-object pages, {} pages'.format(length, numStrings)) 49 | plt.show() 50 | # plt.savefig('E[Y]vsdeg{},{}'.format(length, numStrings) + '.png', dpi = 1000) 51 | plt.close() 52 | 53 | if __name__ == '__main__': 54 | #length = [32,64] 55 | length = [32] 56 | ones_range_min = 1 57 | ones_range_max = 32 58 | reps = 10 59 | #numStrings = [80,100,150,200] 60 | numStrings= [80] 61 | 62 | 63 | for l in length: 64 | for n in numStrings: 65 | plot_it(l, ones_range_min, int(l/2), reps, n) 66 | # plot_it(l, 10, 13, 10, n) 67 | print 'max match vs E[Y] plot {},{} done'.format(l,n) -------------------------------------------------------------------------------- /theory/bounds/impdeg+1: -------------------------------------------------------------------------------- 1 | (dp1 2 | (I16 3 | I4 4 | I50 5 | tp2 6 | cnumpy.core.multiarray 7 | scalar 8 | p3 9 | (cnumpy 10 | dtype 11 | p4 12 | (S'f8' 13 | I0 14 | I1 15 | tRp5 16 | (I3 17 | S'<' 18 | NNNI-1 19 | I-1 20 | I0 21 | tbS'\x08\x1dN\xcf\xb1\x1e4@' 22 | tRp6 23 | s(I64 24 | I1 25 | I1000 26 | tp7 27 | g3 28 | (g5 29 | S'\xd6\x11;\xd2\xd8%\x7f@' 30 | tRp8 31 | s(I16 32 | I5 33 | I50 34 | tp9 35 | g3 36 | (g5 37 | S'\xaa\x17j\x92\xe1u0@' 38 | tRp10 39 | s(I16 40 | I1 41 | I100 42 | tp11 43 | g3 44 | (g5 45 | S'4/a\x94\xde]H@' 46 | tRp12 47 | s(I16 48 | I1 49 | I50 50 | tp13 51 | g3 52 | (g5 53 | S'\x17J\xf1\x11-\xf67@' 54 | tRp14 55 | s(I16 56 | I5 57 | I100 58 | tp15 59 | g3 60 | (g5 61 | S'\x18f\xa7\xb6\x87"C@' 62 | tRp16 63 | s(I16 64 | I6 65 | I50 66 | tp17 67 | g3 68 | (g5 69 | S'R\xa1\xf7tBn(@' 70 | tRp18 71 | s(I16 72 | I2 73 | I100 74 | tp19 75 | g3 76 | (g5 77 | S'\xb7D\xf1DW\xd7G@' 78 | tRp20 79 | s(I16 80 | I2 81 | I50 82 | tp21 83 | g3 84 | (g5 85 | S'\xc0M\x08\x08.27@' 86 | tRp22 87 | s(I16 88 | I6 89 | I100 90 | tp23 91 | g3 92 | (g5 93 | S'\xb1\xb6r\x93\xe1o<@' 94 | tRp24 95 | s(I16 96 | I3 97 | I100 98 | tp25 99 | g3 100 | (g5 101 | S'+p\xb5\x17\x88\x0fG@' 102 | tRp26 103 | s(I16 104 | I7 105 | I50 106 | tp27 107 | g3 108 | (g5 109 | S'\x0c\x18-\xaf}\x9d\n@' 110 | tRp28 111 | s(I16 112 | I7 113 | I100 114 | tp29 115 | g3 116 | (g5 117 | S"0,\x8e\xee\xe1G'@" 118 | tRp30 119 | s(I16 120 | I3 121 | I50 122 | tp31 123 | g3 124 | (g5 125 | S'\xb6\x06ER\xd9\x0b6@' 126 | tRp32 127 | s(I16 128 | I4 129 | I100 130 | tp33 131 | g3 132 | (g5 133 | S'>a\xb9\x9c\x14\xc1E@' 134 | tRp34 135 | s. -------------------------------------------------------------------------------- /theory/choose.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Apr 27 16:52:27 2016 4 | 5 | @author: devd 6 | """ 7 | #from __future__ import division 8 | from math import factorial 9 | from scipy.misc import comb 10 | 11 | 12 | #def nCr(n, r): 13 | # return factorial(n) // factorial(n-r) // factorial(r) 14 | 15 | #def nCr(n,r): 16 | # result = factorial(n) 17 | # result /= factorial(r) 18 | # result /= factorial(n-r) 19 | # return result 20 | 21 | def nCr(n,r): 22 | return comb(n,r) 23 | 24 | 25 | 26 | 27 | def compute_q(length, numOnes): 28 | result = float((nCr(length-numOnes, numOnes)))/(nCr(length, numOnes)) 29 | return result 30 | 31 | def compute_p3(length, numOnes): 32 | result = float((nCr(length-(2*numOnes), numOnes)))/(nCr(length, numOnes)) 33 | return result 34 | 35 | #print compute_q(32,1) 36 | 37 | 38 | if __name__ == '__main__': 39 | for i in range(9,16): 40 | print 'q val for {}: {}\n'.format(i,compute_q(32,i)) 41 | #print compute_q(32,13) 42 | if __name__ == "__main__": 43 | print compute_q(32, 6) -------------------------------------------------------------------------------- /theory/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | 5 | __all__ = ['ERROR', 'WARN', 'INFO', 'DEBUG', 'log', 'slurp'] 6 | 7 | # from rainbow 8 | def make_reporter(verbosity, quiet, filelike): 9 | ''' 10 | Returns a function suitable for logging use. 11 | ''' 12 | if not quiet: 13 | 14 | def report(level, msg, *args): 15 | 'Log if the specified severity is <= the initial verbosity.' 16 | if level <= verbosity: 17 | if len(args): 18 | filelike.write(msg % args + '\n') 19 | else: 20 | filelike.write('%s\n' % (msg, )) 21 | else: 22 | 23 | def report(level, msg, *args): 24 | '/dev/null logger.' 25 | pass 26 | 27 | return report 28 | 29 | 30 | ERROR = 0 31 | WARN = 1 32 | INFO = 2 33 | DEBUG = 3 34 | 35 | log = make_reporter(DEBUG, False, sys.stderr) 36 | 37 | def slurp(file_name): 38 | ''' 39 | Reads in a file, stripping leading and trailing whitespace. 40 | ''' 41 | with open(file_name, 'r') as f: 42 | return f.read().strip() 43 | -------------------------------------------------------------------------------- /theory/compute_exp_Y.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Apr 27 22:18:12 2016 4 | 5 | @author: devd 6 | """ 7 | from __future__ import division 8 | from choose import compute_q, nCr, compute_p3 9 | import cPickle as pickle 10 | 11 | def prob(a,b,c, numStrings, numOnes, p1, p2, p3): 12 | p4 = 1-p1-p2-p3 13 | d = numStrings-2-a-b-c 14 | return (nCr(numStrings-2,a)*(p1**a)*nCr(numStrings-2-a, b)*(p2**b)*nCr(numStrings-2-a-b, c)*(p3**c)*(p4**d)) 15 | 16 | def lookup(bound_id, length, numOnes, numStrings): 17 | path = "bounds/{}".format(bound_id) 18 | with open(path, "rb") as file: 19 | bound_dict = pickle.load(file) 20 | try: 21 | bound = bound_dict[(length, numOnes, numStrings)] 22 | except KeyError: 23 | return None, bound_dict 24 | return bound, None 25 | 26 | def store(bound_id, bound_dict): 27 | path = "bounds/{}".format(bound_id) 28 | with open(path, "wb") as file: 29 | pickle.dump(bound_dict, file) 30 | 31 | 32 | def compute_exp_Y(length, numOnes, numStrings): 33 | q = compute_q(length, numOnes) 34 | p3 = compute_p3(length, numOnes) 35 | p1 = q-p3 36 | p2 = p1 37 | 38 | p4 = 1-p1-p2-p3 39 | 40 | sum = 0 41 | for a in range(numStrings-2+1): 42 | for b in range(numStrings-2-a+1): 43 | for c in range(numStrings-2-a-b+1): 44 | add = min(1/(a+c+1), 1/(b+c+1))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 45 | # add = min(1/(a+c), 1/(b+c))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 46 | sum += add 47 | # sum += min(1/(a+c+1), 1/(b+c+1))*prob(a,b,c,numStrings,p1,p2,p3) 48 | sum *= q 49 | return sum*nCr(numStrings,2) 50 | 51 | def compute_degree_bound(length, numOnes, numStrings): 52 | print length 53 | print numOnes 54 | q = compute_q(length, numOnes) 55 | exp_degree = (numStrings-1)*q 56 | a = exp_degree 57 | b = exp_degree 58 | return numStrings/2*(a/(b+1)) 59 | 60 | def compute_isolated_edge_bound(length, numOnes, numStrings): 61 | q = compute_q(length, numOnes) 62 | p3 = compute_p3(length, numOnes) 63 | m = numStrings 64 | bound1 = (m-1)*q*(1-(2*q)+p3)**(m-2) 65 | bound2 = 2- 2*(1-q)**(m-1) - (m-1)*q 66 | return (m/2)*max(bound1,bound2) 67 | # return (m/2)*bound2 68 | 69 | def compute_degreeplusone_bound(length, numOnes, numStrings): 70 | q = compute_q(length, numOnes) 71 | p3 = compute_p3(length, numOnes) 72 | p1 = q-p3 73 | p2 = p1 74 | 75 | p4 = 1-p1-p2-p3 76 | 77 | sum = 0 78 | for a in range(numStrings-2+1): 79 | for b in range(numStrings-2-a+1): 80 | for c in range(numStrings-2-a-b+1): 81 | add = min(1/(a+c+2), 1/(b+c+2))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 82 | # add = .5*(1/(a+c+2) + 1/(b+c+2))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 83 | sum += add 84 | sum *= q 85 | return sum*nCr(numStrings,2) 86 | 87 | def compute_improved_degreeplusone_bound(length, numOnes, numStrings): 88 | bound, bound_dict = lookup("impdeg+1", length, numOnes, numStrings) 89 | if bound: 90 | print 'value already exists, retrieving from database' 91 | return bound 92 | q = compute_q(length, numOnes) 93 | p3 = compute_p3(length, numOnes) 94 | p1 = q-p3 95 | p2 = p1 96 | 97 | p4 = 1-p1-p2-p3 98 | 99 | sum = 0 100 | for a in range(numStrings-2+1): 101 | for b in range(numStrings-2-a+1): 102 | for c in range(numStrings-2-a-b+1): 103 | if a+c+1==1 and b+c+1==1: 104 | add = 1*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 105 | elif a+c+1==1: 106 | add = (1/(b+c+1))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 107 | elif b+c+1==1: 108 | add = (1/(a+c+1))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 109 | # elif a+c+1==2 and b+c+1==2: 110 | ## add = .5*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 111 | elif (a+c+1==2 and b+c+1==3) or (a+c+1==3 and b+c+1==2): 112 | add = prob(a,b,c,numStrings, numOnes,p1,p2,p3)/3.0 113 | # elif a+c+1 != b+c+1: 114 | # add = min(1/(a+c+1), 1/(b+c+1))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 115 | else: 116 | add = min(1/(a+c+2), 1/(b+c+2))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 117 | # add = .5*(1/(a+c+2) + 1/(b+c+2))*prob(a,b,c,numStrings, numOnes,p1,p2,p3) 118 | sum += add 119 | sum *= q 120 | result = sum*nCr(numStrings,2) 121 | bound_dict[(length, numOnes, numStrings)] = result 122 | store("impdeg+1", bound_dict) 123 | print 'value did not already exist, writing to database' 124 | return result 125 | 126 | def boundRetrieve(identifier): 127 | fetcher = { "impdeg+1": (compute_improved_degreeplusone_bound) 128 | } 129 | return fetcher[identifier] 130 | 131 | if __name__ == '__main__': 132 | #print prob(18,2,12,80,0.03125,0.03125,0.9375)/31 133 | # print compute_exp_Y(32, 13, 80) 134 | print compute_improved_degreeplusone_bound(16, 4, 100) -------------------------------------------------------------------------------- /theory/createRandomString.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 27 21:20:37 2016 4 | 5 | @author: David Tench 6 | """ 7 | import random 8 | import math 9 | 10 | def createRandomString(length, numOnes): 11 | """Returns a random binary string with specified number of randomly located 12 | ones.""" 13 | counter = numOnes 14 | string = '' 15 | for i in range(length): 16 | string += '0' 17 | while counter !=0: 18 | loc = random.randrange(length) 19 | while string[loc] == '1': 20 | loc = random.randrange(length) 21 | string = string[:loc] + '1' + string[loc+1:] 22 | counter -= 1 23 | return string 24 | 25 | def createConstRandomStrings(length, numStrings, numOnes): 26 | """Returns a list of random binary strings, each with exactly numOnes ones.""" 27 | strings = [] 28 | for i in range(numStrings): 29 | strings.append(createRandomString(length, numOnes)) 30 | return strings 31 | 32 | def createIndependentRandomStrings(length, numStrings, q = -1, numOnes = -1): 33 | """Returns a set (size numStrings) of binary strings where each bit is 34 | randomly 0 or 1 with probability s.t. the probability of 2 strings meshing 35 | is q.""" 36 | if q >= 0: 37 | # print 'q = (1-p^2)^b' 38 | p = math.sqrt(1 - (q**(1.0/length))) 39 | elif numOnes > 0: 40 | # print 'p = n/b' 41 | p = float(numOnes)/length 42 | # print "q = {}".format(((1 - (p)**2)**length)) 43 | elif numOnes == 0: 44 | raise Exception("numOnes should not be 0.") 45 | else: 46 | raise Exception('must specify q or numOnes') 47 | # print "q = {}".format(q) 48 | # print "p = {}".format(p) 49 | # print "occupancy mean is {} variance is {}".format(length*p,length*p*(1-p)) 50 | all_strings = [] 51 | for i in range(numStrings): 52 | string = '' 53 | for i in range(length): 54 | if random.random() < p: 55 | string += '1' 56 | else: 57 | string += '0' 58 | all_strings.append(string) 59 | string = '' 60 | 61 | return all_strings 62 | 63 | def providedStrings(filename): 64 | """not yet implemented. will return the list of strings from some location.""" 65 | return None 66 | 67 | 68 | #print createRandomString(32, 16) 69 | #print createIndependentRandomStrings(32,.5,1) -------------------------------------------------------------------------------- /theory/deg_bound_check.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Apr 15 15:25:44 2016 4 | 5 | @author: devd 6 | """ 7 | 8 | from __future__ import division 9 | from createRandomString import * 10 | from makeGraph import * 11 | import matplotlib.pyplot as plt 12 | import matplotlib.patches as mpatches 13 | import networkx as nx 14 | import numpy as np 15 | import time 16 | from compute_exp_Y import compute_exp_Y, compute_degree_bound, compute_isolated_edge_bound, compute_degreeplusone_bound, compute_improved_degreeplusone_bound 17 | from choose import compute_q 18 | import logging 19 | 20 | logging.getLogger('').handlers = [] 21 | logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') 22 | 23 | def compute_actual_improved_deg_bound(graph): 24 | edgeList = graph.edges() 25 | lower_bound = 0 26 | for edge in edgeList: 27 | deg1 = graph.degree(edge[0]) 28 | deg2 = graph.degree(edge[1]) 29 | # if deg1 == deg2 == 1: 30 | # lower_bound += 1 31 | # elif deg1 == 1 and deg2 == 2: 32 | # lower_bound+=.5 33 | # elif deg1 == 1: 34 | # lower_bound += (1.0/deg2) 35 | # elif deg2 ==1: 36 | # lower_bound += (1.0/deg1) 37 | # elif deg1 == 2 and deg2 == 3: 38 | # lower_bound += 1.0/3.0 39 | if deg1 == 2 and deg2 == 2: 40 | lower_bound += 1/3.0 41 | else: 42 | # lower_bound += min((1.0/(deg1+1)),(1.0/(deg2+1))) 43 | lower_bound += min((1.0/(deg1)),(1.0/(deg2))) 44 | return lower_bound 45 | 46 | def experiment(length, ones_range_min, ones_range_max, reps, numStrings): 47 | strings = [] 48 | 49 | 50 | for numOnes in range(ones_range_min, ones_range_max+1): 51 | for iterations in range (reps): 52 | # for i in range(numStrings): 53 | # strings.append(createRandomString(length, numOnes)) 54 | strings = createIndependentRandomStrings(length = length, numStrings = numStrings, numOnes = numOnes) 55 | 56 | graph = makeGraph(strings) 57 | max_matching = len(nx.max_weight_matching(graph))/2 58 | lower_bound = compute_actual_improved_deg_bound(graph) 59 | if max_matching+.0001 < lower_bound: 60 | print 'Max matching {} but lower bound {} for length {} strings, occupancy {}, {} total strings'.format(max_matching, lower_bound, length, numOnes, numStrings) 61 | print nx.triangles(graph) 62 | print graph.degree() 63 | # nx.draw(graph) 64 | strings = [] 65 | 66 | def experiment2(numNodes): 67 | graph = nx.complete_graph(numNodes) 68 | max_matching = len(nx.max_weight_matching(graph))/2 69 | lower_bound = compute_actual_improved_deg_bound(graph) 70 | if max_matching+.0001 < lower_bound: 71 | print 'Max matching {} but lower bound {} for complete graph on {} nodes'.format(max_matching, lower_bound, numNodes) 72 | 73 | 74 | def plot_it(length, ones_range_min, ones_range_max, reps, numStrings): 75 | experiment(length, ones_range_min, ones_range_max, reps, numStrings) 76 | 77 | 78 | 79 | if __name__ == '__main__': 80 | # experiment2(101) 81 | 82 | length = [32] 83 | ones_range_min = 1 84 | # ones_range_min = 8 85 | ones_range_max = 32 86 | reps = 100 87 | numStrings= [20] 88 | 89 | start = time.time() 90 | t = time.localtime() 91 | logging.info('deg bound experiment with lengths {}, num strings {} started'.format(length, numStrings)) 92 | for l in length: 93 | for n in numStrings: 94 | plot_it(l, ones_range_min, int(l/2), reps, n) 95 | logging.info('completed length {} num strings {}'.format(l, n)) 96 | end = time.time() 97 | print 'done in {} seconds'.format(end-start) 98 | # graph = makeGraph(['0101','1100','0011','1010']) 99 | # nx.draw(graph) 100 | # print compute_actual_improved_deg_bound(graph) -------------------------------------------------------------------------------- /theory/degcheck.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jun 13 11:46:53 2016 4 | 5 | @author: devd 6 | """ 7 | 8 | from createRandomString import * 9 | from makeGraph import * 10 | import networkx as nx 11 | import matplotlib.pyplot as plt 12 | 13 | def experiment(numOnes, numStrings, length, reps): 14 | maxdeg = [] 15 | mindeg = [] 16 | for i in range(reps): 17 | strings = [] 18 | for i in range(numStrings): 19 | strings.append(createRandomString(length, numOnes)) 20 | graph = makeGraph(strings) 21 | # nx.draw(graph) 22 | max = min = graph.degree(0) 23 | # print 'initial max/min is {}'.format(max) 24 | for j in range(1, numStrings): 25 | deg = graph.degree(j) 26 | # print 'this one has degree {}'.format(deg) 27 | if deg>max: 28 | max = deg 29 | if deg span list from a mesh dump file at `json_path` 24 | ''' 25 | size_classes = defaultdict(list) 26 | 27 | with open(json_path) as f: 28 | for l in f.readlines(): 29 | obj = json.loads(l) 30 | span = Span(obj) 31 | size_classes[span.size].append(span) 32 | 33 | return size_classes 34 | 35 | 36 | def count_meshes(mesher, spans): 37 | bitmaps = [s.bitmap for s in spans] 38 | if len(bitmaps) % 2 == 1: 39 | bitmaps.append('1' * len(s.bitmap)) 40 | 41 | return mesher(bitmaps) 42 | 43 | 44 | def main(): 45 | parser = argparse.ArgumentParser() 46 | parser.add_argument('--size', type=int, help='size to dump graph') 47 | parser.add_argument('json_file', nargs=1, help='A JSON dump from libmesh') 48 | args = parser.parse_args() 49 | 50 | if not args.json_file: 51 | parser.print_help() 52 | return 1 53 | 54 | size_classes = read_data(args.json_file[0]) 55 | 56 | sizes = sorted(size_classes.keys(), reverse=True) 57 | 58 | total_size = 0 59 | for size in sizes: 60 | spans = size_classes[size] 61 | total_size += sum([s.size * s.length for s in spans]) 62 | 63 | print('Total heap size: %.1f MiB' % (total_size * MB,)) 64 | 65 | saved = 0 66 | for size in sizes: 67 | if size >= 4096: 68 | continue 69 | spans = size_classes[size] 70 | # n = count_meshes(meshers.optimalMesher, spans) 71 | # n = count_meshes(meshers.optimalMesher, spans) 72 | n = count_meshes(meshers.greedyMesher, spans) 73 | print('\t%5d: %d spans (%d meshes)' % (size, len(spans), len(n))) 74 | saved += (size * spans[0].length) * len(n) 75 | 76 | print('Saved size: %.1f MiB' % (saved * MB,)) 77 | 78 | return 0 79 | 80 | 81 | if __name__ == '__main__': 82 | sys.exit(main()) 83 | -------------------------------------------------------------------------------- /theory/test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Apr 25 14:34:04 2016 4 | 5 | @author: devd 6 | """ 7 | from __future__ import division 8 | import logging 9 | import math 10 | from choose import nCr 11 | import numpy as np 12 | from scipy.misc import comb 13 | import createRandomString as c 14 | import meshers 15 | import time 16 | import random 17 | import functools 18 | import json 19 | import pickle 20 | import os 21 | from mesh_util import occupancySort, formatStrings, fast_q 22 | from createRandomString import createIndependentRandomStrings 23 | 24 | #logging.getLogger('').handlers = [] 25 | #logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') 26 | #logging.debug('This is a log message.') 27 | #logging.info('test') 28 | #logging.warning('double test') 29 | # 30 | 31 | #strings = createIndependentRandomStrings(4,10,numOnes = 2) 32 | #new_strings = [] 33 | #for string in strings: 34 | # new_strings.append((string, long(string, base=2))) 35 | #print new_strings 36 | #print "\n \n \n" 37 | ##occupancySort(strings) 38 | #new_strings.sort(key = lambda x: x[0].count("1")) 39 | #print new_strings 40 | 41 | strings = createIndependentRandomStrings(256, 10000, numOnes = 5) 42 | strings = formatStrings(strings) 43 | occs = [x[2] for x in strings] 44 | print np.mean(occs) 45 | print np.std(occs) 46 | 47 | def faster_q(length, occ1, occ2): 48 | numerator = 1 49 | for i in range(length-occ1, length-occ1-occ2, -1): 50 | numerator *= i 51 | denominator = 1 52 | for i in range(length, length-occ2, -1): 53 | denominator *= i 54 | return float(numerator)/float(denominator) 55 | 56 | length = 128 57 | 58 | start = time.time() 59 | for occ1 in range(0,50): 60 | for occ2 in range(0,50): 61 | result1 = fast_q(length, occ1, occ2) 62 | t1 = time.time() - start 63 | 64 | start = time.time() 65 | for occ1 in range(0,50): 66 | for occ2 in range(0,50): 67 | result2 = faster_q(length, occ1, occ2) 68 | t2 = time.time()-start 69 | print 'fast_q got {} in {} ms'.format(result1, t1) 70 | print 'faster_q got {} in {} ms'.format(result2, t2) -------------------------------------------------------------------------------- /tools/bazel: -------------------------------------------------------------------------------- 1 | ../bazel --------------------------------------------------------------------------------