From e277b916578b0c37992a25be313e0d8d916ff2e8 Mon Sep 17 00:00:00 2001 From: grilledham Date: Sat, 22 Jun 2019 22:36:04 +0100 Subject: [PATCH 1/5] priority_queue clean up --- utils/priority_queue.lua | 88 +++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/utils/priority_queue.lua b/utils/priority_queue.lua index bccd2358..85f07335 100644 --- a/utils/priority_queue.lua +++ b/utils/priority_queue.lua @@ -1,20 +1,45 @@ +local Debug = require 'utils.debug' +local is_closure = Debug.is_closure +local floor = math.floor + local PriorityQueue = {} -function PriorityQueue.new() - return {} -end - -local function default_comp(a, b) +local function default_comparator(a, b) return a < b end -local function HeapifyFromEndToStart(queue, comp) - comp = comp or default_comp - local pos = #queue +--- Min heap implementation of a priority queue. Smaller elements, as determined by the comparator, +-- have a higher priority. +-- @param comparator the comparator function used to compare elements +-- @usage +-- local PriorityQueue = require 'utils.priority_queue' +-- +-- local queue = PriorityQueue.new() +-- PriorityQueue.push(queue, 4) +-- PriorityQueue.push(queue, 7) +-- PriorityQueue.push(queue, 2) +-- +-- game.print(PriorityQueue.pop(queue)) -- 2 +-- game.print(PriorityQueue.pop(queue)) -- 4 +-- game.print(PriorityQueue.pop(queue)) -- 7 +function PriorityQueue.new(comparator) + if comparator == nil then + comparator = default_comparator + elseif is_closure(comparator) then + error('comparator cannot be a closure.', 2) + end + + return {_comparator = comparator} +end + +local function heapify_from_end_to_start(self) + local comparator = self._comparator + local pos = #self while pos > 1 do - local parent = bit32.rshift(pos, 1) -- integer division by 2 - if comp(queue[pos], queue[parent]) then - queue[pos], queue[parent] = queue[parent], queue[pos] + local parent = floor(pos / 2) + local a, b = self[pos], self[parent] + if comparator(a, b) then + self[pos], self[parent] = b, a pos = parent else break @@ -22,25 +47,26 @@ local function HeapifyFromEndToStart(queue, comp) end end -local function HeapifyFromStartToEnd(queue, comp) - comp = comp or default_comp +local function heapify_from_start_to_end(self) + local comparator = self._comparator local parent = 1 local smallest = 1 + local count = #self while true do local child = parent * 2 - if child > #queue then + if child > count then break end - if comp(queue[child], queue[parent]) then + if comparator(self[child], self[parent]) then smallest = child end child = child + 1 - if child <= #queue and comp(queue[child], queue[smallest]) then + if child <= count and comparator(self[child], self[smallest]) then smallest = child end if parent ~= smallest then - queue[parent], queue[smallest] = queue[smallest], queue[parent] + self[parent], self[smallest] = self[smallest], self[parent] parent = smallest else break @@ -48,27 +74,31 @@ local function HeapifyFromStartToEnd(queue, comp) end end -function PriorityQueue.size(queue) - return #queue +--- Returns the number of the number of elements in the priority queue. +function PriorityQueue.size(self) + return #self end -function PriorityQueue.push(queue, element, comp) - table.insert(queue, element) - HeapifyFromEndToStart(queue, comp) +-- Inserts an element into the priority queue. +function PriorityQueue.push(self, element) + self[#self + 1] = element + heapify_from_end_to_start(self) end -function PriorityQueue.pop(queue, comp) - local element = queue[1] +-- Removes and returns the highest priority element from the priority queue. +function PriorityQueue.pop(self) + local element = self[1] - queue[1] = queue[#queue] - queue[#queue] = nil - HeapifyFromStartToEnd(queue, comp) + self[1] = self[#self] + self[#self] = nil + heapify_from_start_to_end(self) return element end -function PriorityQueue.peek(queue) - return queue[1] +-- Returns, without removing, the highest priority element from the priority queue. +function PriorityQueue.peek(self) + return self[1] end return PriorityQueue From b426de3ec17b0da05c24b2331b69f9abf404c04c Mon Sep 17 00:00:00 2001 From: grilledham Date: Sat, 22 Jun 2019 22:36:13 +0100 Subject: [PATCH 2/5] task clean up --- utils/task.lua | 93 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/utils/task.lua b/utils/task.lua index a1d30ab9..1faaa835 100644 --- a/utils/task.lua +++ b/utils/task.lua @@ -9,38 +9,63 @@ local PriorityQueue = require 'utils.priority_queue' local Event = require 'utils.event' local Token = require 'utils.token' local ErrorLogging = require 'utils.error_logging' +local Global = require 'utils.global' + +local floor = math.floor +local log10 = math.log10 +local Token_get = Token.get +local pcall = pcall +local Queue_peek = Queue.peek +local Queue_pop = Queue.pop +local Queue_push = Queue.push +local PriorityQueue_peek = PriorityQueue.peek +local PriorityQueue_pop = PriorityQueue.pop +local PriorityQueue_push = PriorityQueue.push local Task = {} -global.callbacks = global.callbacks or PriorityQueue.new() -global.next_async_callback_time = -1 -global.task_queue = global.task_queue or Queue.new() -global.total_task_weight = 0 -global.task_queue_speed = 1 - -local function comp(a, b) +local function comparator(a, b) return a.time < b.time end -global.tpt = global.task_queue_speed +local callbacks = PriorityQueue.new(comparator) +local task_queue = Queue.new() +local primitives = { + next_async_callback_time = -1, + total_task_weight = 0, + task_queue_speed = 1, + task_per_tick = 1 +} + +Global.register( + {callbacks = callbacks, task_queue = task_queue, primitives = primitives}, + function(tbl) + callbacks = tbl.callbacks + task_queue = tbl.task_queue + primitives = tbl.primitives + end +) + local function get_task_per_tick() if game.tick % 300 == 0 then - local size = global.total_task_weight - global.tpt = math.floor(math.log10(size + 1)) * global.task_queue_speed - if global.tpt < 1 then - global.tpt = 1 + local size = primitives.total_task_weight + local task_per_tick = floor(log10(size + 1)) * primitives.task_queue_speed + if task_per_tick < 1 then + task_per_tick = 1 end + + primitives.task_per_tick = task_per_tick + return task_per_tick end - return global.tpt + return primitives.task_per_tick end local function on_tick() - local queue = global.task_queue for i = 1, get_task_per_tick() do - local task = Queue.peek(queue) + local task = Queue_peek(task_queue) if task ~= nil then -- result is error if not success else result is a boolean for if the task should stay in the queue. - local success, result = pcall(Token.get(task.func_token), task.params) + local success, result = pcall(Token_get(task.func_token), task.params) if not success then if _DEBUG then error(result) @@ -48,19 +73,18 @@ local function on_tick() log(result) ErrorLogging.generate_error_report(result) end - Queue.pop(queue) - global.total_task_weight = global.total_task_weight - task.weight + Queue_pop(task_queue) + primitives.total_task_weight = primitives.total_task_weight - task.weight elseif not result then - Queue.pop(queue) - global.total_task_weight = global.total_task_weight - task.weight + Queue_pop(task_queue) + primitives.total_task_weight = primitives.total_task_weight - task.weight end end end - local callbacks = global.callbacks - local callback = PriorityQueue.peek(callbacks) + local callback = PriorityQueue_peek(callbacks) while callback ~= nil and game.tick >= callback.time do - local success, result = pcall(Token.get(callback.func_token), callback.params) + local success, result = pcall(Token_get(callback.func_token), callback.params) if not success then if _DEBUG then error(result) @@ -69,8 +93,8 @@ local function on_tick() ErrorLogging.generate_error_report(result) end end - PriorityQueue.pop(callbacks, comp) - callback = PriorityQueue.peek(callbacks) + PriorityQueue_pop(callbacks) + callback = PriorityQueue_peek(callbacks) end end @@ -85,7 +109,7 @@ function Task.set_timeout_in_ticks(ticks, func_token, params) end local time = game.tick + ticks local callback = {time = time, func_token = func_token, params = params} - PriorityQueue.push(global.callbacks, callback, comp) + PriorityQueue_push(callbacks, callback) end --- Allows you to set a timer (in seconds) after which the tokened function will be run with params given as an argument @@ -109,8 +133,21 @@ end -- Ex. if the task is expected to repeat multiple times (ie. the function returns true and loops several ticks) function Task.queue_task(func_token, params, weight) weight = weight or 1 - global.total_task_weight = global.total_task_weight + weight - Queue.push(global.task_queue, {func_token = func_token, params = params, weight = weight}) + primitives.total_task_weight = primitives.total_task_weight + weight + Queue_push(task_queue, {func_token = func_token, params = params, weight = weight}) +end + +function Task.get_queue_speed() + return primitives.task_queue_speed +end + +function Task.set_queue_speed(value) + value = value or 1 + if value < 0 then + value = 0 + end + + primitives.task_queue_speed = value end Event.add(defines.events.on_tick, on_tick) From 3c5380dd32345d3ed3ec87ea5995d155386adec3 Mon Sep 17 00:00:00 2001 From: grilledham Date: Sat, 22 Jun 2019 22:42:27 +0100 Subject: [PATCH 3/5] made tick local --- utils/task.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/utils/task.lua b/utils/task.lua index 1faaa835..ded5cd66 100644 --- a/utils/task.lua +++ b/utils/task.lua @@ -46,8 +46,8 @@ Global.register( end ) -local function get_task_per_tick() - if game.tick % 300 == 0 then +local function get_task_per_tick(tick) + if tick % 300 == 0 then local size = primitives.total_task_weight local task_per_tick = floor(log10(size + 1)) * primitives.task_queue_speed if task_per_tick < 1 then @@ -61,7 +61,9 @@ local function get_task_per_tick() end local function on_tick() - for i = 1, get_task_per_tick() do + local tick = game.tick + + for i = 1, get_task_per_tick(tick) do local task = Queue_peek(task_queue) if task ~= nil then -- result is error if not success else result is a boolean for if the task should stay in the queue. @@ -83,7 +85,7 @@ local function on_tick() end local callback = PriorityQueue_peek(callbacks) - while callback ~= nil and game.tick >= callback.time do + while callback ~= nil and tick >= callback.time do local success, result = pcall(Token_get(callback.func_token), callback.params) if not success then if _DEBUG then From 73c1515c8107ef8741836029c4d2e8fb810a31bb Mon Sep 17 00:00:00 2001 From: grilledham Date: Sat, 22 Jun 2019 22:53:58 +0100 Subject: [PATCH 4/5] change divide to multiply --- utils/priority_queue.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/priority_queue.lua b/utils/priority_queue.lua index 85f07335..05904997 100644 --- a/utils/priority_queue.lua +++ b/utils/priority_queue.lua @@ -36,7 +36,7 @@ local function heapify_from_end_to_start(self) local comparator = self._comparator local pos = #self while pos > 1 do - local parent = floor(pos / 2) + local parent = floor(pos * 0.5) local a, b = self[pos], self[parent] if comparator(a, b) then self[pos], self[parent] = b, a From 7a9c9f817089a7ecf00b66471e4b6db378b46c21 Mon Sep 17 00:00:00 2001 From: grilledham Date: Sun, 23 Jun 2019 10:07:17 +0100 Subject: [PATCH 5/5] improved priority queue docs --- utils/priority_queue.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/priority_queue.lua b/utils/priority_queue.lua index 05904997..18ab9e8b 100644 --- a/utils/priority_queue.lua +++ b/utils/priority_queue.lua @@ -10,7 +10,8 @@ end --- Min heap implementation of a priority queue. Smaller elements, as determined by the comparator, -- have a higher priority. --- @param comparator the comparator function used to compare elements +-- @param comparator the comparator function used to compare elements, if nil the +-- deafult comparator is used. -- @usage -- local PriorityQueue = require 'utils.priority_queue' -- @@ -86,6 +87,7 @@ function PriorityQueue.push(self, element) end -- Removes and returns the highest priority element from the priority queue. +-- If the priority queue is empty returns nil. function PriorityQueue.pop(self) local element = self[1] @@ -97,6 +99,7 @@ function PriorityQueue.pop(self) end -- Returns, without removing, the highest priority element from the priority queue. +-- If the priority queue is empty returns nil. function PriorityQueue.peek(self) return self[1] end