climateinteractive / SDEverywhere

SDEverywhere translates System Dynamics models from Vensim to C, JavaScript, and WebAssembly
http://sdeverywhere.org/
MIT License
58 stars 21 forks source link

C output: Change from Doubles to Floats #7

Open travisfranck opened 6 years ago

travisfranck commented 6 years ago

We use 64-bit math in SDEverywhere because of a long-standing rule in C that all internal math operations are 64 bit, even if the operands are floats, and results are ultimately rounded to 32 bit precision. I elected to maintain 64-bit precision throughout by storing data in C doubles.

But your comment led me to look into this further yesterday. I discovered that in the C99 standard that modern compilers like clang use, the precision of internal math operations is implementation dependent. And guess what? Clang does not enforce 64 bit precision internally. If you use float operands, math operations are 32-bit.

I think moving to 32-bit math makes sense for SDEverywhere. That is enough precision for SD work. Most people use the 32 bit version of Vensim. We could remove the requirement for validating against data from 64-bit Vensim. And we would cut our memory requirements for data in half. This mostly involves changing "double" to "float" everywhere and using the float versions of standard library functions.

-Todd

travisfranck commented 6 years ago

I agree that Floats should be enough, and comparing to 32-bit Vensim makes sense.

ToddFincannon commented 5 years ago

It also turns out that Vensim stores all values as floats, even when it does double-precision computations. https://www.vensim.com/documentation/index.html?ref_variable_names.htm

chrispcampbell commented 4 years ago

I did a quick experiment to change all uses of doubles to floats (actually an sde_float typedef) in SDEverywhere c files (and in the generated c file).

There are some good performance and size wins to be had from this change (see below), but we'd need to spend some time making sure that any differences in the new outputs are within an acceptable range, so I think we should hold off on the change until we get some more automated checks in place. The runtime performance gains appear to be very slight on modern processors; the main benefit appears to be in shaving off ~120kb from the wasm binary size.

Here are some performance numbers for the record relative to other performance work.

Performance

MacBook Pro (2019) | 2.4 GHz 8-core i9, 32 GB RAM, macOS 10.15

Issue C run (ms) Wasm run (ms) Wasm init (ms) JS mem (MB) Page mem (MB)
baseline 45.8 87.5 38.0 94 685
SDE 18 46.0 85.6 18.0 39 672
SDE 19 42.8 49.4 15.0 38 25
SDE 22 34.8 44.8 15.0 38 21
SDE 23 32.7 42.8 13.0 38 32
SDE 24 26.6 38.2 13.0 39 26
SDE 7 24.4 34.8 10.0 39 20

iPhone 8 | A11, iOS 13

Issue C run (ms) Wasm run (ms) Wasm init (ms) JS mem (MB) Page mem (MB)
baseline 39.9 187.0 165.0 39 645
SDE 18 40.3 219.0 86.0 38 724
SDE 19 40.1 81.6 83.0 38 41
SDE 22 35.5 74.6 86.0 40 40
SDE 23 31.1 73.6 82.0 41 39
SDE 24 28.5 71.6 82.0 40 38
SDE 7 28.7 70.0 70.0 36 36

iPad Air (2013) | A7, iOS 12

Issue C run (ms) Wasm run (ms) Wasm init (ms) JS mem (MB) Page mem (MB)
baseline 151.0 1372.2 30146.0 77 331
SDE 18 166.0 1408.0 4416.0 42 395
SDE 19 151.0 837.6 1291.0 45 41
SDE 22 137.0 771.6 1484.0 44 40
SDE 23 110.1 642.2 1148.0 44 41
SDE 24 111.8 638.4 1236.0 45 37
SDE 7 91.7 543.8 1120.0 43 51

Size

Issue Wasm size (bytes)
baseline 1,084,036
SDE 18 773,968
SDE 19 776,851
SDE 22 737,028
SDE 23 741,668
SDE 24 741,677
SDE 7 616,264

Legend

Issue Date Notes
baseline 2020/07/08 baseline prior to performance work
SDE 18 2020/07/09 change lookup init to use static arrays
SDE 19 2020/07/09 break large functions into chunked subfunctions
SDE 22 2020/07/10 replace wrapper functions with macros
SDE 23 2020/07/10 replace dimension array access with simple index
SDE 24 2020/07/10 optimize __lookup function
SDE 7 2020/07/10 change from doubles to floats