Demonstrates a weird perf mystery regarding auto&for loops.

master
Zed A. Shaw 1 day ago
parent a6248f7b42
commit cc88595af8
  1. 31
      .gitignore
  2. 1
      .vimrc_proj
  3. 10
      LICENSE
  4. 42
      Makefile
  5. 9
      README.md
  6. 41
      meson.build
  7. 7
      scripts/reset_build.ps1
  8. 10
      scripts/reset_build.sh
  9. 10
      src/stats.cpp
  10. 59
      src/stats.hpp
  11. 50
      tests/bao_tests.cpp
  12. 18
      tests/bao_tests.lua
  13. 18
      tests/main.cpp
  14. 498
      tests/perf_tests.cpp
  15. 31
      tests/player_script.lua
  16. 4
      tests/prelude_script.lua
  17. 57
      tests/soa_tests.cpp
  18. 25
      tests/soa_tests.lua
  19. 321
      tests/sol2_test.cpp
  20. 4
      tests/variables.lua
  21. 13
      wraps/fmt.wrap
  22. 9
      wraps/fuc2.wrap
  23. 9
      wraps/sol2.wrap

31
.gitignore vendored

@ -0,0 +1,31 @@
# ---> Vim
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
subprojects
builddir
ttassets
backup
*.exe
*.dll
*.world
coverage
coverage/*
.venv

@ -0,0 +1 @@
set makeprg=meson\ compile\ -C\ .

@ -1 +1,9 @@
It's Copyright. All Rights Reserved. You have no license at all other than permission to view it.
MIT License
Copyright (c) Zed A. Shaw
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,42 @@
all: build test
reset:
ifeq '$(OS)' 'Windows_NT'
powershell -executionpolicy bypass .\scripts\reset_build.ps1
else
sh -x ./scripts/reset_build.sh
endif
build:
meson compile -j 10 -C builddir
release_build:
meson --wipe builddir -Db_ndebug=true --buildtype release
meson compile -j 10 -C builddir
debug_build:
meson setup --wipe builddir -Db_ndebug=true --buildtype debugoptimized
meson compile -j 10 -C builddir
test: build
./builddir/sol2_test
run: build test
ifeq '$(OS)' 'Windows_NT'
powershell "cp ./builddir/sol2_test.exe ."
./sol2_test
else
./builddir/sol2_test
endif
debug: build
gdb --nx -x .gdbinit --ex run --args builddir/sol2_test
debug_run: build
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args builddir/sol2_test
clean:
meson compile --clean -C builddir
debug_test: build
gdb --nx -x .gdbinit --ex run --args builddir/sol2_test -e

@ -1,7 +1,4 @@
# sol2_test
# sol2 testj
Experiments in using Lua with sol2 and how best to structure the data. Contains a shoot out of SOA, vs AOS vs. MOA.
SOA = Struct of Arrays
AOS = Array of Structs
MOA = Many Array of Structs (kind of a hybrid)
Just a quick test of using sol2 and also how a God Object System would work as an alternative to ECS
but using sol2/lua.

@ -0,0 +1,41 @@
project('sol2_test', 'cpp',
version: '0.1.0',
default_options: [
'cpp_std=c++23',
'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1',
])
# use this for common options only for our executables
cpp_args=[
'-Wno-unused-parameter',
'-Wno-unused-function',
'-Wno-unused-variable',
'-Wno-unused-but-set-variable',
'-Wno-deprecated-declarations',
]
link_args=[]
# these are passed as override_defaults
exe_defaults = [ 'warning_level=2', 'werror=false']
fmt = subproject('fmt').get_variable('fmt_dep')
fuc2 = subproject('fuc2').get_variable('fuc2_dep')
sol2 = subproject('sol2').get_variable('sol2_dep')
dependencies = [fmt, fuc2, sol2]
sources = [
]
executable('sol2_test', [
'tests/sol2_test.cpp',
'tests/bao_tests.cpp',
'tests/soa_tests.cpp',
'tests/perf_tests.cpp',
'src/stats.cpp',
'tests/main.cpp',
],
cpp_args: cpp_args,
link_args: link_args,
include_directories: include_directories('src'),
override_options: exe_defaults,
dependencies: dependencies)

@ -0,0 +1,7 @@
mv .\subprojects\packagecache .
rm -recurse -force .\subprojects\,.\builddir\
mkdir subprojects
mv .\packagecache .\subprojects\
mkdir builddir
cp wraps\*.wrap subprojects\
meson setup --default-library=static --prefer-static builddir

@ -0,0 +1,10 @@
#!/usr/bin/env bash
mv -f ./subprojects/packagecache .
rm -rf subprojects builddir
mkdir subprojects
mv -f packagecache ./subprojects/ && true
mkdir builddir
cp wraps/*.wrap subprojects/
# on OSX you can't do this with static
meson setup --default-library=static --prefer-static builddir

@ -0,0 +1,10 @@
#include "stats.hpp"
#include <fmt/core.h>
void Stats::dump(std::string msg)
{
fmt::println("{}: sum: {}, sumsq: {}, n: {}, "
"min: {}, max: {}, mean: {}, stddev: {}",
msg, sum, sumsq, n, min, max, mean(),
stddev());
}

@ -0,0 +1,59 @@
#pragma once
#include <cmath>
#include <chrono>
struct Stats {
using TimeBullshit = std::chrono::time_point<std::chrono::high_resolution_clock>;
double sum = 0.0;
double sumsq = 0.0;
double n = 0.0;
double min = 0.0;
double max = 0.0;
inline void reset() {
sum = 0.0;
sumsq = 0.0;
n = 0.0;
min = 0.0;
max = 0.0;
}
inline double mean() {
return sum / n;
}
inline double stddev() {
return std::sqrt((sumsq - (sum * sum / n)) / (n - 1));
}
inline void sample(double s) {
sum += s;
sumsq += s * s;
if (n == 0) {
min = s;
max = s;
} else {
if (min > s) min = s;
if (max < s) max = s;
}
n += 1;
}
inline TimeBullshit time_start() {
return std::chrono::high_resolution_clock::now();
}
inline void sample_time(TimeBullshit start) {
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration<double>(end - start);
if(elapsed.count() > 0.0) {
sample(1.0/elapsed.count());
}
}
void dump(std::string msg="");
};

@ -0,0 +1,50 @@
#include <fmt/core.h>
#include <deque>
#include <string>
#include <fuc2/testing.hpp>
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
using namespace fuc2;
namespace bao_tests {
struct BAEntity {
std::string name;
int hp=0;
int damage=0;
bool dead=false;
};
void test_bao_basics() {
std::vector<BAEntity> entities;
for(int i = 0; i < 10; i++) {
auto name = fmt::format("Fighter-{}", i);
entities.emplace_back(name, i+10, i+1);
}
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<BAEntity>("BAEntity",
"name", &BAEntity::name,
"hp", &BAEntity::hp,
"damage", &BAEntity::damage,
"dead", &BAEntity::dead);
lua.script_file("tests/bao_tests.lua");
sol::function handler = lua["handler"];
for(auto& fighter : entities) {
handler(fighter);
}
}
fuc2::Set TESTS{
.name="bao",
.options={ .fail_fast=false },
.tests={
TEST(test_bao_basics),
}
};
}

@ -0,0 +1,18 @@
function combat(fighter)
fighter.hp = fighter.hp - fighter.damage
end
function cull_dead(fighter)
if fighter.hp < 0 then
fighter.dead = true
end
end
function handler(fighter)
combat(fighter)
cull_dead(fighter)
print(fighter.name, "HP now", fighter.hp, "damage", fighter.damage, "dead", fighter.dead)
end

@ -0,0 +1,18 @@
#include <fuc2/run.hpp>
TEST_SET(sol2_tests);
TEST_SET(bao_tests);
TEST_SET(soa_tests);
TEST_SET(perf_tests);
using namespace fuc2;
int main(int argc, char* argv[]) {
std::vector<fuc2::Set> tests{
sol2_tests::TESTS,
bao_tests::TESTS,
soa_tests::TESTS,
perf_tests::TESTS,
};
return run_tests(tests, argc, argv);
}

@ -0,0 +1,498 @@
#include <fmt/core.h>
#include <string>
#include <fuc2/testing.hpp>
#include "stats.hpp"
using namespace fuc2;
namespace perf_tests {
struct LargeSOA {
std::vector<int> a01;
std::vector<int> a02;
std::vector<int> a03;
std::vector<int> a04;
std::vector<int> a05;
std::vector<int> a06;
std::vector<int> a07;
std::vector<int> a08;
std::vector<int> a09;
std::vector<int> a10;
std::vector<int> a11;
std::vector<int> a12;
std::vector<int> a13;
std::vector<int> a14;
std::vector<int> a15;
std::vector<int> a16;
std::vector<int> a17;
std::vector<int> a18;
std::vector<int> a19;
std::vector<int> a20;
void push_back(int x) {
a01.push_back(x);
a02.push_back(x);
a03.push_back(x);
a04.push_back(x);
a05.push_back(x);
a06.push_back(x);
a07.push_back(x);
a08.push_back(x);
a09.push_back(x);
a10.push_back(x);
a11.push_back(x);
a12.push_back(x);
a13.push_back(x);
a14.push_back(x);
a15.push_back(x);
a16.push_back(x);
a17.push_back(x);
a18.push_back(x);
a19.push_back(x);
a20.push_back(x);
}
};
struct LargeStruct {
int a01;
int a02;
int a03;
int a04;
int a05;
int a06;
int a07;
int a08;
int a09;
int a10;
int a11;
int a12;
int a13;
int a14;
int a15;
int a16;
int a17;
int a18;
int a19;
int a20;
LargeStruct(int x) :
a01(x),
a02(x),
a03(x),
a04(x),
a05(x),
a06(x),
a07(x),
a08(x),
a09(x),
a10(x),
a11(x),
a12(x),
a13(x),
a14(x),
a15(x),
a16(x),
a17(x),
a18(x),
a19(x),
a20(x) {}
};
struct S1 {
int a01;
int a02;
int a03;
int a04;
S1(int x) : a01(x), a02(x), a03(x), a04(x) { }
};
struct S2 {
int a05;
int a06;
int a07;
int a08;
S2(int x) : a05(x), a06(x), a07(x), a08(x) { }
};
struct S3 {
int a09;
int a10;
int a11;
int a12;
S3(int x) : a09(x), a10(x), a11(x), a12(x) { }
};
struct S4 {
int a13;
int a14;
int a15;
int a16;
S4(int x) : a13(x), a14(x), a15(x), a16(x) { }
};
struct S5 {
int a17;
int a18;
int a19;
int a20;
S5(int x) : a17(x), a18(x), a19(x), a20(x) { }
};
struct MultipleSOA {
std::vector<S1> s1;
std::vector<S2> s2;
std::vector<S3> s3;
std::vector<S4> s4;
std::vector<S5> s5;
void push_back(int x) {
s1.push_back({x});
s2.push_back({x});
s3.push_back({x});
s4.push_back({x});
s5.push_back({x});
}
};
struct Samples {
std::vector<Stats> soa_big_for;
std::vector<Stats> soa_many_for;
std::vector<Stats> ls_big_for;
std::vector<Stats> ls_many_for;
std::vector<Stats> ls_many_for_fast;
std::vector<Stats> moa_big_for;
std::vector<Stats> moa_many_for;
std::vector<Stats> moa_many_for_fast;
};
void run_test(Samples& samples, int sample_size, int sample_number) {
auto soa = new LargeSOA();
auto ls = new std::vector<LargeStruct>;
auto moa = new MultipleSOA();
for(int i = 0; i < sample_size; i++) {
soa->push_back(i);
ls->push_back({i});
moa->push_back(i);
}
fmt::println("Data Size: {}", sizeof(LargeStruct) * sample_size);
Stats big_for;
for(int i = 0; i < sample_number; i++) {
auto start = big_for.time_start();
for(int i = 0; i < sample_size; i++) {
soa->a01[i] += soa->a02[i] + i;
soa->a02[i] += soa->a03[i] + i;
soa->a03[i] += soa->a04[i] + i;
soa->a04[i] += soa->a05[i] + i;
soa->a05[i] += soa->a06[i] + i;
soa->a06[i] += soa->a07[i] + i;
soa->a07[i] += soa->a08[i] + i;
soa->a08[i] += soa->a09[i] + i;
soa->a09[i] += soa->a10[i] + i;
soa->a10[i] += soa->a11[i] + i;
soa->a11[i] += soa->a12[i] + i;
soa->a12[i] += soa->a13[i] + i;
soa->a13[i] += soa->a14[i] + i;
soa->a14[i] += soa->a15[i] + i;
soa->a15[i] += soa->a16[i] + i;
soa->a16[i] += soa->a17[i] + i;
soa->a17[i] += soa->a18[i] + i;
soa->a18[i] += soa->a19[i] + i;
soa->a19[i] += soa->a20[i] + i;
}
big_for.sample_time(start);
}
big_for.dump("soa big for");
fmt::println("-------------\n");
samples.soa_big_for.push_back(big_for);
Stats many_for;
for(int i = 0; i < sample_number; i++) {
auto start = many_for.time_start();
for(int i = 0; i < sample_size; i++) soa->a01[i] += soa->a02[i] + i;
for(int i = 0; i < sample_size; i++) soa->a02[i] += soa->a03[i] + i;
for(int i = 0; i < sample_size; i++) soa->a03[i] += soa->a04[i] + i;
for(int i = 0; i < sample_size; i++) soa->a04[i] += soa->a05[i] + i;
for(int i = 0; i < sample_size; i++) soa->a05[i] += soa->a06[i] + i;
for(int i = 0; i < sample_size; i++) soa->a06[i] += soa->a07[i] + i;
for(int i = 0; i < sample_size; i++) soa->a07[i] += soa->a08[i] + i;
for(int i = 0; i < sample_size; i++) soa->a08[i] += soa->a09[i] + i;
for(int i = 0; i < sample_size; i++) soa->a09[i] += soa->a10[i] + i;
for(int i = 0; i < sample_size; i++) soa->a10[i] += soa->a11[i] + i;
for(int i = 0; i < sample_size; i++) soa->a11[i] += soa->a12[i] + i;
for(int i = 0; i < sample_size; i++) soa->a12[i] += soa->a13[i] + i;
for(int i = 0; i < sample_size; i++) soa->a13[i] += soa->a14[i] + i;
for(int i = 0; i < sample_size; i++) soa->a14[i] += soa->a15[i] + i;
for(int i = 0; i < sample_size; i++) soa->a15[i] += soa->a16[i] + i;
for(int i = 0; i < sample_size; i++) soa->a16[i] += soa->a17[i] + i;
for(int i = 0; i < sample_size; i++) soa->a17[i] += soa->a18[i] + i;
for(int i = 0; i < sample_size; i++) soa->a18[i] += soa->a19[i] + i;
for(int i = 0; i < sample_size; i++) soa->a19[i] += soa->a20[i] + i;
many_for.sample_time(start);
}
many_for.dump("soa many for");
fmt::println("-------------\n");
samples.soa_many_for.push_back(many_for);
Stats ls_big_for;
for(int i = 0; i < sample_number; i++) {
auto start = ls_big_for.time_start();
for(auto& el : *ls) {
el.a01 += el.a02 + i;
el.a02 += el.a03 + i;
el.a03 += el.a04 + i;
el.a04 += el.a05 + i;
el.a05 += el.a06 + i;
el.a06 += el.a07 + i;
el.a07 += el.a08 + i;
el.a08 += el.a09 + i;
el.a09 += el.a10 + i;
el.a10 += el.a11 + i;
el.a11 += el.a12 + i;
el.a12 += el.a13 + i;
el.a13 += el.a14 + i;
el.a14 += el.a15 + i;
el.a15 += el.a16 + i;
el.a16 += el.a17 + i;
el.a17 += el.a18 + i;
el.a18 += el.a19 + i;
el.a19 += el.a20 + i;
}
ls_big_for.sample_time(start);
}
ls_big_for.dump("ls big for");
fmt::println("-------------\n");
samples.ls_big_for.push_back(ls_big_for);
Stats ls_many_for;
for(int i = 0; i < sample_number; i++) {
auto start = ls_many_for.time_start();
for(auto& el : *ls) el.a01 += el.a02 + i;
for(auto& el : *ls) el.a02 += el.a03 + i;
for(auto& el : *ls) el.a03 += el.a04 + i;
for(auto& el : *ls) el.a04 += el.a05 + i;
for(auto& el : *ls) el.a05 += el.a06 + i;
for(auto& el : *ls) el.a06 += el.a07 + i;
for(auto& el : *ls) el.a07 += el.a08 + i;
for(auto& el : *ls) el.a08 += el.a09 + i;
for(auto& el : *ls) el.a09 += el.a10 + i;
for(auto& el : *ls) el.a10 += el.a11 + i;
for(auto& el : *ls) el.a11 += el.a12 + i;
for(auto& el : *ls) el.a12 += el.a13 + i;
for(auto& el : *ls) el.a13 += el.a14 + i;
for(auto& el : *ls) el.a14 += el.a15 + i;
for(auto& el : *ls) el.a15 += el.a16 + i;
for(auto& el : *ls) el.a16 += el.a17 + i;
for(auto& el : *ls) el.a17 += el.a18 + i;
for(auto& el : *ls) el.a18 += el.a19 + i;
for(auto& el : *ls) el.a19 += el.a20 + i;
ls_many_for.sample_time(start);
}
ls_many_for.dump("ls many for");
fmt::println("-------------\n");
samples.ls_many_for.push_back(ls_many_for);
Stats ls_many_for_fast;
for(int i = 0; i < sample_number; i++) {
auto start = ls_many_for_fast.time_start();
for(int i = 0; i < sample_size; i++) (*ls)[i].a01 += (*ls)[i].a02 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a02 += (*ls)[i].a03 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a03 += (*ls)[i].a04 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a04 += (*ls)[i].a05 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a05 += (*ls)[i].a06 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a06 += (*ls)[i].a07 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a07 += (*ls)[i].a08 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a08 += (*ls)[i].a09 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a09 += (*ls)[i].a10 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a10 += (*ls)[i].a11 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a11 += (*ls)[i].a12 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a12 += (*ls)[i].a13 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a13 += (*ls)[i].a14 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a14 += (*ls)[i].a15 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a15 += (*ls)[i].a16 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a16 += (*ls)[i].a17 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a17 += (*ls)[i].a18 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a18 += (*ls)[i].a19 + i;
for(int i = 0; i < sample_size; i++) (*ls)[i].a19 += (*ls)[i].a20 + i;
ls_many_for_fast.sample_time(start);
}
ls_many_for_fast.dump("ls many for");
fmt::println("-------------\n");
samples.ls_many_for_fast.push_back(ls_many_for_fast);
Stats moa_big_for;
for(int i = 0; i < sample_number; i++) {
auto start = moa_big_for.time_start();
for(int i = 0; i < sample_size; i++) {
moa->s1[i].a01 += moa->s1[i].a02 + i;
moa->s1[i].a02 += moa->s1[i].a03 + i;
moa->s1[i].a03 += moa->s1[i].a04 + i;
moa->s1[i].a04 += moa->s2[i].a05 + i;
moa->s2[i].a05 += moa->s2[i].a06 + i;
moa->s2[i].a06 += moa->s2[i].a07 + i;
moa->s2[i].a07 += moa->s2[i].a08 + i;
moa->s2[i].a08 += moa->s3[i].a09 + i;
moa->s3[i].a09 += moa->s3[i].a10 + i;
moa->s3[i].a10 += moa->s3[i].a11 + i;
moa->s3[i].a11 += moa->s3[i].a12 + i;
moa->s3[i].a12 += moa->s4[i].a13 + i;
moa->s4[i].a13 += moa->s4[i].a14 + i;
moa->s4[i].a14 += moa->s4[i].a15 + i;
moa->s4[i].a15 += moa->s4[i].a16 + i;
moa->s4[i].a16 += moa->s5[i].a17 + i;
moa->s5[i].a17 += moa->s5[i].a18 + i;
moa->s5[i].a18 += moa->s5[i].a19 + i;
moa->s5[i].a19 += moa->s5[i].a20 + i;
}
moa_big_for.sample_time(start);
}
moa_big_for.dump("moa big for");
fmt::println("-------------\n");
samples.moa_big_for.push_back(moa_big_for);
Stats moa_many_for;
for(int i = 0; i < sample_number; i++) {
auto start = moa_many_for.time_start();
for(int i = 0; i < sample_size; i++) {
for(auto& el : moa->s1) {
el.a01 += el.a02 + i;
el.a02 += el.a03 + i;
el.a03 += el.a04 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s1[i].a04 += moa->s2[i].a05 + i;
}
for(auto& el : moa->s2) {
el.a05 += el.a06 + i;
el.a06 += el.a07 + i;
el.a07 += el.a08 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s2[i].a08 += moa->s3[i].a09 + i;
}
for(auto& el : moa->s3) {
el.a09 += el.a10 + i;
el.a10 += el.a11 + i;
el.a11 += el.a12 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s3[i].a12 += moa->s4[i].a13 + i;
}
for(auto& el : moa->s4) {
el.a13 += el.a14 + i;
el.a14 += el.a15 + i;
el.a15 += el.a16 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s4[i].a16 += moa->s5[i].a17 + i;
}
for(auto& el : moa->s5) {
el.a17 += el.a18 + i;
el.a18 += el.a19 + i;
el.a19 += el.a20 + i;
}
}
moa_many_for.sample_time(start);
}
moa_many_for.dump("moa many for");
fmt::println("-------------\n");
samples.moa_many_for.push_back(moa_many_for);
Stats moa_many_for_fast;
for(int i = 0; i < sample_number; i++) {
auto start = moa_many_for_fast.time_start();
for(int i = 0; i < sample_size; i++) {
moa->s1[i].a01 += moa->s1[i].a02 + i;
moa->s1[i].a02 += moa->s1[i].a03 + i;
moa->s1[i].a03 += moa->s1[i].a04 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s1[i].a04 += moa->s2[i].a05 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s2[i].a05 += moa->s2[i].a06 + i;
moa->s2[i].a06 += moa->s2[i].a07 + i;
moa->s2[i].a07 += moa->s2[i].a08 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s2[i].a08 += moa->s3[i].a09 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s3[i].a09 += moa->s3[i].a10 + i;
moa->s3[i].a10 += moa->s3[i].a11 + i;
moa->s3[i].a11 += moa->s3[i].a12 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s3[i].a12 += moa->s4[i].a13 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s4[i].a13 += moa->s4[i].a14 + i;
moa->s4[i].a14 += moa->s4[i].a15 + i;
moa->s4[i].a15 += moa->s4[i].a16 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s4[i].a16 += moa->s5[i].a17 + i;
}
for(int i = 0; i < sample_size; i++) {
moa->s5[i].a17 += moa->s5[i].a18 + i;
moa->s5[i].a18 += moa->s5[i].a19 + i;
moa->s5[i].a19 += moa->s5[i].a20 + i;
}
moa_many_for_fast.sample_time(start);
}
moa_many_for_fast.dump("moa many for");
fmt::println("-------------\n");
samples.moa_many_for_fast.push_back(moa_many_for_fast);
}
void test_soa_performance() {
Samples samples;
for(int i = 0; i < 10; i++) {
run_test(samples, 100, 100);
}
for(size_t i = 0; i < samples.soa_big_for.size(); i++) {
fmt::println("{:.2f} {:.2f} {:.2f} {:.2f}!={:.2f} {:.2f} {:.2f}!={:.2f}",
samples.soa_big_for[i].mean(),
samples.soa_many_for[i].mean(),
samples.ls_big_for[i].mean(),
samples.ls_many_for[i].mean(),
samples.ls_many_for_fast[i].mean(),
samples.moa_big_for[i].mean(),
samples.moa_many_for[i].mean(),
samples.moa_many_for_fast[i].mean());
}
}
fuc2::Set TESTS{
.name="perf",
.tests={
TEST(test_soa_performance),
}
};
}

@ -0,0 +1,31 @@
p1 = player.new(2)
-- p2 is still here from being
-- set with lua["p2"] = player(0); below
local p2shoots = p2:shoot()
assert(not p2shoots)
-- had 0 ammo
-- set variable property setter
p1.hp = 545
-- get variable through property unqualified_getter
print(p1.hp)
assert(p1.hp == 545)
local did_shoot_1 = p1:shoot()
print(did_shoot_1)
print(p1.bullets)
local did_shoot_2 = p1:shoot()
print(did_shoot_2)
print(p1.bullets)
local did_shoot_3 = p1:shoot()
print(did_shoot_3)
-- can read
print(p1.bullets)
-- would error: is a readonly variable, cannot write
-- p1.bullets = 20
p1:boost()
-- call the function we define at runtime from a Lua script
p1:brake()

@ -0,0 +1,4 @@
function player:brake ()
self.speed = 0
print("we hit the brakes!")
end

@ -0,0 +1,57 @@
#include <fmt/core.h>
#include <deque>
#include <string>
#include <fuc2/testing.hpp>
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include "stats.hpp"
using namespace fuc2;
namespace soa_tests {
struct SOAStyle {
std::vector<std::string> name;
std::vector<int> hp;
std::vector<int> damage;
std::vector<bool> dead;
size_t count=0;
};
void test_soa_basics() {
SOAStyle world;
for(int i = 0; i < 10; i++) {
auto name = fmt::format("Fighter-{}", i);
world.name.emplace_back(name);
world.hp.emplace_back(i+10);
world.damage.emplace_back(i+1);
world.dead.emplace_back(false);
world.count++;
}
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<SOAStyle>("SOAStyle",
"name", &SOAStyle::name,
"hp", &SOAStyle::hp,
"damage", &SOAStyle::damage,
"dead", &SOAStyle::dead,
"count", &SOAStyle::count);
lua.script_file("tests/soa_tests.lua");
sol::function handler = lua["handler"];
handler(world);
}
fuc2::Set TESTS{
.name="soa",
.options={ .fail_fast=false },
.tests={
TEST(test_soa_basics),
}
};
}

@ -0,0 +1,25 @@
function combat(world, index)
world.hp[index] = world.hp[index] - world.damage[index]
end
function cull_dead(world, index)
if world.hp[index] < 0 then
world.dead[index] = true
end
end
function handler(world)
print("world count", world.count)
for i = 1, world.count + 1, 1
do
combat(world, i)
cull_dead(world, i)
print(world.name[i],
"HP now", world.hp[i],
"damage", world.damage[i],
"dead", world.dead[i])
end
end

@ -0,0 +1,321 @@
#include <fmt/core.h>
#include <deque>
#include <string>
#include <fuc2/testing.hpp>
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
using namespace fuc2;
namespace sol2_tests {
void test_basic_lua_run() {
sol::state lua;
int x = 0;
lua.set_function("beep", [&x]{++x; });
lua.script("beep()");
EQUAL(x, 1, "Lua failed to set x");
}
struct vars {
int boop = 0;
};
void test_big_ass_vector() {
std::vector<vars> variables;
for(int i = 0; i < 100; i++) {
variables.emplace_back(i);
}
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<vars>("vars", "boop", &vars::boop);
lua.script(R"(
function handler(v)
v.boop = v.boop + 1
end
)");
sol::function handler = lua["handler"];
for(auto& v : variables) {
int boop_before = v.boop;
handler(v);
CHECK(boop_before == v.boop - 1);
}
}
void test_usertype_vars() {
sol::state lua;
lua.new_usertype<vars>("vars", "boop", &vars::boop);
lua.script("beep = vars.new()\n"
"beep.boop = 1");
EQUAL(lua.get<vars>("beep").boop, 1);
}
void test_base_libraries() {
using L = sol::lib;
sol::state lua;
lua.open_libraries(
L::base,
L::package,
L::coroutine,
L::string,
L::os,
L::math,
L::table,
L::debug,
L::bit32,
L::io,
L::ffi,
L::jit);
lua.script("print('bark bark bark!')");
fmt::println("");
}
void test_sol_lua_error() {
BLOWS_UP([]() {
sol::state lua;
auto result1 = lua.safe_script("bad.code");
});
}
void test_variables_from_file() {
sol::state lua;
lua.script_file("tests/variables.lua");
CHECK(lua["config"]["fullscreen"] == false);
sol::table config = lua["config"];
CHECK(config["fullscreen"] == false);
}
void test_passing_args_to_scripts() {
sol::state lua;
lua.open_libraries(sol::lib::base);
const auto& my_script = R"(
local a,b,c = ...
print(a, b, c)
)";
sol::load_result fx = lua.load(my_script);
if(!fx.valid()) {
sol::error err = fx;
throw err;
}
fx("your", "arguments", "here");
}
void test_transfer_func() {
sol::state lua;
sol::state lua2;
lua2.open_libraries(sol::lib::base);
sol::load_result lr = lua.load("a = function(v) print(v) return v end");
CHECK(lr.valid());
sol::protected_function target = lr.get<sol::protected_function>();
sol::bytecode target_bc = target.dump();
auto result2 = lua2.safe_script(target_bc.as_string_view(), sol::script_pass_on_error);
CHECK(result2.valid());
sol::protected_function pf = lua2["a"];
int v = pf(25557);
CHECK(v == 25557);
}
void test_get_variables() {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.set("number", 24);
CHECK(lua["number"] == 24);
lua["number"] = 24.5;
CHECK(lua["number"] == 24.5);
// how to compare a string?
lua["important_string"] = "test";
std::string a_string = "test";
CHECK(lua["important_string"] == a_string);
lua["a_function"] = []() { return 100; };
auto result = lua["a_function"]();
CHECK(result.valid());
int as_int = result;
CHECK(as_int == 100);
lua["some_table"] = lua.create_table_with("value", 24);
CHECK(lua["some_table"]["value"] == 24);
}
void test_this_has_a_bug_in_sol2() {
sol::state lua;
lua.script(R"(
function handler (message)
return "Handled this message: " .. message
end
function f (a)
if a < 0 then
error("negative number detected")
end
return a + 5
end
)");
sol::protected_function f = lua["f"];
// this fails, error_handler is private
// f.error_handler = lua["handler"];
sol::protected_function_result result = f(-500);
if (result.valid()) {
// Call succeeded
int x = result;
}
else {
// Call failed
sol::error err = result;
std::string what = err.what();
// 'what' Should read
// "Handled this message: negative number detected"
}
}
void test_multiple_returns() {
sol::state lua;
lua.script("function f(a,b,c) return a, b, c end");
std::tuple<int, int, int> result;
result = lua["f"](1, 2, 3);
CHECK(std::get<0>(result) == 1);
CHECK(std::get<1>(result) == 2);
CHECK(std::get<2>(result) == 3);
int a;
int b;
std::string c;
sol::tie(a, b, c) = lua["f"](1, 2, "bar");
CHECK(a == 1);
CHECK(b == 2);
CHECK(c == "bar");
}
sol::object fancy_func(sol::object a, sol::object b, sol::this_state s) {
sol::state_view lua(s);
if(a.is<int>() && b.is<int>()) {
return sol::make_object(lua, a.as<int>() + b.as<int>());
} else if(a.is<bool>()) {
bool do_triple = a.as<bool>();
return sol::make_object(lua, b.as<double>() * (do_triple ? 3 : 1));
} else {
return sol::make_object(lua, sol::lua_nil);
}
}
void test_any_object_types() {
sol::state lua;
lua["f"] = fancy_func;
int result = lua["f"](1, 2);
CHECK(result == 3);
double result2 = lua["f"](false, 2.5);
CHECK(result2 == 2.5);
lua.script("result3 = f(true, 5.5)");
double result3 = lua["result3"];
CHECK(result3 == 16.5);
}
struct player {
int bullets;
int speed;
player() : player(3, 100) {}
player(int ammo) : player(ammo, 100) {}
player(int ammo, int hitpoints) :
bullets(ammo), hp(hitpoints) {}
void boost() { speed += 10; }
bool shoot() {
if(bullets < 1) {
return false;
}
--bullets;
return true;
}
void set_hp(int value) {
hp = value;
}
int get_hp() const {
return hp;
}
private:
int hp;
};
void test_udt_operations() {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua["p2"] = player(0);
sol::usertype<player> player_type = lua.new_usertype<player>("player",
sol::constructors<
player(),
player(int),
player(int, int)>());
player_type["shoot"] = &player::shoot;
player_type["boost"] = &player::boost;
player_type["hp"] = sol::property(&player::get_hp, &player::set_hp);
player_type["speed"] = &player::speed;
player_type.set("bullets", sol::readonly(&player::bullets));
lua.script_file("tests/prelude_script.lua");
lua.script_file("tests/player_script.lua");
}
fuc2::Set TESTS{
.name="sol2",
.options={ .fail_fast=false },
.tests={
TEST(test_basic_lua_run),
TEST(test_usertype_vars),
TEST(test_base_libraries),
TEST(test_sol_lua_error),
TEST(test_variables_from_file),
TEST(test_big_ass_vector),
TEST(test_passing_args_to_scripts),
TEST(test_get_variables),
TEST(test_this_has_a_bug_in_sol2),
TEST(test_multiple_returns),
TEST(test_any_object_types),
TEST(test_udt_operations),
}
};
}

@ -0,0 +1,4 @@
config = {
fullscreen = false,
resolution = { x = 1024, y = 768}
}

@ -0,0 +1,13 @@
[wrap-file]
directory = fmt-12.0.0
source_url = https://github.com/fmtlib/fmt/archive/12.0.0.tar.gz
source_filename = fmt-12.0.0.tar.gz
source_hash = aa3e8fbb6a0066c03454434add1f1fc23299e85758ceec0d7d2d974431481e40
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_12.0.0-1/fmt-12.0.0.tar.gz
patch_filename = fmt_12.0.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/fmt_12.0.0-1/get_patch
patch_hash = 307f288ebf3850abf2f0c50ac1fb07de97df9538d39146d802f3c0d6cada8998
wrapdb_version = 12.0.0-1
[provide]
dependency_names = fmt

@ -0,0 +1,9 @@
[wrap-git]
directory=fuc2-0.1.0
url=https://lcthw.dev/cpp/fuc2.git
revision=HEAD
depth=1
method=meson
[provide]
fuc2 = fuc2_dep

@ -0,0 +1,9 @@
[wrap-git]
directory=sol2-HEAD
url=https://github.com/ThePhD/sol2.git
revision=HEAD
depth=1
method=meson
[provide]
sol2 = sol2_dep
Loading…
Cancel
Save