Skip to content
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

Optimization: Use constant folding for divisions not a power of two #9609

Merged
merged 5 commits into from
Oct 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/apps/filemanager/filemanagermenu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -889,10 +889,10 @@ function FileManagerMenu:_getTabIndexFromLocation(ges)
if not ges then
return last_tab_index
-- if the start position is far right
elseif ges.pos.x > 2 * Screen:getWidth() / 3 then
elseif ges.pos.x > Screen:getWidth() * (2/3) then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bigger question's what they are on ARM than on x86 though. (Not an objection. :-) )

Copy link
Member

@NiLuJe NiLuJe Oct 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends on the CPU (some... don't even have native euclidean div hardware ;)).

But generally the same principles apply (i.e., it's a few clock cycles slower).

Which makes getting these out of the ARM technical manuals kind of a bitch, because it's optional on the A7 & A8 (IIRC, it's always supported on Kindle & Kobo, though).

But, if we look at a slightly more modern processor where it's stock: https://hardwarebug.org/2014/05/15/cortex-a7-instruction-cycle-timings/

(So, yeah, much slower ;)).

Copy link
Member

@NiLuJe NiLuJe Oct 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In compiled languages, it'd be the compiler's job to transform as many divisions as possibly in MULs or shifts, but I'm not quite sure what happens in Lua's case ;o).

Copy link
Member

@Frenzie Frenzie Oct 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LuaJIT tends to be pretty good about these things but you'd have to check. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made a small benchmark

x = 0
for i = 1,500000 do
    for j = 1,1000 do
        x = x + (j + i) * (1/3)
    end
end

print(x)

and

x = 0
for i = 1,500000 do
    for j = 1,1000 do
        x = x + (j + i) / 3
    end
end

pint(x)

Run both with time luajit bench.lua.
On a Sage the first one takes 5.4s the second one 12.4s.
On my Laptop the first on needs 0.5s the second one 2.1s.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LuaJIT tends to be pretty good about these things but you'd have to check. :-)

In https://luapower.com/luajit-notes#luajit-assumptions there is

divisions are 4x slower than multiplications on x86, so when dividing by a constant, it helps turning x / c into x * (1 / c) since the constant expression is folded – LuaJIT does this already for power-of-2 constants where the semantics are equivalent.

Copy link
Member

@NiLuJe NiLuJe Oct 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context re: my link above, the Sage is running on a quad-A7 @ 1.8GHz, FWIW (although we mostly only keep a single core online).

Our other devices tend to run on a A8 or an A9 at 1GHz.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks.

