1
0
Fork
You've already forked structstore
0
Multi-process shared data structures without communication overhead. Seamlessly interoperate concurrently in shared memory from C++ and/or Python without writing bindings.
  • C++ 80.9%
  • CMake 7.8%
  • Python 6.1%
  • Shell 5.2%
Max Mertens 94875243fa
All checks were successful
ci/woodpecker/push/test_archlinux/1 Pipeline was successful
ci/woodpecker/push/test_devel_clang Pipeline was successful
ci/woodpecker/push/test_devel_coverage Pipeline was successful
ci/woodpecker/push/test_devel_extralarge/1 Pipeline was successful
ci/woodpecker/push/test_devel_release Pipeline was successful
ci/woodpecker/push/test_pyproj/1 Pipeline was successful
ci/woodpecker/tag/test_archlinux/1 Pipeline was successful
ci/woodpecker/tag/test_devel_clang Pipeline was successful
ci/woodpecker/tag/test_devel_coverage Pipeline was successful
ci/woodpecker/tag/test_devel_extralarge/1 Pipeline was successful
ci/woodpecker/tag/test_devel_release Pipeline was successful
ci/woodpecker/tag/test_pyproj/1 Pipeline was successful
ci/woodpecker/release/test_archlinux/1 Pipeline was successful
ci/woodpecker/release/test_devel_clang Pipeline was successful
ci/woodpecker/release/test_devel_coverage Pipeline was successful
ci/woodpecker/release/test_devel_extralarge/1 Pipeline was successful
ci/woodpecker/release/test_devel_release Pipeline was successful
ci/woodpecker/release/test_pyproj/1 Pipeline was successful
ci/woodpecker/push/test_archlinux/2 Pipeline was successful
ci/woodpecker/push/test_devel_extralarge/2 Pipeline was successful
ci/woodpecker/push/test_devel_sanitize Pipeline was successful
ci/woodpecker/push/test_pyproj/2 Pipeline was successful
ci/woodpecker/tag/test_archlinux/2 Pipeline was successful
ci/woodpecker/tag/test_devel_extralarge/2 Pipeline was successful
ci/woodpecker/tag/test_devel_sanitize Pipeline was successful
ci/woodpecker/tag/test_pyproj/2 Pipeline was successful
ci/woodpecker/release/test_archlinux/2 Pipeline was successful
ci/woodpecker/release/test_devel_extralarge/2 Pipeline was successful
ci/woodpecker/release/test_devel_sanitize Pipeline was successful
ci/woodpecker/release/test_pyproj/2 Pipeline was successful
Merge pull request 'v0.6.1' ( #2 ) from develop into main
Reviewed-on: #2 
2025年12月22日 12:15:09 +01:00
.woodpecker CHG: Disable some arm64 builds to save CI resources 2025年12月12日 23:07:11 +01:00
cmake Switched to 32bit pointers and hashes. Python bindings broken for now 2025年02月10日 23:07:25 +01:00
ext Switched to ankerl::unordered_dense with proper fancy pointer support 2025年02月08日 20:34:09 +01:00
fuzzer Add flag for extra large StructStore with 64 bit pointers 2025年12月10日 09:08:37 +01:00
scripts Add flag for extra large StructStore with 64 bit pointers 2025年12月10日 09:08:37 +01:00
src v0.6.1 2025年12月22日 12:13:24 +01:00
tests CHG: Store member infos statically; reference alloc in FieldView 2025年12月22日 11:08:59 +01:00
.gitignore Moved python casts to separate function registry, support pickle 2024年10月17日 08:35:46 +02:00
.gitlab-ci.yml Revised readme; run snippets during tests 2025年09月19日 06:43:21 +02:00
.gitmodules Switched to ankerl::unordered_dense with proper fancy pointer support 2025年02月08日 20:34:09 +01:00
CHANGELOG.md v0.6.1 2025年12月22日 12:13:24 +01:00
CMakeLists.txt v0.6.1 2025年12月22日 12:13:24 +01:00
LICENSE Relicensed StructStore under LGPL-3.0-only 2025年09月18日 23:17:03 +02:00
PKGBUILD v0.6.1 2025年12月22日 12:13:24 +01:00
pyproject.toml CHG: Simplify substore syntax in readme example 2025年12月12日 23:00:16 +01:00
README.md CHG: Store member infos statically; reference alloc in FieldView 2025年12月22日 11:08:59 +01:00
requirements.txt v0.6.0 2025年12月12日 22:33:59 +01:00

StructStore

status-badge

Multi-process shared data structures without communication overhead.

Features:

  • Efficiently operate on arbitrary* C++ data structures in shared memory.
  • Seamlessly operate on the same data from Python without writing Python bindings.
  • Directly work on data structures in mmap'ed files.
  • Work in progress! Do not use in productive software (yet).

* note: See supported types below. STL containers need to be replaced by shared memory-compatible ones.

Applications

  • Share nested structures and containers between processes without worrying about communication and synchronization.
  • Do performance-critical operations in C++ while controlling parameters and visualizing live results in a Python GUI.
  • Directly create/read/modify large dataset files without (de)serialization.
  • Send program state via network and continue processing elsewhere.

Usage examples

Create and access fields from C++:

#include <structstore/structstore.hpp>namespace stst = structstore;
void example1() {
 // all fields will reside in shared memory
 stst::StructStore shdata("/shdata");
 shdata["num"] = 42;
 shdata["str"] = "str";
 shdata["nested"]["flag"] = false;
 // serialize to a simple text representation:
 std::cout << "shared data: " << *shdata << std::endl;
 // lock for atomic updates spanning multiple lines:
 {
 auto lock = shdata->write_lock();
 shdata["str"] = "foo";
 }
}

Create and access the same fields from Python at the same time:

import structstore
shdata = structstore.StructStore("/shdata")
shdata.num = 10
shdata.nested = dict(flag=True)
# recursively convert to pure Python types:
print(shdata.nested.deepcopy())

Alternatively, use C++ classes that will reside in shared memory:

struct Sample : public stst::Struct<Sample> {
 inline static const stst::TypeInfo& type_info = stst::register_type<Sample>("Sample");
 double t = 0.0;
 uint16_t nums[2];
 stst::OffsetPtr<double> t_ptr = &t;
 explicit Sample() {
 store_ref("t", t);
 store_ref("num1", nums[0]);
 store_ref("num2", nums[1]);
 store_ref("t_ptr", t_ptr);
 }
 Sample& operator=(const Sample& other) {
 copy_from(other);
 t_ptr = &t;
 return *this;
 }
};
void example2() {
 // the same code can be used when running multiple processes
 stst::StructStore shdata("/shdata1");
 Sample& sample = shdata["sample"];
 std::cout << "shared sample: " << sample << std::endl;
}

If class instances need to reside on the stack or heap (or if the above method is undesired due to other reasons), use a C++ class that only refers to data in shared memory:

struct Settings {
 stst::FieldMap& fields;
 int& num = fields["num"] = 5;
 double& value = fields["value"] = 3.14;
 bool& flag = fields["flag"] = true;
 stst::String& str = fields["str"] = "foo";
 explicit Settings(stst::FieldMap& fields) : fields(fields) {}
};
void example3() {
 // the same code is used when accessing from multiple processes
 stst::StructStore shdata("/shdata");
 stst::FieldMap& settings_fields = shdata["settings"];
 Settings settings{settings_fields};
 std::cout << "shared settings: " << settings_fields << std::endl;
}

Seamlessly operate on the same data from Python at the same time:

shdata = structstore.StructStore("/shdata1")
# lock for atomic updates spanning multiple lines:
with shdata.write_lock():
 if hasattr(shdata, "sample"):
 shdata.sample.flag = True
 shdata.settings = dict(num=6, value=1.414, flag=False, str="foo")

Alternatively, use (data)classes in Python:

from dataclasses import dataclass
from typing import List
class Substate:
 def __init__(self, subnum: int):
 self.subnum = subnum
@dataclass
class State:
 num: int
 mystr: str
 flag: bool
 substate: Substate
 lst: List[int]
shdata = structstore.StructStore("/shdata2")
shdata.state = State(5, 'foo', True, Substate(42), [0, 1])

Directly working on mmap'ed files is as easy as setting a flag:

shdata = structstore.StructStore("/my/dataset", use_file=True)
shdata.values[42] += 3.14

Visualize and control data with a Python GUI (powered by the amazing imviz library):

import imviz as viz
shdata = structstore.StructStore("/shdata")
while viz.wait(vsync=True):
 if viz.begin_window('shared data'):
 # lock for atomic access
 with shdata.write_lock()
 # this automagic visualization takes about 0.5ms per frame
 viz.autogui(shdata)

Send snapshots of structured date via network (only works if both hosts have the same in-memory object representation):

shdata = structstore.StructStore("/shdata3")
buf = shdata.to_bytes()
# now send `buf` using your favorite network library

Implementation details

Dynamic structures (such as the internal field map and any containers) use a custom allocator with a memory block residing next to the StructStore itself. Thus, the whole structure including dynamic structures with pointers can be mmap'ed by several processes.

Limitations

  • Supported types: int, double, string, bool, list, NumPy float64 vectors, 2D NumPy float64 arrays, nested structures, user-defined classes.
  • The shared memory region is limited to a size of 8GB due to 32bit offset pointers.
  • Opening shared memory multiple times from one process (e.g., in separate threads) is currently not supported.
  • All processes working on the same data need to use the same memory layout (same CPU endianness, same StructStore version, same compiler, etc.).
  • Overall, the library is by no means complete and especially lacks documentation.

Contribution

This is a recreational project developed out of personal interest and needs, released in case someone finds it useful. Therefore, currently, feature requests, pull requests, and bug reports will probably not be considered. Feel free to still create an issue or contact me though, who knows what happens. :)

License

This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License v3.0 as published by the Free Software Foundation. See LICENSE file.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU General Lesser Public License along with this program. If not, see https://www.gnu.org/licenses/.