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

Add datafacade allocator backed by mmap #4881

Merged
merged 2 commits into from
Feb 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# UNRELEASED
- Changes from 5.16.0:
- Bugfixes: fix deduplication of route steps when waypoints are used [#4909](https://github.com/Project-OSRM/osrm-backend/issues/4909)
- Tools:
- `osrm-routed` accepts a new property `--memory_file` to store memory in a file on disk.
- NodeJS:
- `OSRM` object accepts a new option `memory_file` that stores the memory in a file on disk.


# 5.16.0
- Changes from 5.15.2:
Expand All @@ -17,7 +22,6 @@
- ADDED #4775: Exposes more information to the turn function, now being able to set turn weights with highway and access information of the turn as well as other roads at the intersection [#4775](https://github.com/Project-OSRM/osrm-backend/issues/4775)
- FIXED #4763: Add support for non-numerical units in car profile for maxheight [#4763](https://github.com/Project-OSRM/osrm-backend/issues/4763)
- ADDED #4872: Handling of `barrier=height_restrictor` nodes [#4872](https://github.com/Project-OSRM/osrm-backend/pull/4872)

# 5.15.2
- Changes from 5.15.1:
- Features:
Expand Down
1 change: 1 addition & 0 deletions docs/nodejs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var osrm = new OSRM('network.osrm');
Make sure you prepared the dataset with the correct toolchain.
- `options.shared_memory` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Connects to the persistent shared memory datastore.
This requires you to run `osrm-datastore` prior to creating an `OSRM` object.
- `options.memory_file` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** Path to a file on disk to store the memory using mmap.
- `options.path` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** The path to the `.osrm` files. This is mutually exclusive with setting {options.shared_memory} to true.
- `options.max_locations_trip` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. locations supported in trip query (default: unlimited).
- `options.max_locations_viaroute` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. locations supported in viaroute query (default: unlimited).
Expand Down
45 changes: 45 additions & 0 deletions include/engine/datafacade/mmap_memory_allocator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef OSRM_ENGINE_DATAFACADE_MMAP_MEMORY_ALLOCATOR_HPP_
#define OSRM_ENGINE_DATAFACADE_MMAP_MEMORY_ALLOCATOR_HPP_

#include "engine/datafacade/contiguous_block_allocator.hpp"

#include "storage/storage_config.hpp"

#include "util/vector_view.hpp"

#include <boost/iostreams/device/mapped_file.hpp>

#include <memory>

namespace osrm
{
namespace engine
{
namespace datafacade
{

/**
* This allocator uses file backed mmap memory block as the data location.
*/
class MMapMemoryAllocator : public ContiguousBlockAllocator
{
public:
explicit MMapMemoryAllocator(const storage::StorageConfig &config,
const boost::filesystem::path &memory_file);
~MMapMemoryAllocator() override final;

// interface to give access to the datafacades
storage::DataLayout &GetLayout() override final;
char *GetMemory() override final;

private:
storage::DataLayout *data_layout;
util::vector_view<char> mapped_memory;
boost::iostreams::mapped_file mapped_memory_file;
};

} // namespace datafacade
} // namespace engine
} // namespace osrm

#endif // OSRM_ENGINE_DATAFACADE_SHARED_MEMORY_ALLOCATOR_HPP_
28 changes: 28 additions & 0 deletions include/engine/datafacade_provider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "engine/data_watchdog.hpp"
#include "engine/datafacade.hpp"
#include "engine/datafacade/contiguous_internalmem_datafacade.hpp"
#include "engine/datafacade/mmap_memory_allocator.hpp"
#include "engine/datafacade/process_memory_allocator.hpp"
#include "engine/datafacade_factory.hpp"

Expand All @@ -25,6 +26,31 @@ template <typename AlgorithmT, template <typename A> class FacadeT> class DataFa
virtual std::shared_ptr<const Facade> Get(const api::TileParameters &) const = 0;
};

template <typename AlgorithmT, template <typename A> class FacadeT>
class ExternalProvider final : public DataFacadeProvider<AlgorithmT, FacadeT>
{
public:
using Facade = typename DataFacadeProvider<AlgorithmT, FacadeT>::Facade;

ExternalProvider(const storage::StorageConfig &config,
const boost::filesystem::path &memory_file)
: facade_factory(std::make_shared<datafacade::MMapMemoryAllocator>(config, memory_file))
{
}

std::shared_ptr<const Facade> Get(const api::TileParameters &params) const override final
{
return facade_factory.Get(params);
}
std::shared_ptr<const Facade> Get(const api::BaseParameters &params) const override final
{
return facade_factory.Get(params);
}

private:
DataFacadeFactory<FacadeT, AlgorithmT> facade_factory;
};

