Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARROW-456: Add jemalloc based MemoryPool #270

Closed
wants to merge 14 commits into from
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ addons:
- libboost-dev
- libboost-filesystem-dev
- libboost-system-dev
- libjemalloc-dev

matrix:
fast_finish: true
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ build_script:
- cd build
# A lot of features are still deactivated as they do not build on Windows
# * gbenchmark doesn't build with MSVC
- cmake -G "%GENERATOR%" -DARROW_BOOST_USE_SHARED=OFF -DARROW_IPC=OFF -DARROW_HDFS=OFF -DARROW_BUILD_BENCHMARKS=OFF ..
- cmake -G "%GENERATOR%" -DARROW_BOOST_USE_SHARED=OFF -DARROW_IPC=OFF -DARROW_HDFS=OFF -DARROW_BUILD_BENCHMARKS=OFF -DARROW_JEMALLOC=OFF ..
- cmake --build . --config Debug

# test_script:
Expand Down
5 changes: 5 additions & 0 deletions ci/travis_before_script_cpp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ set -ex

: ${CPP_BUILD_DIR=$TRAVIS_BUILD_DIR/cpp-build}

if [ $TRAVIS_OS_NAME == "osx" ]; then
brew update > /dev/null
brew install jemalloc
fi

mkdir $CPP_BUILD_DIR
pushd $CPP_BUILD_DIR

Expand Down
30 changes: 29 additions & 1 deletion cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ set(THIRDPARTY_DIR "${CMAKE_SOURCE_DIR}/thirdparty")

set(GFLAGS_VERSION "2.1.2")
set(GTEST_VERSION "1.7.0")
set(GBENCHMARK_VERSION "1.0.0")
set(GBENCHMARK_VERSION "1.1.0")
set(FLATBUFFERS_VERSION "1.3.0")

find_package(ClangTools)
Expand Down Expand Up @@ -74,6 +74,10 @@ if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
"Build the Arrow IPC extensions"
ON)

option(ARROW_JEMALLOC
"Build the Arrow jemalloc-based allocator"
ON)

option(ARROW_BOOST_USE_SHARED
"Rely on boost shared libraries where relevant"
ON)
Expand Down Expand Up @@ -238,6 +242,16 @@ function(ADD_ARROW_BENCHMARK_DEPENDENCIES REL_BENCHMARK_NAME)
add_dependencies(${BENCHMARK_NAME} ${ARGN})
endfunction()

# A wrapper for target_link_libraries() that is compatible with NO_BENCHMARKS.
function(ARROW_BENCHMARK_LINK_LIBRARIES REL_BENCHMARK_NAME)
if(NO_TESTS)
return()
endif()
get_filename_component(BENCHMARK_NAME ${REL_BENCHMARK_NAME} NAME_WE)

target_link_libraries(${BENCHMARK_NAME} ${ARGN})
endfunction()


############################################################
# Testing
Expand Down Expand Up @@ -526,7 +540,11 @@ if(ARROW_BUILD_BENCHMARKS)
set(GBENCHMARK_CMAKE_ARGS
"-DCMAKE_BUILD_TYPE=Release"
"-DCMAKE_INSTALL_PREFIX:PATH=${GBENCHMARK_PREFIX}"
"-DBENCHMARK_ENABLE_TESTING=OFF"
"-DCMAKE_CXX_FLAGS=-fPIC ${GBENCHMARK_CMAKE_CXX_FLAGS}")
if (APPLE)
set(GBENCHMARK_CMAKE_ARGS ${GBENCHMARK_CMAKE_ARGS} "-DBENCHMARK_USE_LIBCXX=ON")
endif()
if (CMAKE_VERSION VERSION_GREATER "3.2")
# BUILD_BYPRODUCTS is a 3.2+ feature
ExternalProject_Add(gbenchmark_ep
Expand Down Expand Up @@ -575,6 +593,12 @@ endif()
message(STATUS "RapidJSON include dir: ${RAPIDJSON_INCLUDE_DIR}")
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})

if (ARROW_JEMALLOC)
find_package(jemalloc REQUIRED)
ADD_THIRDPARTY_LIB(jemalloc
SHARED_LIB ${JEMALLOC_SHARED_LIB})
endif()

## Google PerfTools
##
## Disabled with TSAN/ASAN as well as with gold+dynamic linking (see comment
Expand Down Expand Up @@ -737,6 +761,10 @@ add_subdirectory(src/arrow)
add_subdirectory(src/arrow/io)
add_subdirectory(src/arrow/util)

if(ARROW_JEMALLOC)
add_subdirectory(src/arrow/jemalloc)
endif()

