Skip to content

Conversation

@Vdauphin
Copy link
Contributor

@Vdauphin Vdauphin commented Apr 3, 2020

For CBA_missionTime above to 6e5s (7 days):
Addition with CBA_missionTime has some floating point precision error appearing randomly. This can be see thanks to this code mimicking the actual CBA behaviour:

Vdofin_somme = 0;
Vdofin_start = 6e5;

Vdofin_missionTime = Vdofin_start;
Vdofin_lastTickTime = diag_tickTime;
v = addMissionEventHandler ["EachFrame", {
    private _tickTime = diag_tickTime;
    private _additionCheckPrecision = _tickTime - Vdofin_lastTickTime;

    Vdofin_somme = Vdofin_somme + _additionCheckPrecision;

    Vdofin_missionTime = Vdofin_missionTime + _additionCheckPrecision;
    Vdofin_lastTickTime = _tickTime;
}];

Check in debug console the leak happening after a few second:
Vdofin_somme + Vdofin_start - Vdofin_missionTime

This leak is due to the problem that _tickTime - GVAR(lastTickTime) is around 0.01 to 0.1 which is between 1e6 to 1e7 power of magnitude smaller than CBA_missionTime. So when _tickTime - GVAR(lastTickTime) is added to 6e5 the result is the same as before the addition.
The idea is to wait until _tickTime - GVAR(lastTickTime) is higher. To do so, first we need to check if a floating point error occur and don't update CBA_missionTime AND GVAR(lastTickTime). So the _tickTime - GVAR(lastTickTime) will get higher and higher until the floating point error do not occur any more.

For the previous code this mean this:

Vdofin_somme = 0;
Vdofin_start = 6e5;

Vdofin_lastTickTime = diag_tickTime;
Vdofin_missionTime = Vdofin_start;
a = addMissionEventHandler ["EachFrame", {
    private _tickTime = diag_tickTime;
    private _additionCheckPrecision = _tickTime - Vdofin_lastTickTime;
    Vdofin_somme = Vdofin_somme + _additionCheckPrecision;

    private _storeAddition = Vdofin_missionTime + _additionCheckPrecision;
    if (Vdofin_missionTime == _storeAddition) exitWith{};
    Vdofin_missionTime = _storeAddition;
    Vdofin_lastTickTime = _tickTime;
}];

For CBA, see commits.

This allow dedicated server with CBA work longer than 5e5s and lower than 1e6s without significant time leak.

When merged this pull request will:

  • This PR avoid time leak when CBA_missionTime above to 6e5s (7 days) and lower than 1e6s (11.5 days).
  • Each time the CBA_missionTime is updated
    • Now checking if the addition : CBA_missionTime + (_tickTime - GVAR(lastTickTime)) is equal to than CBA_missionTime
    • If is, this mean there is a floating point precision error
      • Skip the addition and the update of GVAR(lastTickTime)
    • If not, update the CBA_missionTime and GVAR(lastTickTime)

Above to 6e5, some precision error appearing randomly.
This commit exit the addition of CBA_missionTime when a precision error occure and don't actualise the GVAR(lastTickTime) to not lost this time.
@commy2
Copy link
Contributor

commy2 commented Apr 4, 2020

What does _additionCheckPrecision mean?

I don't understand what you mean by "leak", but I haven't tried your debug code for now.

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 4, 2020

What does _additionCheckPrecision mean?

Don't know how to call it but in the PR it contain CBA_missionTime + (_tickTime - GVAR(lastTickTime)); and is use later to check if the addition has been done as intended.

I don't understand what you mean by "leak", but I haven't tried your debug code for now.

Basically 6e5 + 0.03 return 6e5 and not 600000.03, you repeat this addition 100 times (in game CBA do it every frame) you still have 6e5 but you should have 600003.00. So in this worst case you lost/leak 3 seconds.

@commy2
Copy link
Contributor

commy2 commented Apr 4, 2020

So instead of adding small deltas of time we want the delta to grow enough to make a difference?

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 4, 2020

Yes

@commy2
Copy link
Contributor

commy2 commented Apr 4, 2020

Hmm, that has its own imprecissions. I was thinking about incorporating
https://community.bistudio.com/wiki/diag_deltaTime
somehow.
And also storing the tickTime as string internally.

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 4, 2020

Hmm, that has its own imprecissions.

Agree, the imprecision with this implementation is less than a second which is enough for us I think.

I was thinking about incorporating
https://community.bistudio.com/wiki/diag_deltaTime
somehow.

I saw it but didn't find a clever use of it or we need to design this thing way differently
We need to avoid an addition between a big and a small number, then we are safe.

And also storing the tickTime as string internally.

I also think about it. Like you store "60000.00", then select the "00.00", add 00.00 + 0.03, and concatenate in "60000.03" but it is may be over engineering.

But computer scientist should already design something to deal with this, isn't it?

We could also store the big part of the number in a variable and the small part in an other:
[6e5, 0.03]. When the small part is "full", add it to the big part