template <typename AlgorithmT, template <typename A> class FacadeT>
class ImmutableProvider final : public DataFacadeProvider<AlgorithmT, FacadeT>
{
Expand Down Expand Up @@ -74,6 +100,8 @@ template <typename AlgorithmT>
using WatchingProvider = detail::WatchingProvider<AlgorithmT, DataFacade>;
template <typename AlgorithmT>
using ImmutableProvider = detail::ImmutableProvider<AlgorithmT, DataFacade>;
template <typename AlgorithmT>
using ExternalProvider = detail::ExternalProvider<AlgorithmT, DataFacade>;
}
}

Expand Down
7 changes: 7 additions & 0 deletions include/engine/engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ template <typename Algorithm> class Engine final : public EngineInterface
<< routing_algorithms::name<Algorithm>();
facade_provider = std::make_unique<WatchingProvider<Algorithm>>();
}
else if (!config.memory_file.empty())
{
util::Log(logDEBUG) << "Using memory mapped filed at " << config.memory_file
<< " with algorithm " << routing_algorithms::name<Algorithm>();
facade_provider = std::make_unique<ExternalProvider<Algorithm>>(config.storage_config,
config.memory_file);
}
else
{
util::Log(logDEBUG) << "Using internal memory with algorithm "
Expand Down
1 change: 1 addition & 0 deletions include/engine/engine_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ struct EngineConfig final
int max_results_nearest = -1;
int max_alternatives = 3; // set an arbitrary upper bound; can be adjusted by user
bool use_shared_memory = true;
boost::filesystem::path memory_file;
Algorithm algorithm = Algorithm::CH;
std::string verbosity;
};
Expand Down
17 changes: 17 additions & 0 deletions include/nodejs/node_osrm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,31 @@ inline engine_config_ptr argumentsToEngineConfig(const Nan::FunctionCallbackInfo
if (path.IsEmpty())
return engine_config_ptr();

auto memory_file = params->Get(Nan::New("memory_file").ToLocalChecked());
if (memory_file.IsEmpty())
return engine_config_ptr();

auto shared_memory = params->Get(Nan::New("shared_memory").ToLocalChecked());
if (shared_memory.IsEmpty())
return engine_config_ptr();

if (!memory_file->IsUndefined())
{
if (path->IsUndefined())
{
Nan::ThrowError("memory_file option requires a path to a file.");
return engine_config_ptr();
}

engine_config->memory_file =
*v8::String::Utf8Value(Nan::To<v8::String>(memory_file).ToLocalChecked());
}

if (!path->IsUndefined())
{
engine_config->storage_config =
osrm::StorageConfig(*v8::String::Utf8Value(Nan::To<v8::String>(path).ToLocalChecked()));

engine_config->use_shared_memory = false;
}
if (!shared_memory->IsUndefined())
Expand Down
34 changes: 34 additions & 0 deletions include/util/mmap_file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,32 @@ util::vector_view<T> mmapFile(const boost::filesystem::path &file, RegionT &regi
SOURCE_REF);
}
}

template <typename T, typename RegionT>
util::vector_view<T>
mmapFile(const boost::filesystem::path &file, RegionT &region, const std::size_t size)
{
try
{
// Create a new file with the given size in bytes
boost::iostreams::mapped_file_params params;
params.path = file.string();
params.flags = boost::iostreams::mapped_file::readwrite;
params.new_file_size = size;
region.open(params);

std::size_t num_objects = size / sizeof(T);
auto data_ptr = region.data();
BOOST_ASSERT(reinterpret_cast<uintptr_t>(data_ptr) % alignof(T) == 0);
return util::vector_view<T>(reinterpret_cast<T *>(data_ptr), num_objects);
}
catch (const std::exception &exc)
{
throw exception(
boost::str(boost::format("File %1% mapping failed: %2%") % file % exc.what()) +
SOURCE_REF);
}
}
}

template <typename T>
Expand All @@ -48,6 +74,14 @@ util::vector_view<T> mmapFile(const boost::filesystem::path &file,
{
return detail::mmapFile<T>(file, region);
}

template <typename T>
util::vector_view<T> mmapFile(const boost::filesystem::path &file,
boost::iostreams::mapped_file &region,
std::size_t size)
{
return detail::mmapFile<T>(file, region, size);
}
}
}

Expand Down
53 changes: 53 additions & 0 deletions src/engine/datafacade/mmap_memory_allocator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include "engine/datafacade/mmap_memory_allocator.hpp"

#include "storage/storage.hpp"

#include "util/log.hpp"
#include "util/mmap_file.hpp"

#include "boost/assert.hpp"

namespace osrm
{
namespace engine
{
namespace datafacade
{

MMapMemoryAllocator::MMapMemoryAllocator(const storage::StorageConfig &config,
const boost::filesystem::path &memory_file)
{
storage::Storage storage(config);

if (!boost::filesystem::exists(memory_file))
{
storage::DataLayout initial_layout;
storage.PopulateLayout(initial_layout);

auto data_size = initial_layout.GetSizeOfLayout();
auto total_size = data_size + sizeof(storage::DataLayout);

mapped_memory = util::mmapFile<char>(memory_file, mapped_memory_file, total_size);

data_layout = reinterpret_cast<storage::DataLayout *>(mapped_memory.data());
*data_layout = initial_layout;
storage.PopulateData(*data_layout, GetMemory());
}
else
{
mapped_memory = util::mmapFile<char>(memory_file, mapped_memory_file);
data_layout = reinterpret_cast<storage::DataLayout *>(mapped_memory.data());
}
}

MMapMemoryAllocator::~MMapMemoryAllocator() {}

storage::DataLayout &MMapMemoryAllocator::GetLayout() { return *data_layout; }
char *MMapMemoryAllocator::GetMemory()
{
return mapped_memory.data() + sizeof(storage::DataLayout);
}

} // namespace datafacade
} // namespace engine
} // namespace osrm
1 change: 1 addition & 0 deletions src/nodejs/node_osrm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ NAN_MODULE_INIT(Engine::Init)
* @param {Boolean} [options.shared_memory] Connects to the persistent shared memory datastore.
* This requires you to run `osrm-datastore` prior to creating an `OSRM` object.
* @param {String} [options.path] The path to the `.osrm` files. This is mutually exclusive with setting {options.shared_memory} to true.
* @param {String} [options.memory_file] Path to a file to store the memory using mmap.
* @param {Number} [options.max_locations_trip] Max. locations supported in trip query (default: unlimited).
* @param {Number} [options.max_locations_viaroute] Max. locations supported in viaroute query (default: unlimited).
* @param {Number} [options.max_locations_distance_table] Max. locations supported in distance table query (default: unlimited).
Expand Down
3 changes: 3 additions & 0 deletions src/tools/routed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ inline unsigned generateServerProgramOptions(const int argc,
("shared-memory,s",
value<bool>(&config.use_shared_memory)->implicit_value(true)->default_value(false),
"Load data from shared memory") //
("memory_file",
value<boost::filesystem::path>(&config.memory_file),
"Store data in a memory mapped file rather than in process memory.") //
("algorithm,a",
value<EngineConfig::Algorithm>(&config.algorithm)
->default_value(EngineConfig::Algorithm::CH, "CH"),
Expand Down
2 changes: 2 additions & 0 deletions test/nodejs/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ if (process.env.OSRM_DATA_PATH !== undefined) {
exports.data_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "ch/monaco.osrm");
exports.mld_data_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "mld/monaco.osrm");
exports.corech_data_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "corech/monaco.osrm");
exports.test_memory_path = path.join(path.resolve(process.env.OSRM_DATA_PATH), "test_memory");
console.log('Setting custom data path to ' + exports.data_path);
} else {
exports.data_path = path.resolve(path.join(__dirname, "../data/ch/monaco.osrm"));
exports.mld_data_path = path.resolve(path.join(__dirname, "../data/mld/monaco.osrm"));
exports.corech_data_path = path.resolve(path.join(__dirname, "../data/corech/monaco.osrm"));
exports.test_memory_path = path.resolve(path.join(__dirname, "../data/test_memory"));
}
7 changes: 7 additions & 0 deletions test/nodejs/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var OSRM = require('../../');
var test = require('tape');
var monaco_path = require('./constants').data_path;
var test_memory_file = require('./constants').test_memory_file;
var monaco_mld_path = require('./constants').mld_data_path;
var monaco_corech_path = require('./constants').corech_data_path;

Expand Down Expand Up @@ -37,6 +38,12 @@ test('constructor: takes a shared memory argument', function(assert) {
assert.ok(osrm);
});

test('constructor: takes a memory file', function(assert) {
assert.plan(1);
var osrm = new OSRM({path: monaco_path, memory_file: test_memory_file});
assert.ok(osrm);
});

test('constructor: throws if shared_memory==false with no path defined', function(assert) {
assert.plan(1);
assert.throws(function() { new OSRM({shared_memory: false}); },
Expand Down