#----------------------------------------------------------------------
# IPC library

Expand Down
1 change: 1 addition & 0 deletions cpp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ variables
* Google Benchmark: `GBENCHMARK_HOME` (only required if building benchmarks)
* Flatbuffers: `FLATBUFFERS_HOME` (only required for the IPC extensions)
* Hadoop: `HADOOP_HOME` (only required for the HDFS I/O extensions)
* jemalloc: `JEMALLOC_HOME` (only required for the jemalloc-based memory pool)

## Continuous Integration

Expand Down
86 changes: 86 additions & 0 deletions cpp/cmake_modules/Findjemalloc.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Tries to find jemalloc headers and libraries.
#
# Usage of this module as follows:
#
# find_package(jemalloc)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# JEMALLOC_HOME -
# When set, this path is inspected instead of standard library locations as
# the root of the jemalloc installation. The environment variable
# JEMALLOC_HOME overrides this veriable.
#
# This module defines
# JEMALLOC_INCLUDE_DIR, directory containing headers
# JEMALLOC_SHARED_LIB, path to libjemalloc.so/dylib
# JEMALLOC_FOUND, whether flatbuffers has been found

if( NOT "$ENV{JEMALLOC_HOME}" STREQUAL "")
file( TO_CMAKE_PATH "$ENV{JEMALLOC_HOME}" _native_path )
list( APPEND _jemalloc_roots ${_native_path} )
elseif ( JEMALLOC_HOME )
list( APPEND _jemalloc_roots ${JEMALLOC_HOME} )
endif()

set(LIBJEMALLOC_NAMES jemalloc libjemalloc.so.1 libjemalloc.so.2 libjemalloc.dylib)

# Try the parameterized roots, if they exist
if ( _jemalloc_roots )
find_path( JEMALLOC_INCLUDE_DIR NAMES jemalloc/jemalloc.h
PATHS ${_jemalloc_roots} NO_DEFAULT_PATH
PATH_SUFFIXES "include" )
find_library( JEMALLOC_SHARED_LIB NAMES ${LIBJEMALLOC_NAMES}
PATHS ${_jemalloc_roots} NO_DEFAULT_PATH
PATH_SUFFIXES "lib" )
else ()
find_path( JEMALLOC_INCLUDE_DIR NAMES jemalloc/jemalloc.h )
message(STATUS ${JEMALLOC_INCLUDE_DIR})
find_library( JEMALLOC_SHARED_LIB NAMES ${LIBJEMALLOC_NAMES})
message(STATUS ${JEMALLOC_SHARED_LIB})
endif ()

if (JEMALLOC_INCLUDE_DIR AND JEMALLOC_SHARED_LIB)
set(JEMALLOC_FOUND TRUE)
else ()
set(JEMALLOC_FOUND FALSE)
endif ()

if (JEMALLOC_FOUND)
if (NOT jemalloc_FIND_QUIETLY)
message(STATUS "Found the jemalloc library: ${JEMALLOC_LIBRARIES}")
endif ()
else ()
if (NOT jemalloc_FIND_QUIETLY)
set(JEMALLOC_ERR_MSG "Could not find the jemalloc library. Looked in ")
if ( _flatbuffers_roots )
set(JEMALLOC_ERR_MSG "${JEMALLOC_ERR_MSG} in ${_jemalloc_roots}.")
else ()
set(JEMALLOC_ERR_MSG "${JEMALLOC_ERR_MSG} system search paths.")
endif ()
if (jemalloc_FIND_REQUIRED)
message(FATAL_ERROR "${JEMALLOC_ERR_MSG}")
else (jemalloc_FIND_REQUIRED)
message(STATUS "${JEMALLOC_ERR_MSG}")
endif (jemalloc_FIND_REQUIRED)
endif ()
endif ()

mark_as_advanced(
JEMALLOC_INCLUDE_DIR
JEMALLOC_SHARED_LIB
)
1 change: 1 addition & 0 deletions cpp/src/arrow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ ADD_ARROW_TEST(schema-test)
ADD_ARROW_TEST(status-test)
ADD_ARROW_TEST(table-test)

ADD_ARROW_BENCHMARK(builder-benchmark)
ADD_ARROW_BENCHMARK(column-benchmark)
6 changes: 2 additions & 4 deletions cpp/src/arrow/buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,11 @@ Status PoolBuffer::Reserve(int64_t new_capacity) {
uint8_t* new_data;
new_capacity = BitUtil::RoundUpToMultipleOf64(new_capacity);
if (mutable_data_) {
RETURN_NOT_OK(pool_->Allocate(new_capacity, &new_data));
memcpy(new_data, mutable_data_, size_);
pool_->Free(mutable_data_, capacity_);
RETURN_NOT_OK(pool_->Reallocate(capacity_, new_capacity, &mutable_data_));
} else {
RETURN_NOT_OK(pool_->Allocate(new_capacity, &new_data));
mutable_data_ = new_data;
}
mutable_data_ = new_data;
data_ = mutable_data_;
capacity_ = new_capacity;
}
Expand Down
64 changes: 64 additions & 0 deletions cpp/src/arrow/builder-benchmark.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#include "benchmark/benchmark.h"

#include "arrow/builder.h"
#include "arrow/memory_pool.h"
#include "arrow/test-util.h"

namespace arrow {

constexpr int64_t kFinalSize = 256;

static void BM_BuildPrimitiveArrayNoNulls(
benchmark::State& state) { // NOLINT non-const reference
// 2 MiB block
std::vector<int64_t> data(256 * 1024, 100);
while (state.KeepRunning()) {
Int64Builder builder(default_memory_pool(), arrow::int64());
for (int i = 0; i < kFinalSize; i++) {
// Build up an array of 512 MiB in size
builder.Append(data.data(), data.size(), nullptr);
}
std::shared_ptr<Array> out;
builder.Finish(&out);
}
state.SetBytesProcessed(
state.iterations() * data.size() * sizeof(int64_t) * kFinalSize);
}

BENCHMARK(BM_BuildPrimitiveArrayNoNulls)->Repetitions(3)->Unit(benchmark::kMillisecond);

static void BM_BuildVectorNoNulls(
benchmark::State& state) { // NOLINT non-const reference
// 2 MiB block
std::vector<int64_t> data(256 * 1024, 100);
while (state.KeepRunning()) {
std::vector<int64_t> builder;
for (int i = 0; i < kFinalSize; i++) {
// Build up an array of 512 MiB in size
builder.insert(builder.end(), data.cbegin(), data.cend());
}
}
state.SetBytesProcessed(
state.iterations() * data.size() * sizeof(int64_t) * kFinalSize);
}

BENCHMARK(BM_BuildVectorNoNulls)->Repetitions(3)->Unit(benchmark::kMillisecond);

} // namespace arrow
1 change: 1 addition & 0 deletions cpp/src/arrow/builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ Status PrimitiveBuilder<T>::Resize(int32_t capacity) {
const int64_t new_bytes = TypeTraits<T>::bytes_required(capacity);
RETURN_NOT_OK(data_->Resize(new_bytes));
raw_data_ = reinterpret_cast<value_type*>(data_->mutable_data());
// TODO(emkornfield) valgrind complains without this
memset(data_->mutable_data() + old_bytes, 0, new_bytes - old_bytes);
}
return Status::OK();
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/arrow/column-benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ std::shared_ptr<Array> MakePrimitive(int32_t length, int32_t null_count = 0) {
static void BM_BuildInt32ColumnByChunk(
benchmark::State& state) { // NOLINT non-const reference
ArrayVector arrays;
for (int chunk_n = 0; chunk_n < state.range_x(); ++chunk_n) {
for (int chunk_n = 0; chunk_n < state.range(0); ++chunk_n) {
arrays.push_back(MakePrimitive<Int32Array>(100, 10));
}
const auto INT32 = std::make_shared<Int32Type>();
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/arrow/io/interfaces.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ Status ReadableFileInterface::ReadAt(
}

Status Writeable::Write(const std::string& data) {
return Write(reinterpret_cast<const uint8_t*>(data.c_str()),
static_cast<int64_t>(data.size()));
return Write(
reinterpret_cast<const uint8_t*>(data.c_str()), static_cast<int64_t>(data.size()));
}

} // namespace io
Expand Down
13 changes: 13 additions & 0 deletions cpp/src/arrow/io/io-file-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,19 @@ class MyMemoryPool : public MemoryPool {

void Free(uint8_t* buffer, int64_t size) override { std::free(buffer); }

Status Reallocate(int64_t old_size, int64_t new_size, uint8_t** ptr) override {
*ptr = reinterpret_cast<uint8_t*>(std::realloc(*ptr, new_size));

if (*ptr == NULL) {
std::stringstream ss;
ss << "realloc of size " << new_size << " failed";
return Status::OutOfMemory(ss.str());
}


return Status::OK();
}

int64_t bytes_allocated() const override { return -1; }

int64_t num_allocations() const { return num_allocations_; }
Expand Down
Loading