@commy2
Copy link
Contributor

commy2 commented Apr 4, 2020

But computer scientist should already design something to deal with this, isn't it?

They'd just use doubles instead of single floats and be done with it.

We're stuck with singles. By using strings, I attempt to get the same benefit as doubles by changing data type.

Maybe we could also use some array [a,b], where b is the last 6 digits of the mission time and a is the number of millions.
[10,987654]
= 10,987,654

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 4, 2020

Maybe we could also use some array [a,b], where b is the last 6 digits of the mission time and a is the number of millions.
[10,987654]
= 10,987,654

I like this idea but the imprecision start at 1e5 because we are summing to it 1e-2 number

@commy2
Copy link
Contributor

commy2 commented Apr 4, 2020

[10,000,000]
then.

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 4, 2020

If we use 3D array, we can use vector operation.
1201.09 will be define as [0,1,201.09]

@commy2
Copy link
Contributor

commy2 commented Apr 4, 2020

lol sure

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 4, 2020

Here is an example of addition without imprecision until 1e9 (theoretical).

removeMissionEventHandler ["EachFrame", v];

Vdofin_somme = 0;
Vdofin_start = 6e5;

Vdofin_missionTime = [
    floor((Vdofin_start % 1e9)/1e6),
    floor((Vdofin_start % 1e6)/1e3),
    Vdofin_start % 1e3
];
Vdofin_lastTickTime = diag_tickTime;

v = addMissionEventHandler ["EachFrame", {
    private _tickTime = diag_tickTime;

    Vdofin_somme = Vdofin_somme + _tickTime - Vdofin_lastTickTime;

    Vdofin_missionTime = Vdofin_missionTime vectorAdd [0, 0, _tickTime - Vdofin_lastTickTime];
    if (selectMax Vdofin_missionTime > 1000) then {
        private _limitReach = Vdofin_missionTime findIf {_x > 1000};
        private _diff = [0, 0, 0];
        _diff set [_limitReach, -1000];
        _diff set [_limitReach - 1, 1];
        Vdofin_missionTime = Vdofin_missionTime vectorAdd _diff;
    };
    Vdofin_lastTickTime = _tickTime;
}];

Watch in debug, the different is null:
Vdofin_somme + Vdofin_start - (Vdofin_missionTime vectorDotProduct [1e6 , 1e3, 1])

The commit is a prof of concept. May be it is a too big hammer

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 5, 2020

So, if this concept is fine for the CBA Team (?)
I can provide backward compatibility by defining a CBA_missionTimeTriple and update CBA_missionTime with CBA_missionTimeTriple vectorDotProduct [1e6 , 1e3, 1]

Note:
Convert positive time (R+) from number to vector:

_timeVector = [
    floor((_time % 1e9)/1e6),
    floor((_time % 1e6)/1e3),
    time % 1e3
];

Convert vector time to positive or negative time (R):

_timeVector vectorDotProduct [1e6 , 1e3, 1]

Addition of two vector time:

_timeVector1 vectorAdd _timeVector2

Difference of two vector time:

_timeVector1 vectorDiff _timeVector2

Comparison of vector time, test if _timeVector1 > _timeVector2:

private _timeDiff = [_timeVector1 , _timeVector2];
_timeDiff sort false;
_timeDiff#0 isEqualTo _timeVector1;

@commy2
Copy link
Contributor

commy2 commented Apr 5, 2020

Turning CBA_missionTime into an array breaks bwc.

This implementation feels goofy. Will have to think about this some more.

@commy2 commy2 added the WIP label Apr 5, 2020
@commy2 commy2 modified the milestones: 3.15, 3.15.1 Apr 5, 2020
@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 5, 2020

Turning CBA_missionTime into an array breaks bwc.

I know, that why I said:

I can provide backward compatibility by defining a CBA_missionTimeTriple and update CBA_missionTime with CBA_missionTimeTriple vectorDotProduct [1e6 , 1e3, 1]

@commy2
Copy link
Contributor

commy2 commented Apr 5, 2020

I'm still in favour of using a string to store this non-single float.

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 5, 2020

With vector representation we have more engine method: #1320 (comment)

@commy2
Copy link
Contributor

commy2 commented Apr 5, 2020

You'd have to change the string covering the >1000 digits only once every 1000 seconds=16,67 minutes. Performance is irrelevant.

@Vdauphin
Copy link
Contributor Author

Vdauphin commented Apr 5, 2020

You'd have to change the string covering the >1000 digits only once every 1000 seconds=16,67 minutes. Performance is irrelevant.

I don't see a string approach without imprecision when doing addition, substation, comparison and sort will be useless

What is your idea?

@PabstMirror
Copy link
Contributor

see #1323

@PabstMirror PabstMirror modified the milestones: 3.15.1, Ongoing Apr 22, 2020
@Vdauphin
Copy link
Contributor Author

See #1324

@Vdauphin Vdauphin closed this Apr 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants