Multiple (but not too much) calculations per tick

Place to post guides, observations, things related to modding that are not mods themselves.
Post Reply
User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Multiple (but not too much) calculations per tick

Post by darkfrei »

Problem: I have a lot of functions, that must be spread by the several ticks. For example find a lot of inserters in the multiple surfaces world and replace them with another inserters with another prototype parameters.

I can actually do small searches, for example the surface.find_entities only few chunks per tick, save them into the global. When I done the first surface I can do the search on the next surface. Ok, all surfaces are done.

The next step is replacing one entities to another. I can take few inserters from the global and replace them. On the next tick I do the same with next few inserters. I can delete processed inserters from the global, but by the huge table it needs to much UPS. It looks like that the removing last elements from the table or rewriting with another needs mush less CPU time.

Is here the good way to support more than one calculations per tick, but not all of them? It could be good if the player can choose how many calculations per tick he needs.

Honktown
Smart Inserter
Smart Inserter
Posts: 1025
Joined: Thu Oct 03, 2019 7:10 am
Contact:

Re: Multiple (but not too much) calculations per tick

Post by Honktown »

Normally I've seen mods use a max updates per tick value (Better Bots Technologies replaces roboports).

To manage handling the inserters, chunks can be stored in a table of inserters[surface.name][chunk_x][chunk_y] = chunk. This would reduce searching tremendously. Also, the entire surface doesn't have to be searched at once: only chunks that exist or come into existence need to have their inserters replaced (only one scan of chunks needs to be done, and index new chunks as generated, and remove chunks as they're converted). You shouldn't be able to have a large table from this, as only what, 32x32 inserters can exist on a chunk at a time? This reduces finding and storing all inserters, to finding and converting chunks at a time.
I have mods! I guess!
Link

Honktown
Smart Inserter
Smart Inserter
Posts: 1025
Joined: Thu Oct 03, 2019 7:10 am
Contact:

Re: Multiple (but not too much) calculations per tick

Post by Honktown »

Also, storing chunks as [x][y] is waaaaaaaay more efficient than storing it as a string, unless a string pair() is done using per-character searching. I've seen people do stuff like x .. "x" .. y as the key for a table, and that doesn't make sense to me, compared to [x][y].

Edit: I thought chunks would've had a type in game like luasurface. [x][y] = true would have to be used instead, same difference
I have mods! I guess!
Link

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Multiple (but not too much) calculations per tick

Post by darkfrei »

In this mod Vertical Axis Wind Turbines i've done it like this:

Code: Select all

function on_tick ()
  -- multiple handlers per tick
  local handlers_per_tick = handlers_per_tick_global_setting or 1
  if not global.handlers then
    global.handlers = {}
    return -- nothing to do in this tick
  end
  local id = global.next_handler_id or 1
  local n = 0
  while n < handlers_per_tick do
    local handler = global.handlers[id+n]
    n = n + 1
    if handler then
      -- do the function
      -----
      local valid = main_handler (handler) -- returns false or nil if it was something wrong
      -----
      if not valid then
        local max_id = #global.handlers
        if id < max_id then
          global.handlers[id] = global.handlers[max_id]
          global.handlers[max_id] = nil
        else -- id => max_id; the last one
          global.handlers[id] = nil
        end
      end
    else -- no handler, earlier was the last one
      global.next_handler_id = 1
      return
    end
  end
  -- the next tick next handlers
  global.next_handler_id = id + n
end

robot256
Filter Inserter
Filter Inserter
Posts: 596
Joined: Sun Mar 17, 2019 1:52 am
Contact:

Re: Multiple (but not too much) calculations per tick

Post by robot256 »

I can delete processed inserters from the global, but by the huge table it needs to much UPS.
As darkfrei showed, the proper way to delete items from a table in Lua is with

Code: Select all

global.mytable[index]=nil
You have to write your loops to use

Code: Select all

for index,value in pairs(global.mytable)
or

Code: Select all

index,value = next(global.mytable)
instead of assuming it is an array with contiguous indexes, and reassigning indexes when you delete from the middle. Have you tried that already?

Another thing you can try, if you need to operate on the entire list over and over, is to add and remove entities to the list with events when they are created and destroyed.

Post Reply

Return to “Modding discussion”