blob: 50a894134d0686670d753c1277ca388c353180e1 [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// 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.
#pragma once
#include "dctv.h"
#include <boost/intrusive/list.hpp>
#include <cmath>
#include <utility>
#include "hunk.h"
#include "list.h"
#include "mmap.h"
#include "pyutil.h"
#include "unique_fd.h"
#include "vector.h"
namespace dctv {
struct StringTable;
struct QueryExecution;
struct HunkListAccess {
using HunkList = boost::intrusive::list<
Hunk,
boost::intrusive::constant_time_size<false>,
boost::intrusive::member_hook<Hunk,
Hunk::HunkListLink,
&Hunk::link>
>;
};
struct QueryCacheConfig final {
QueryCacheConfig();
QueryCacheConfig(pyref args, pyref kwargs);
unique_pyref tmpdir;
size_t memory_lwm_nbytes = std::numeric_limits<size_t>::max();
size_t memory_hwm_nbytes = std::numeric_limits<size_t>::max();
size_t memory_increment_nbytes = 0;
size_t disk_max_nbytes = 0;
size_t mmap_min_nbytes;
size_t block_size = 4 * 1024 * 1024;
npy_intp max_freelist = 5;
npy_intp fill_ratio = 3;
size_t min_disk_spill_nbytes = 8192;
};
// Manages storage for a query session.
struct QueryCache final : BasePyObject,
HasPyCtor,
SupportsWeakRefs,
HasDict,
SupportsGcClear
{
explicit QueryCache(QueryCacheConfig config);
~QueryCache() noexcept;
int py_traverse(visitproc visit, void* arg) const noexcept;
int py_clear() noexcept;
// Clear the cache.
unique_pyref clear();
void prune();
// Keep track of QueryExecution instances using this cache.
void note_execution_in_progress(QueryExecution* qe);
void note_execution_done(QueryExecution* qe) noexcept;
inline const QueryCacheConfig& get_config() const noexcept;
// Return the number of tracked hunks.
npy_intp get_nhunks() const noexcept;
// Return the target block size for query operations. For various
// reasons, blocks may have different sizes, but we try to use this
// one as often as possible.
inline npy_intp get_block_size() const noexcept;
// Get the string table used for this query cache. Immutable.
inline StringTable* get_st() const noexcept;
// Get the number of bytes currently used to hunk data.
inline npy_intp get_memory_nbytes() const noexcept;
// Make an uninitialized hunk-backed array for use in the
// query system.
unique_pyarray make_uninit_array(unique_dtype dtype, npy_intp nelem);
// Set the score to assign to freshly-created hunks for testing
// purposes. Set SCORE_OVERRIDE to NAN to disable the override
// mechanism. Illegal to call in optimized builds.
HunkScore get_new_hunk_score_override();
void set_new_hunk_score_override(HunkScore score_override);
std::pair<String, UniqueFd> make_cache_file(std::string_view label);
UniqueFd open_cache_file(const String& name, int mode);
void delete_cache_file(const String& name) noexcept;
// Disable cache trim operations
struct TrimDisabler final : NoCopy {
explicit inline TrimDisabler(QueryCache* qc) noexcept;
inline ~TrimDisabler() noexcept;
private:
QueryCache* qc;
};
static PyTypeObject pytype;
private:
// QueryCache is intimately tied to the hunk mechanism.
friend struct Hunk;
struct Freelist {
size_t nbytes;
List<MmapShared> spare;
};
Freelist* find_fl(size_t sz) noexcept;
void report() noexcept;
void trim_memory(size_t target_memory_nbytes,
Hunk* protect_hunk /*nullable*/);
// Note changes to memory use. Called by Hunk when creating,
// resizing, deflating, inflating, and destroying hunk data.
void account_memory(size_t nbytes, Hunk* protect_hunk /*nullable*/);
void unaccount_memory(size_t nbytes) noexcept;
// Allocate a memory mapping of at least NBYTES bytes for backing a hunk.
MmapShared allocate_mmap(size_t nbytes);
// Determine whether an allocation should use the regular C heap or
// the mmap-chunk machinery for an allocation of a particular size.
bool should_use_heap(size_t nbytes) const noexcept;
// Determine whether we should compress a blob to memory or just
// spill it to disk.
bool should_spill_to_memory(size_t nbytes) const noexcept;
// Resize a memory mapping, preserving contents. The returned
// memory mapping will have room for at least NEW_NBYTES bytes, but
// may also be larger. We try to avoid copying when possible.
// Does _not_ do memory accounting; it's the caller's job to call
// account_memory() or unaccount_memory() as appropriate.
void resize_mmap(MmapShared* map, size_t new_nbytes);
// Return a memory mapping allocated using allocate_mmap() to the
// free list.
void donate_mmap(MmapShared for_freelist) noexcept;
QueryCacheConfig config;
unique_obj_pyref<StringTable> st;
Vector<Freelist> freelists;
Vector<QueryExecution*> executions;
HunkScore new_hunk_score_override = NAN;
HunkListAccess::HunkList hunks;
npy_intp memory_nbytes = 0;
npy_intp disk_nbytes = 0;
UniqueFd cachedir;
bool disable_trim = false;
int64_t file_name_counter = 0;
static PyGetSetDef pygetset[];
static PyMemberDef pymembers[];
static PyMethodDef pymethods[];
};
void init_query_cache(pyref m);
} // namespace dctv
#include "query_cache-inl.h"