return BD.mirroredUILayout() and 1 or #self.tab_item_table
-- if the start position is far left
elseif ges.pos.x < Screen:getWidth() / 3 then
elseif ges.pos.x < Screen:getWidth() * (1/3) then
return BD.mirroredUILayout() and #self.tab_item_table or 1
-- if center return the last index
else
Expand Down
2 changes: 1 addition & 1 deletion frontend/apps/reader/modules/readerdevicestatus.lua
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function ReaderDeviceStatus:init()
if statm then
local dummy, rss = statm:read("*number", "*number")
statm:close()
rss = math.floor(rss * 4096 / 1024 / 1024)
rss = math.floor(rss * (4096 / 1024 / 1024))
if rss >= self.memory_threshold then
if self.memory_confirm_box then
UIManager:close(self.memory_confirm_box)
Expand Down
4 changes: 2 additions & 2 deletions frontend/apps/reader/modules/readerdogear.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ function ReaderDogear:init()
-- to not overwrite the book text.
-- For other documents, there is no easy way to know if valuable content
-- may be hidden by the icon (kopt's page_margin is quite obscure).
self.dogear_min_size = math.ceil(math.min(Screen:getWidth(), Screen:getHeight()) / 40)
self.dogear_max_size = math.ceil(math.min(Screen:getWidth(), Screen:getHeight()) / 32)
self.dogear_min_size = math.ceil(math.min(Screen:getWidth(), Screen:getHeight()) * (1/40))
self.dogear_max_size = math.ceil(math.min(Screen:getWidth(), Screen:getHeight()) * (1/32))
self.dogear_size = nil
self.dogear_y_offset = 0
self.top_pad = nil
Expand Down
8 changes: 4 additions & 4 deletions frontend/apps/reader/modules/readerfooter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ local footerTextGeneratorMap = {
local dummy, rss = statm:read("*number", "*number")
statm:close()
-- we got the nb of 4Kb-pages used, that we convert to MiB
rss = math.floor(rss * 4096 / 1024 / 1024)
rss = math.floor(rss * (4096 / 1024 / 1024))
return (prefix .. " %d"):format(rss)
end
return ""
Expand Down Expand Up @@ -390,7 +390,7 @@ local footerTextGeneratorMap = {
local title = doc_info.title:gsub(" ", "\xC2\xA0") -- replace space with no-break-space
local title_widget = TextWidget:new{
text = title,
max_width = footer._saved_screen_width * footer.settings.book_title_max_width_pct / 100,
max_width = footer._saved_screen_width * footer.settings.book_title_max_width_pct * (1/100),
face = Font:getFace(footer.text_font_face, footer.settings.text_font_size),
bold = footer.settings.text_font_bold,
}
Expand All @@ -410,7 +410,7 @@ local footerTextGeneratorMap = {
chapter_title = chapter_title:gsub(" ", "\xC2\xA0") -- replace space with no-break-space
local chapter_widget = TextWidget:new{
text = chapter_title,
max_width = footer._saved_screen_width * footer.settings.book_chapter_max_width_pct / 100,
max_width = footer._saved_screen_width * footer.settings.book_chapter_max_width_pct * (1/100),
face = Font:getFace(footer.text_font_face, footer.settings.text_font_size),
bold = footer.settings.text_font_bold,
}
Expand Down Expand Up @@ -2168,7 +2168,7 @@ function ReaderFooter:_updateFooterText(force_repaint, force_recompute)
self.footer_text.height = 0
else
-- Alongside a progress bar, it's the bar's width plus whatever's left.
local text_max_available_ratio = (100 - self.settings.progress_bar_min_width_pct) / 100
local text_max_available_ratio = (100 - self.settings.progress_bar_min_width_pct) * (1/100)
self.footer_text:setMaxWidth(math.floor(text_max_available_ratio * self._saved_screen_width - 2 * self.settings.progress_margin_width - self.horizontal_margin))
-- Add some spacing between the text and the bar
self.text_width = self.footer_text:getSize().w + self.horizontal_margin
Expand Down
4 changes: 2 additions & 2 deletions frontend/apps/reader/modules/readerhighlight.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2022,8 +2022,8 @@ end
function ReaderHighlight:onMoveHighlightIndicator(args)
if self.view.visible_area and self._current_indicator_pos then
local dx, dy, quick_move = unpack(args)
local quick_move_distance_dx = self.view.visible_area.w / 5 -- quick move distance: fifth of visible_area
local quick_move_distance_dy = self.view.visible_area.h / 5
local quick_move_distance_dx = self.view.visible_area.w * (1/5) -- quick move distance: fifth of visible_area
local quick_move_distance_dy = self.view.visible_area.h * (1/5)
-- single move distance, small and capable to move on word with small font size and narrow line height
local move_distance = Size.item.height_default / 4
local rect = self._current_indicator_pos:copy()
Expand Down
4 changes: 2 additions & 2 deletions frontend/apps/reader/modules/readermenu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,10 @@ function ReaderMenu:_getTabIndexFromLocation(ges)
if not ges then
return self.last_tab_index
-- if the start position is far right
elseif ges.pos.x > 2 * Screen:getWidth() / 3 then
elseif ges.pos.x > Screen:getWidth() * (2/3) then
return BD.mirroredUILayout() and 1 or #self.tab_item_table
-- if the start position is far left
elseif ges.pos.x < Screen:getWidth() / 3 then
elseif ges.pos.x < Screen:getWidth() * (1/3) then
return BD.mirroredUILayout() and #self.tab_item_table or 1
-- if center return the last index
else
Expand Down
6 changes: 3 additions & 3 deletions frontend/apps/reader/modules/readerpaging.lua
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ end

function ReaderPaging:onGotoPercent(percent)
logger.dbg("goto document offset in percent:", percent)
local dest = math.floor(self.number_of_pages * percent / 100)
local dest = math.floor(self.number_of_pages * percent * (1/100))
if dest < 1 then dest = 1 end
if dest > self.number_of_pages then
dest = self.number_of_pages
Expand Down Expand Up @@ -908,8 +908,8 @@ function ReaderPaging:onGotoPageRel(diff)
local x_pan_off, y_pan_off = 0, 0
local right_to_left = self.ui.document.configurable.writing_direction and self.ui.document.configurable.writing_direction > 0
local bottom_to_top = self.ui.zooming.zoom_bottom_to_top
local h_progress = 1 - self.ui.zooming.zoom_overlap_h / 100
local v_progress = 1 - self.ui.zooming.zoom_overlap_v / 100
local h_progress = 1 - self.ui.zooming.zoom_overlap_h * (1/100)
local v_progress = 1 - self.ui.zooming.zoom_overlap_v * (1/100)
local old_va = self.visible_area
local old_page = self.current_page
local x, y, w, h = "x", "y", "w", "h"
Expand Down
4 changes: 2 additions & 2 deletions frontend/apps/reader/modules/readerpanning.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ function ReaderPanning:onPanning(args, _)
local dx, dy = unpack(args)
-- for now, bounds checking/calculation is done in the view
self.view:PanningUpdate(
dx * self.panning_steps.normal * self.dimen.w / 100,
dy * self.panning_steps.normal * self.dimen.h / 100)
dx * self.panning_steps.normal * self.dimen.w * (1/100),
dy * self.panning_steps.normal * self.dimen.h * (1/100))
return true
end

Expand Down
18 changes: 9 additions & 9 deletions frontend/apps/reader/modules/readerrolling.lua
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ function ReaderRolling:onGotoXPointer(xp, marker_xp)
-- where xpointer target is (and remove if after 1s)
local screen_y, screen_x = self.ui.document:getScreenPositionFromXPointer(marker_xp)
local doc_margins = self.ui.document:getPageMargins()
local marker_h = Screen:scaleBySize(self.ui.font.font_size * 1.1 * self.ui.font.line_space_percent/100.0)
local marker_h = Screen:scaleBySize(self.ui.font.font_size * 1.1 * self.ui.font.line_space_percent * (1/100))
-- Make it 4/5 of left margin wide (and bigger when huge margin)
local marker_w = math.floor(math.max(doc_margins["left"] - Screen:scaleBySize(5), doc_margins["left"] * 4/5))

Expand Down Expand Up @@ -794,7 +794,7 @@ function ReaderRolling:onGotoViewRel(diff)
local pan_diff = diff * page_visible_height
if self.view.page_overlap_enable then
local overlap_lines = G_reader_settings:readSetting("copt_overlap_lines") or 1
local overlap_h = Screen:scaleBySize(self.ui.font.font_size * 1.1 * self.ui.font.line_space_percent/100.0) * overlap_lines
local overlap_h = Screen:scaleBySize(self.ui.font.font_size * 1.1 * self.ui.font.line_space_percent * (1/100)) * overlap_lines
if pan_diff > overlap_h then
pan_diff = pan_diff - overlap_h
elseif pan_diff < -overlap_h then
Expand Down Expand Up @@ -1023,9 +1023,9 @@ end

function ReaderRolling:_gotoPercent(new_percent)
if self.view.view_mode == "page" then
self:_gotoPage(new_percent * self.ui.document:getPageCount() / 100)
self:_gotoPage(new_percent * self.ui.document:getPageCount() * (1/100))
else
self:_gotoPos(new_percent * self.ui.document.info.doc_height / 100)
self:_gotoPos(new_percent * self.ui.document.info.doc_height * (1/100))
end
end

Expand Down Expand Up @@ -1151,24 +1151,24 @@ function ReaderRolling:handleEngineCallback(ev, ...)
self:showEngineProgress(0) -- Start initial delay countdown
elseif ev == "OnLoadFileProgress" then
-- Initial load from file (step 1/2) or from cache (step 1/1)
self:showEngineProgress(args[1]/100/2)
self:showEngineProgress(args[1]*(1/100/2))
elseif ev == "OnNodeStylesUpdateStart" then -- Start of re-rendering
self:showEngineProgress(0) -- Start initial delay countdown
elseif ev == "OnNodeStylesUpdateProgress" then
-- Update node styles (step 1/2 on re-rendering)
self:showEngineProgress(args[1]/100/2)
self:showEngineProgress(args[1]*(1/100/2))
elseif ev == "OnFormatStart" then -- Start of step 2/2
self:showEngineProgress(1/2) -- 50%, in case of no OnFormatProgress
elseif ev == "OnFormatProgress" then
-- Paragraph formatting and page splitting (step 2/2 after load
-- from file, step 2/2 on re-rendering)
self:showEngineProgress(1/2 + args[1]/100/2)
self:showEngineProgress(1/2 + args[1]*(1/100/2))
elseif ev == "OnSaveCacheFileStart" then -- Start of cache file save
self:showEngineProgress(1) -- Start initial delay countdown, fully filled
elseif ev == "OnSaveCacheFileProgress" then
-- Cache file save (when closing book after initial load from
-- file or re-rendering)
self:showEngineProgress(1 - args[1]/100) -- unfill progress
self:showEngineProgress(1 - args[1]*(1/100)) -- unfill progress
elseif ev == "OnDocumentReady" or ev == "OnSaveCacheFileEnd" then
self:showEngineProgress() -- cleanup
elseif ev == "OnLoadFileError" then
Expand Down Expand Up @@ -1214,7 +1214,7 @@ function ReaderRolling:showEngineProgress(percent)
y = y + self.current_header_height
end

local w = math.floor(Screen:getWidth() / 3)
local w = math.floor(Screen:getWidth() * (1/3))
local h = Size.line.progress
if self.engine_progress_widget then
self.engine_progress_widget:setPercentage(percent)
Expand Down
2 changes: 1 addition & 1 deletion frontend/apps/reader/modules/readerscrolling.lua
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ function ReaderScrolling:_setupAction()
local dist = math.floor(self._velocity * self._inertial_scroll_interval)
if math.abs(dist) < self.end_scroll_dist then
-- Decrease it even more so scrolling stops sooner
self._velocity = self._velocity / 1.5
self._velocity = self._velocity * (2/3)
end
-- self._stats_scroll_iterations = self._stats_scroll_iterations + 1
-- self._stats_scroll_distance = self._stats_scroll_distance + dist
Expand Down
2 changes: 1 addition & 1 deletion frontend/apps/reader/modules/readerthumbnail.lua
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ function ReaderThumbnail:setupCache()
local max_bytes = math.ceil(N * Screen:getWidth() * Screen:getHeight() * Blitbuffer.TYPE_TO_BPP[self.bb_type] / 8)
-- We don't really care about limiting any number of slots, so allow
-- for at least 5 pages of 10x10 tiles
local avg_itemsize = math.ceil(max_bytes / 500)
local avg_itemsize = math.ceil(max_bytes * (1/500))
self.tile_cache = Cache:new{
size = max_bytes,
avg_itemsize = avg_itemsize, -- will make slots=500
Expand Down
6 changes: 3 additions & 3 deletions frontend/apps/reader/modules/readerview.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ local ReaderView = OverlapGroup:extend{

-- single page state
state = nil, -- table
outer_page_color = Blitbuffer.gray(G_defaults:readSetting("DOUTER_PAGE_COLOR") / 15),
outer_page_color = Blitbuffer.gray(G_defaults:readSetting("DOUTER_PAGE_COLOR") * (1/15)),
-- highlight with "lighten" or "underscore" or "strikeout" or "invert"
highlight = nil, -- table
highlight_visible = true,
Expand All @@ -42,7 +42,7 @@ local ReaderView = OverlapGroup:extend{
note_mark_pos_x2 = nil, -- page 2 in two-page mode
-- PDF/DjVu continuous paging
page_scroll = nil,
page_bgcolor = Blitbuffer.gray(G_defaults:readSetting("DBACKGROUND_COLOR") / 15),
page_bgcolor = Blitbuffer.gray(G_defaults:readSetting("DBACKGROUND_COLOR") * (1/15)),
page_states = nil, -- table
-- properties of the gap drawn between each page in scroll mode:
page_gap = nil, -- table
Expand Down Expand Up @@ -95,7 +95,7 @@ function ReaderView:init()
self.page_states = {}
self.page_gap = {
-- color (0 = white, 8 = gray, 15 = black)
color = Blitbuffer.gray((G_reader_settings:readSetting("page_gap_color") or 8) / 15),
color = Blitbuffer.gray((G_reader_settings:readSetting("page_gap_color") or 8) * (1/15)),
}
self.visible_area = Geom:new{x = 0, y = 0, w = 0, h = 0}
self.page_area = Geom:new{x = 0, y = 0, w = 0, h = 0}
Expand Down
2 changes: 1 addition & 1 deletion frontend/device/kindle/device.lua
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ function Kindle:outofScreenSaver()
-- The banner on a 1236x1648 PW5 is 1235x125; we refresh the bottom 10% of the screen to be safe.
local Geom = require("ui/geometry")
local screen_height = self.screen:getHeight()
local refresh_height = math.ceil(screen_height / 10)
local refresh_height = math.ceil(screen_height * (1/10))
local refresh_region = Geom:new{
x = 0,
y = screen_height - 1 - refresh_height,
Expand Down
6 changes: 3 additions & 3 deletions frontend/device/kobo/powerd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function KoboPowerD:_syncKoboLightOnStart()
-- ColorSetting is stored as a color temperature scale in Kelvin,
-- from 1500 to 6400
-- so normalize this to [0, 100] on our end.
new_warmth = (100 - Math.round((new_color - 1500) / 49))
new_warmth = (100 - Math.round((new_color - 1500) * (1/49)))
end
end
if is_frontlight_on == nil then
Expand Down Expand Up @@ -303,7 +303,7 @@ function KoboPowerD:turnOffFrontlightHW()
end
ffiUtil.runInSubProcess(function()
for i = 1,5 do
self:setIntensityHW(math.floor(self.fl_intensity - ((self.fl_intensity / 5) * i)))
self:setIntensityHW(math.floor(self.fl_intensity - ((self.fl_intensity * (1/5)) * i)))
--- @note: Newer devices appear to block slightly longer on FL ioctls/sysfs, so only sleep on older devices,
--- otherwise we get a jump and not a ramp ;).
if not self.device:hasNaturalLight() then
Expand Down Expand Up @@ -336,7 +336,7 @@ function KoboPowerD:turnOnFrontlightHW()
end
ffiUtil.runInSubProcess(function()
for i = 1,5 do
self:setIntensityHW(math.ceil(self.fl_min + ((self.fl_intensity / 5) * i)))
self:setIntensityHW(math.ceil(self.fl_min + ((self.fl_intensity * (1/5)) * i)))
--- @note: Newer devices appear to block slightly longer on FL ioctls/sysfs, so only sleep on older devices,
--- otherwise we get a jump and not a ramp ;).
if not self.device:hasNaturalLight() then
Expand Down
2 changes: 1 addition & 1 deletion frontend/ui/data/optionsutil.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ local function convertSizeTo(px, format)
local format_factor = 1 -- we are defaulting on mm

if format == "pt" then
format_factor = format_factor * 2660 / 1000 -- see https://www.wikiwand.com/en/Metric_typographic_units
format_factor = format_factor * (2660 / 1000) -- see https://www.wikiwand.com/en/Metric_typographic_units
elseif format == "in" then
format_factor = 1 / 25.4
end
Expand Down
2 changes: 1 addition & 1 deletion frontend/ui/elements/page_turns.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ table.insert(page_turns_tap_zones_sub_items, {
value_max = 100,
default_value = math.floor(G_defaults:readSetting("DTAP_ZONE_FORWARD").w * 100),
callback = function(spin)
G_reader_settings:saveSetting("page_turns_tap_zone_forward_size_ratio", spin.value / 100)
G_reader_settings:saveSetting("page_turns_tap_zone_forward_size_ratio", spin.value * (1/100))
ReaderUI.instance.view:setupTouchZones()
if touchmenu_instance then touchmenu_instance:updateItems() end
end,
Expand Down
4 changes: 2 additions & 2 deletions frontend/ui/elements/timeout_android.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ local timeout_custom6 = 25 * 60 * 1000
local timeout_custom7 = 30 * 60 * 1000

local function humanReadableTimeout(timeout)
local sec = timeout / 1000
local sec = timeout * (1/1000)
if sec >= 60 then
return T(N_("1 minute", "%1 minutes", sec), sec / 60)
return T(N_("1 minute", "%1 minutes", sec), sec * (1/60))
else
return T(N_("1 second", "%1 seconds", sec), sec)
end
Expand Down
4 changes: 2 additions & 2 deletions frontend/ui/otamanager.lua
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,10 @@ function OTAManager:_buildLocalPackage()
-- Defaults to a sane-ish value as-of now, in case shit happens...
local blocks = 6405
if tarball_size then
blocks = tarball_size / (512 * 20)
blocks = tarball_size * (1/(512 * 20))
end
-- And since we want a percentage, devise the exact value we need for tar to spit out exactly 100 checkpoints ;).
local cpoints = blocks / 100
local cpoints = blocks * (1/100)
return os.execute(string.format(
"./tar --no-recursion -cf %s -C .. -T %s --checkpoint=%d --checkpoint-action=exec='./fbink -q -y -6 -P $(($TAR_CHECKPOINT/%d))'",
self.installed_package, self.package_indexfile, cpoints, cpoints))
Expand Down
2 changes: 1 addition & 1 deletion frontend/ui/time.lua
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ end
--- Converts an fts time to a Lua (decimal) number (sec.usecs) (accurate to the ms, rounded to 4 decimal places)
function time.to_number(time_fts)
-- Round to 4 decimal places
return math.floor(time.to_s(time_fts) * 10000 + 0.5) / 10000
return math.floor(time.to_s(time_fts) * 10000 + 0.5) * (1/10000)
end

--- Converts an fts to a Lua (int) number (resolution: 1µs)
Expand Down
Loading