Page MenuHomeWildfire Games

Seed random sounds
ClosedPublic

Authored by Stan on Jun 21 2018, 2:57 PM.

Details

Summary
Test Plan

See it compiles fine, doesn't trigger weird behaviours, and also that the sound produced by one unit is always the same.

Diff Detail

Repository
rP 0 A.D. Public Repository
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
vladislavbelov added inline comments.Apr 7 2019, 12:42 PM
source/soundmanager/scripting/SoundGroup.cpp
191 ↗(On Diff #7205)

What's about static?

Stan updated this revision to Diff 7693.Apr 7 2019, 12:59 PM
  • Make rng static to cut the compute time by half
  • Use a custom seed only when cmpVisual is set
Stan marked an inline comment as done.Apr 7 2019, 1:00 PM
Vulcan added a comment.Apr 7 2019, 1:02 PM

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1179/display/redirect

Stan marked 2 inline comments as done.Apr 7 2019, 5:37 PM
Stan updated this revision to Diff 7700.Apr 8 2019, 8:33 PM

Fix diff that should not have been able to build.

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1181/display/redirect

In D1584#74541, @Stan wrote:

It makes sense since we use boost::mt19937 everywhere. And at sometime we may replace it at once in all places. Could you compare? I.e. you can add your sample to our tests and you'll be able to compile with boost.

Stan added a comment.EditedApr 9 2019, 3:47 PM

Thanks for the tip, I was able to make a new test (I had some troubles with static variables XD)

Btw should I add the static variable to the class like the test or keep it global ?

https://pastebin.com/EgafKFjH

STL seems faster on my machine

RandFloat(float min, float max): 0.672794 seconds
RandFloatBoost(float min, float max): 26.7208 seconds
RandFloatStl(float min, float max): 22.1563 seconds

RandFloat(float min, float max): 0.792204 seconds
RandFloatBoost(float min, float max): 27.6625 seconds
RandFloatStl(float min, float max): 23.7849 seconds

Updated outside script https://pastebin.com/677SM52N with more profiling (now with clang-cl & clang++ https://chocolatey.org/packages/llvm on windows) (script: https://pastebin.com/54DBNbTL)

CompilerNo OptimizationO1O2O3OFast/Ox
cl.exe RandFloat(float min, float max)0.6678710.5506050.571285/0.478461
cl.exe RandFloatStl(float min, float max)102.488024.2374022.25870/22.49370
mingw32-g++.exe RandFloat(float min, float max)0.8396380.3425330.3896680.2834800.281247
mingw32-g++.exe RandFloatStl(float min, float max)152.870053.7082053.0650047.067447.91880
clang++.exe RandFloat(float min, float max)0.9706880.5556590.2863390.3402320.291435
clang++.exe RandFloatStl(float min, float max)48.3702023.8028023.2542023.2969021.67440
clang-cl.exe RandFloat(float min, float max)0.6351020.4384600.314780/0.268367
clang-cl.exe RandFloatStl(float min, float max)44.0480021.9462022.59220/22.25470

Warning by clang

file.cpp(61,21): warning: declaration requires a global constructor [-Wglobal-constructors]
static std::mt19937 m_StlRng;

mt19937 seems ok for me, but I'm worrying about performance. Could you test how many calls of the UploadPropertiesAndPlay function we have on the biggest map with biggest number of units per second?

This revision was automatically updated to reflect the committed changes.
Owners added a subscriber: Restricted Owners Package.Apr 9 2019, 11:50 PM
Stan updated this revision to Diff 7710.EditedApr 10 2019, 12:30 AM
Stan marked 2 inline comments as not done.

@vladislavbelov Three questions

How do you do get the frame reset ?

source/soundmanager/scripting/SoundGroup.cpp
40 ↗(On Diff #7710)

Should this be SoundGroup::g_Rng ?

43 ↗(On Diff #7710)

Should this be SoundGroup::RandFloat ?

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1185/display/redirect

Stan added a comment.Apr 10 2019, 12:09 PM

Here is a graph of a 4x4 match PopCap Unlimited, difficulty medium{F891459} Most calls 162
Diff to test

Replay

Stan updated this revision to Diff 7716.EditedApr 10 2019, 4:34 PM
  • Use srand(seed) instead. According to my tests it works way faster. (See below)
  • We don't care about network generating other numbers because sounds should be out of the simulation.
clang++ -02
base time: 0.000382867 seconds
srand time: 0.000917521 seconds
std::mt19973 time: 0.0250902 seconds
clang-cl -02
base time: 0.000463292 seconds
srand time: 0.000489345 seconds
std::mt19973 time: 0.0320702 seconds
msvc /02
base time: 0.000429687 seconds
srand time: 0.00112821 seconds
std::mt19973 time: 0.0217086 seconds
mingw -02
base time: 0 seconds
srand time: 0 seconds
std::mt19973 time: 0.051875 seconds
srand(3);
float value2 = RandFloat(min, max);
srand(3);
float value3 = RandFloat(min, max);
TS_ASSERT(value3 == value2); // True
srand(2533652);
float value4 = RandFloat(min, max);
srand(2533652); 
float value5 = RandFloat(min, max);
TS_ASSERT(value4 == value5); // True
TS_ASSERT(value3 != value5); //False

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1189/display/redirect

vladislavbelov added inline comments.Apr 10 2019, 8:56 PM
source/soundmanager/scripting/SoundGroup.cpp
187 ↗(On Diff #7716)

It's not good to use srand here, because there are other clients of rand().

Itms reopened this revision.Apr 10 2019, 9:21 PM
This revision is now accepted and ready to land.Apr 10 2019, 9:21 PM
Stan added inline comments.Apr 11 2019, 7:18 AM
source/soundmanager/scripting/SoundGroup.cpp
187 ↗(On Diff #7716)

Do you know of any fast random generators with seeds ? We don't need a bazooka.

43 ↗(On Diff #7710)
Stan added a comment.Apr 17 2019, 9:40 AM

@wraitii Any idea on what could be faster than a MT19973 ? srand() would have been perfect but we cannot use it.

I mean if we don't really care about actual randomness (which it seems we don't), maybe something from there: https://stackoverflow.com/questions/1640258/need-a-fast-random-generator-for-c
Like this should be super-fast and should be enough:

inline int fastrand() { 
  g_seed = (214013*g_seed+2531011); 
  return (g_seed>>16)&0x7FFF; 
}

Or maybe look at those, they are also a few LOC:
https://arvid.io/2018/07/02/better-cxx-prng/

Stan added a comment.Apr 17 2019, 6:13 PM
cl.exe file.cpp /fp:precise
file.cpp
Microsoft (R) Incremental Linker Version 14.00.24245.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:file.exe 
file.obj 

time: 0.114689 seconds
time: 0.00824674 seconds
time: 0.166148 seconds
time: 18.0688 seconds
cl.exe file.cpp /EHsc /O1 /fp:precise
file.cpp
Microsoft (R) Incremental Linker Version 14.00.24245.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:file.exe 
file.obj 

time: 0.11172 seconds
time: 0.00184184 seconds
time: 0.119409 seconds
time: 4.03114 seconds
cl.exe file.cpp /EHsc /O2 /fp:precise
file.cpp
Microsoft (R) Incremental Linker Version 14.00.24245.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:file.exe 
file.obj 

time: 0.0937283 seconds
time: 0.000168023 seconds
time: 0.119085 seconds
time: 3.99642 seconds
cl.exe file.cpp /EHsc /Ox /fp:precise
file.cpp
Microsoft (R) Incremental Linker Version 14.00.24245.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:file.exe 
file.obj 

time: 0.0864048 seconds
time: 0.00019974 seconds
time: 0.115145 seconds
time: 4.15333 seconds
clang++ -Xclang -flto-visibility-public-std -Wall -Wextra -Wpedantic file.cpp -o file.exe

time: 0.182753 seconds
time: 0.01829 seconds
time: 0.199486 seconds
time: 8.68231 seconds
clang++ -Xclang -flto-visibility-public-std -Wall -Wextra -Wpedantic -O1 file.cpp -o file.exe

time: 0.0987724 seconds
time: 0.0103253 seconds
time: 0.121672 seconds
time: 4.25378 seconds
clang++ -Xclang -flto-visibility-public-std -Wall -Wextra -Wpedantic -O2 file.cpp -o file.exe

time: 0.0754372 seconds
time: 3.78e-07 seconds
time: 0.100211 seconds
time: 4.16511 seconds
clang++ -Xclang -flto-visibility-public-std -Wall -Wextra -Wpedantic -O3 file.cpp -o file.exe

time: 0.0575376 seconds
time: 0 seconds
time: 0.0823314 seconds
time: 4.14444 seconds
clang++ -Xclang -flto-visibility-public-std file.cpp -Wall -Wextra -Wpedantic -Ofast -o file.exe

time: 0.0538619 seconds
time: 0 seconds
time: 0.0858678 seconds
time: 4.14739 seconds
clang-cl file.cpp -o file.exe -Wall -Wextra -Wpedantic

time: 0.123457 seconds
time: 0.00816971 seconds
time: 0.155622 seconds
time: 8.44064 seconds
clang-cl file.cpp -o file.exe -Wall -Wextra -Wpedantic -O1

time: 0.0752284 seconds
time: 0 seconds
time: 0.100644 seconds
time: 4.16412 seconds
clang-cl file.cpp -o file.exe -Wall -Wextra -Wpedantic -O2

time: 0.0780395 seconds
time: 0 seconds
time: 0.104815 seconds
time: 4.13017 seconds
clang-cl file.cpp -o file.exe -Wall -Wextra -Wpedantic -Ox

time: 0.0775343 seconds
time: 0 seconds
time: 0.103522 seconds
time: 4.16733 seconds
mingw32-g++ file.cpp -o file.exe -Wall -Wextra -Wpedantic -static-libgcc -static-libstdc++

time: 0.105719 seconds
time: 0.013957 seconds
time: 0.132643 seconds
time: 28.3352 seconds
mingw32-g++ file.cpp -o file.exe -Wall -Wextra -Wpedantic -O1 -static-libgcc -static-libstdc++

time: 0.061683 seconds
time: 0 seconds
time: 0.082779 seconds
time: 9.54547 seconds
mingw32-g++ file.cpp -o file.exe -Wall -Wextra -Wpedantic -O2 -static-libgcc -static-libstdc++

time: 0.065681 seconds
time: 0 seconds
time: 0.08375 seconds
time: 9.30311 seconds
mingw32-g++ file.cpp -o file.exe -Wall -Wextra -Wpedantic -O3 -static-libgcc -static-libstdc++

time: 0.040891 seconds
time: 0 seconds
time: 0.063861 seconds
time: 8.30778 seconds
mingw32-g++ file.cpp -o file.exe -Wall -Wextra -Wpedantic -Ofast -static-libgcc -static-libstdc++

time: 0.039929 seconds
time: 0 seconds
time: 0.060801 seconds
time: 8.38361 seconds
end

Too lazy to format those, but the

inline int fastrand() { 
  g_seed = (214013*g_seed+2531011); 
  return (g_seed>>16)&0x7FFF; 
}

is at worst 10 times faster than the current code. So not only will the game go faster but we'll get the feature. Win/win

Stan updated this revision to Diff 7762.Apr 17 2019, 6:36 PM
  • Use the fast random function
  • Make function and variables members of the SoundGroup.cpp class.
  • Make them non static as it doesn't matter as they are constantly overridden.

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/differential/1222/display/redirect

Stan marked 4 inline comments as done.Apr 17 2019, 6:38 PM
Stan updated this revision to Diff 7763.Apr 17 2019, 6:41 PM
  • Fix incorrect function call
  • Remove deleted whitespace

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1223/display/redirect

Stan added a comment.Apr 17 2019, 6:46 PM

@vladislavbelov Can you recheck that everything is fine so I can commit it ?

Stan updated this revision to Diff 7766.Apr 18 2019, 2:05 PM
  • Initialize private member before using it.

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1224/display/redirect

Stan updated this revision to Diff 7826.Apr 24 2019, 10:46 AM
  • Don't use randfloat for int ^^

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1262/display/redirect

I don't have any objection to use the fast random function, because it won't be used for visual things where non-uniformness is visible.

source/soundmanager/scripting/SoundGroup.cpp
42 ↗(On Diff #7826)

Any source, why these constants?

43 ↗(On Diff #7826)

Why 0x7FFF and not 0xFFFF?

source/soundmanager/scripting/SoundGroup.h
85 ↗(On Diff #7826)

I see 0x7FFF, it's usually uint16_t, but you have uint16_t overflow here, so it have to be uint32_t I suppose.

Stan marked 3 inline comments as done.Apr 24 2019, 9:35 PM
Stan added inline comments.
source/soundmanager/scripting/SoundGroup.cpp
42 ↗(On Diff #7826)

Well thats used by a lot of number generators but that aside I don't know. Maybe @wraitii does.

43 ↗(On Diff #7826)

Was that way I assume its faster to generate smaller numbers.

source/soundmanager/scripting/SoundGroup.h
85 ↗(On Diff #7826)

Yeah that was the recommendation on stackoverflow

vladislavbelov added inline comments.Apr 24 2019, 9:45 PM
source/soundmanager/scripting/SoundGroup.cpp
42 ↗(On Diff #7826)

I mean it'd be good to add a link to an original source or a research.

43 ↗(On Diff #7826)

I don't see a difference between 0x7FFF and 0xFFFF for speed.

source/soundmanager/scripting/SoundGroup.h
85 ↗(On Diff #7826)

Use uint32_t instead of unsigned int.

wraitii added inline comments.Apr 24 2019, 9:53 PM
source/soundmanager/scripting/SoundGroup.cpp
42 ↗(On Diff #7826)

Seems referred here https://software.intel.com/en-us/articles/fast-random-number-generator-on-the-intel-pentiumr-4-processor but honestly I just found it on stack overflow so no idea.

Stan updated this revision to Diff 7839.Apr 24 2019, 10:37 PM
Stan marked 3 inline comments as done.

Use 0xFFF instead 0x7FF
replace unsigned int by u32
replace uint8_t by u8

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1271/display/redirect

Stan marked 4 inline comments as done.Apr 24 2019, 11:12 PM
source/soundmanager/scripting/SoundGroup.cpp
42 ↗(On Diff #7839)

Parenthesis isn't required.

source/soundmanager/scripting/SoundGroup.h
84 ↗(On Diff #7839)

But it still returns int.

Stan updated this revision to Diff 7841.Apr 25 2019, 12:22 AM

Make FastRand return an u32

Stan marked 2 inline comments as done.Apr 25 2019, 12:22 AM

Successful build - Chance fights ever on the side of the prudent.

Link to build: https://jenkins.wildfiregames.com/job/differential/1273/display/redirect

elexis added a subscriber: elexis.Apr 25 2019, 12:08 PM

std::mt19937 looked good to me, as Vladislav said since it's used in the other places too.
Actually the simulation and AI seed use boost::rand48.
But still better than using custom magic numbers?
The only possible performance problem was reinitializing the seed often right?
Wouldn't it be sufficient initialize the seed upon component construction once? Or upon first soundplay?
(Though the magic numbers aren't so bad, only one line, or four including the wrapping.)
(Especially if it's some orders of magnitude faster. But perhaps there are other std or boost RNG that are also orders of magnitude faster.)

source/soundmanager/scripting/SoundGroup.cpp
44 ↗(On Diff #7841)

magic numbers warrant a comment explaining whether they're fully arbitrary or not

Stan added a comment.EditedApr 25 2019, 12:17 PM
In D1584#76088, @elexis wrote:

std::mt19937 looked good to me, as Vladislav said since it's used in the other places too.
Actually the simulation and AI seed use boost::rand48.
But still better than using custom magic numbers?
The only possible performance problem was reinitializing the seed often right?
Wouldn't it be sufficient initialize the seed upon component construction once? Or upon first soundplay?
(Though the magic numbers aren't so bad, only one line, or four including the wrapping.)
(Especially if it's some orders of magnitude faster. But perhaps there are other std or boost RNG that are also orders of magnitude faster.)

Yeah but it slow as hell. I can be called up to 180 per frame on a 4x4 game. This new random function (Fastrand) is ten time faster than the current code and therefore maybe up 100 times faster than mt. So at worst it makes the sound system faster and at best it does what this ticket is about.

Yeah seed reinit is slow. No you need to set the seed each time because you need to reset the random generator as its used more than once.

0XFFF is the max value tat can be reached.
The other two constants are not arbitrary they are what make the seed collision possible.

vladislavbelov accepted this revision.Apr 25 2019, 11:53 PM
This revision was automatically updated to reflect the committed changes.
In D1584#76094, @Stan wrote:

Yeah but it slow as hell.

You refer to the constructor of std::mt19937, right?

You need to set the seed each time because you need to reset the random generator as its used more than once.

How so? An RNG is initialized once and then generates a sequence of random numbers (with the () operator), not only one number.
That's how the simulation and AI seed work too and how it's supposed to work.

The other two constants are not arbitrary they are what make the seed collision possible.
The magic numbers are chosen so that they generate pseudo random numbers over a big enough period

How did you chose them? How did you know that 214013 is a good number, and how does the reader know that 214014 wouldn't be better?
It sounds like Stackoverflow copypaste. Then you should link it.
Doing a websearch seems to confirm this https://stackoverflow.com/questions/4768180/rand-implementation

Stan added a comment.Apr 26 2019, 1:32 PM

It's also a Microsoft Implementation, used in Erlang and Cisco projects. (http://irclogs.wildfiregames.com/2019-04/2019-04-25-QuakeNet-%230ad-dev.log 22:05)
https://rosettacode.org/wiki/Linear_congruential_generator
http://pi.math.cornell.edu/~mec/Winter2009/Luo/Linear%20Congruential%20Generator/linear%20congruential%20gen1.html
https://github.com/cisco-system-traffic-generator/trex-core
https://github.com/erlang/otp

You refer to the constructor of std::mt19937, right?

No even the number generation is slow.

How so? An RNG is initialized once and then generates a sequence of random numbers (with the () operator), not only one number.
That's how the simulation and AI seed work too and how it's supposed to work.

Here we generate two numbers at most m_Gain and m_Pitch. Those number should be the same sequence for a given cmpVisualActor seed. If you keep the same one and the sequence is like 10,25,65,12 the second time it's called for the same seed it will generate different numbers.

  1. M_Gain = 10 M_Pitch = 25 Seed 123456
  2. M_Gain = 25 M_Pitch = 65 Seed 123456

Hence why it needs to be reset so it starts at the sequence beginning again.

How did you chose them? How did you know that 214013 is a good number, and how does the reader know that 214014 wouldn't be better?
It sounds like Stackoverflow copypaste. Then you should link it.
Doing a websearch seems to confirm this https://stackoverflow.com/questions/4768180/rand-implementation

That's part of the Theorem and used in most implementation, sure I could have found other numbers. But I don't see the point.

In D1584#76329, @Stan wrote:

You refer to the constructor of std::mt19937, right?

No even the number generation is slow.

Okay, thanks for having tested.

That's part of the Theorem and used in most implementation

(https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use)
Since the code comment mentions the algorithm, people can lookup the value ranges of the numbers, okay.

Then at last, there is http://www.cplusplus.com/reference/random/linear_congruential_engine/ (@vladislavbelov)

Stan added a comment.Apr 26 2019, 3:34 PM

Then at last, there is http://www.cplusplus.com/reference/random/linear_congruential_engine/ (@vladislavbelov)

Ah didnt find that one I guess I could have run more profiling. But the usage is a bit different so maybe not.

vladislavbelov added a comment.EditedApr 26 2019, 8:12 PM
In D1584#76343, @elexis wrote:

AFAIK it's a bit different, it explicitly does the modulo division. Current implementation uses an implicit and probably faster modulo division through unsigned 32 bit overflowing.

I see that http://www.cplusplus.com/reference/random/mt19937/ has so many arguments that it must be slower, and that the other std::random generators only produce unsigned int types, so okay here.
(Still not a fan of reinventing the wheel. If the other components also need fast random numbers, they would have to introduce a copy. It's not more than 9 lines, but it's 9 lines that would be repeated for the next use case.)

it explicitly does the modulo division

This one does & 0xFFFF, if the implementation is smart, it will not perform the modulo operator at all if the modulo argument was set to 0. (Anyhow, not a defect.)

In D1584#76397, @elexis wrote:

This one does & 0xFFFF.

This line will present in the code anyway, because it's for result, not for seed.

In D1584#76397, @elexis wrote:

This one does & 0xFFFF.

This line will present in the code anyway, because it's for result, not for seed.

But then thanks for the patch and review.