-
Notifications
You must be signed in to change notification settings - Fork 19
C++ base of AsyncPriorityQueue
#483
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
7fabc67
Scale queue_priority instead of using a tuple
gxuu a0ea5e3
Implement underlying util for AsyncPriorityQueue
gxuu 0d1176d
C extension for StablePriorityQueue
gxuu b833a82
Make use of StablePriorityQueue
gxuu c8aabe6
Add StablePriorityQueue to package build target
gxuu 2d07159
Make StablePriroityQueue a heap type
gxuu cbae0c5
Put PyStablePriorityQueue in namespace
gxuu ea0611d
Namespace fix
gxuu 735f3a6
remove using namespace
gxuu 141c2ed
apply https://github.com/finos/opengris-scaler/pull/483#discussion_r2…
gxuu 4a04f39
Add expl. for queue_priority
gxuu 6d66c6b
Merge branch 'main' into dec-cpp-apq
sharpener6 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| #include "scaler/utility/stable_priority_queue.h" | ||
|
|
||
| #include "scaler/utility/pymod/compatibility.h" | ||
|
|
||
| namespace scaler { | ||
| namespace utility { | ||
| namespace stable_priority_queue { | ||
| namespace pymod { | ||
|
|
||
| using scaler::utility::pymod::OwnedPyObject; | ||
|
|
||
| extern "C" { | ||
| struct PyStablePriorityQueue { | ||
| PyObject_HEAD; | ||
| scaler::utility::StablePriorityQueue<OwnedPyObject<PyObject>> queue; | ||
| }; | ||
|
|
||
| static PyObject* PyStablePriorityQueueNew(PyTypeObject* type, PyObject* args, PyObject* kwds) | ||
| { | ||
| PyStablePriorityQueue* self {}; | ||
| self = (PyStablePriorityQueue*)type->tp_alloc(type, 0); | ||
| return (PyObject*)self; | ||
| } | ||
|
|
||
| static int PyStablePriorityQueueInit(PyStablePriorityQueue* self, PyObject* args, PyObject* kwds) | ||
| { | ||
| new (&((PyStablePriorityQueue*)self)->queue) scaler::utility::StablePriorityQueue<OwnedPyObject<PyObject>>(); | ||
| return 0; | ||
| } | ||
|
|
||
| static void PyStablePriorityQueueDealloc(PyObject* self) | ||
| { | ||
| ((PyStablePriorityQueue*)self)->queue.~StablePriorityQueue<OwnedPyObject<PyObject>>(); | ||
| Py_TYPE(self)->tp_free((PyObject*)self); | ||
| } | ||
|
|
||
| static PyObject* PyStablePriorityQueuePut(PyStablePriorityQueue* self, PyObject* args) | ||
| { | ||
| int64_t priority {}; | ||
| PyObject* data {}; | ||
|
|
||
| if (!PyArg_ParseTuple(args, "LO", &priority, &data)) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| self->queue.put({priority, OwnedPyObject<>::fromBorrowed(data)}); | ||
| Py_RETURN_NONE; | ||
| } | ||
|
|
||
| static PyObject* PyStablePriorityQueueGet(PyStablePriorityQueue* self, PyObject* args) | ||
| { | ||
| (void)args; | ||
|
|
||
| auto [priorityAndData, exists] = self->queue.get(); | ||
| if (!exists) { | ||
| PyErr_SetString(PyExc_ValueError, "cannot get from an empty queue"); | ||
| return nullptr; | ||
| } | ||
|
|
||
| auto [priority, data] = std::move(priorityAndData); | ||
| OwnedPyObject<> res = PyTuple_Pack(2, PyLong_FromLongLong(priority), data.take()); | ||
| if (!res) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| return res.take(); | ||
| } | ||
|
|
||
| static PyObject* PyStablePriorityQueueRemove(PyStablePriorityQueue* self, PyObject* args) | ||
| { | ||
| PyObject* data {}; | ||
| if (!PyArg_ParseTuple(args, "O", &data)) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| self->queue.remove(OwnedPyObject<>::fromBorrowed(data)); | ||
| Py_RETURN_NONE; | ||
| } | ||
|
|
||
| static PyObject* PyStablePriorityQueueDecreasePriority(PyStablePriorityQueue* self, PyObject* args) | ||
| { | ||
| PyObject* data {}; | ||
| if (!PyArg_ParseTuple(args, "O", &data)) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| self->queue.decreasePriority(OwnedPyObject<>::fromBorrowed(data)); | ||
|
|
||
| Py_RETURN_NONE; | ||
| } | ||
|
|
||
| static PyObject* PyStablePriorityQueueMaxPriorityItem(PyStablePriorityQueue* self, PyObject* args) | ||
| { | ||
| (void)args; | ||
|
|
||
| auto [priorityAndData, exists] = self->queue.maxPriorityItem(); | ||
| if (!exists) { | ||
| PyErr_SetString(PyExc_ValueError, "cannot return max priority item from empty queue"); | ||
| return nullptr; | ||
| } | ||
|
|
||
| auto [priority, data] = std::move(priorityAndData); | ||
| OwnedPyObject<> res = PyTuple_Pack(2, PyLong_FromLongLong(priority), data.take()); | ||
| if (!res) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| return res.take(); | ||
| } | ||
|
|
||
| // Define the methods for the StablePriorityQueue Python class | ||
| static PyMethodDef PyStablePriorityQueueMethods[] = { | ||
| {"put", (PyCFunction)PyStablePriorityQueuePut, METH_VARARGS, "Put a priority-item list to the queue"}, | ||
| {"get", | ||
| (PyCFunction)PyStablePriorityQueueGet, | ||
| METH_VARARGS, | ||
| "Pop and Return priority-item list with the highest priority in the queue"}, | ||
| {"remove", (PyCFunction)PyStablePriorityQueueRemove, METH_VARARGS, "Remove an item from the queue"}, | ||
| {"decrease_priority", | ||
| (PyCFunction)PyStablePriorityQueueDecreasePriority, | ||
| METH_VARARGS, | ||
| "Decrease priority of an item"}, | ||
| {"max_priority_item", | ||
| (PyCFunction)PyStablePriorityQueueMaxPriorityItem, | ||
| METH_VARARGS, | ||
| "Return priority-item list with the highest priority in the queue"}, | ||
| {nullptr}, | ||
| }; | ||
|
|
||
| static Py_ssize_t PyStablePriorityQueueSize(PyObject* self) | ||
| { | ||
| return ((PyStablePriorityQueue*)self)->queue.size(); | ||
| } | ||
|
|
||
| static PyType_Slot PyStablePriorityQueueSlots[] = { | ||
| {Py_tp_new, (void*)PyStablePriorityQueueNew}, | ||
| {Py_tp_init, (void*)PyStablePriorityQueueInit}, | ||
| {Py_tp_dealloc, (void*)PyStablePriorityQueueDealloc}, | ||
| {Py_tp_methods, (void*)PyStablePriorityQueueMethods}, | ||
| {Py_sq_length, (void*)PyStablePriorityQueueSize}, | ||
| {Py_tp_doc, (void*)"StablePriorityQueue"}, | ||
| {0, nullptr}, | ||
| }; | ||
|
|
||
| static PyType_Spec PyStablePriorityQueueSpec = { | ||
| .name = "stable_priority_queue.StablePriorityQueue", | ||
| .basicsize = sizeof(PyStablePriorityQueue), | ||
| .itemsize = 0, | ||
| .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | ||
| .slots = PyStablePriorityQueueSlots, | ||
| }; | ||
|
|
||
| static PyModuleDef stable_priority_queue_module = { | ||
| .m_base = PyModuleDef_HEAD_INIT, | ||
| .m_name = "stable_priority_queue", | ||
| .m_doc = PyDoc_STR("A module that wraps a C++ StablePriorityQueue class"), | ||
| .m_size = 0, | ||
| .m_slots = nullptr, | ||
| .m_free = nullptr, | ||
| }; | ||
| } | ||
| } // namespace pymod | ||
| } // namespace stable_priority_queue | ||
| } // namespace utility | ||
| } // namespace scaler | ||
|
|
||
| PyMODINIT_FUNC PyInit_stable_priority_queue(void) | ||
| { | ||
| using scaler::utility::stable_priority_queue::pymod::PyStablePriorityQueueSpec; | ||
| using scaler::utility::stable_priority_queue::pymod::stable_priority_queue_module; | ||
|
|
||
| PyObject* m = PyModule_Create(&stable_priority_queue_module); | ||
| if (!m) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| PyObject* type = PyType_FromSpec(&PyStablePriorityQueueSpec); | ||
| if (!type) { | ||
| Py_DECREF(m); | ||
| return nullptr; | ||
| } | ||
|
|
||
| if (PyModule_AddObject(m, "StablePriorityQueue", type) < 0) { | ||
| Py_DECREF(type); | ||
| Py_DECREF(m); | ||
| return nullptr; | ||
| } | ||
|
|
||
| return m; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <cstdint> | ||
| #include <map> | ||
| #include <unordered_map> | ||
|
|
||
| namespace scaler { | ||
| namespace utility { | ||
|
|
||
| template <typename T> | ||
| struct StablePriorityQueue { | ||
| using PriorityType = int64_t; | ||
| using CounterType = uint64_t; | ||
| using MapKeyType = std::pair<PriorityType, CounterType>; | ||
| using ItemType = std::pair<PriorityType, T>; | ||
|
|
||
| std::unordered_map<T, MapKeyType> _locator; | ||
| std::map<MapKeyType, T> _queue; | ||
| CounterType _itemCounter; | ||
|
|
||
| StablePriorityQueue(): _itemCounter {} {} | ||
|
|
||
| constexpr uint64_t size() const { return _queue.size(); } | ||
|
|
||
| void put(ItemType item) | ||
| { | ||
| const auto& [priority, data] = item; | ||
| MapKeyType mapKey = {priority, _itemCounter}; | ||
| _locator[data] = mapKey; | ||
| _queue[mapKey] = std::move(data); | ||
| ++_itemCounter; | ||
| } | ||
|
|
||
| std::pair<ItemType, bool> get() | ||
| { | ||
| ItemType res {}; | ||
| if (_queue.empty()) { | ||
| return {res, false}; | ||
| } | ||
|
|
||
| auto it = _queue.begin(); | ||
| MapKeyType key = std::move(it->first); | ||
| T data = std::move(it->second); | ||
|
|
||
| _queue.erase(it); | ||
| _locator.erase(data); | ||
|
|
||
| res.first = key.first; | ||
| res.second = std::move(data); | ||
| return {res, true}; | ||
| } | ||
|
|
||
| void remove(const T& data) | ||
| { | ||
| const auto it = _locator.find(data); | ||
| if (it == _locator.end()) { | ||
| return; | ||
| } | ||
|
|
||
| _queue.erase(it->second); | ||
| _locator.erase(data); | ||
| } | ||
|
|
||
| void decreasePriority(const T& data) | ||
| { | ||
| auto it = _locator.find(data); | ||
| if (it == _locator.end()) { | ||
| return; | ||
| } | ||
|
|
||
| auto& key = it->second; | ||
| auto oldData = std::move(_queue[key]); | ||
| _queue.erase(key); | ||
|
|
||
| --key.first; | ||
| key.second = _itemCounter; | ||
| _queue[key] = std::move(oldData); | ||
|
|
||
| ++_itemCounter; | ||
| } | ||
|
|
||
| std::pair<ItemType, bool> maxPriorityItem() const | ||
| { | ||
| if (_queue.empty()) { | ||
| return {{}, false}; | ||
| } | ||
| auto kvit = _queue.cbegin(); | ||
| return {ItemType {kvit->first.first, kvit->second}, true}; | ||
| } | ||
| }; | ||
|
|
||
| } // namespace utility | ||
| } // namespace scaler | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.