Skip to content

Commit 0bc70d7

Browse files
committed
Add Test Cases to catch issues with >= 128 streams
1 parent 457f530 commit 0bc70d7

1 file changed

Lines changed: 168 additions & 0 deletions

File tree

tests/test_cases/gpu/test_cuda_simulation.cu

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,174 @@ TEST(TestCUDASimulation, setEnvironmentProperty) {
774774
s.simulate();
775775
}
776776

777+
778+
/**
779+
* Test that simullations work with a large number of agent functions in a single layer (i.e. > 128).
780+
* This needs to be a non-trivial model in order to trigger the encountered issue described by #727
781+
* https://github.com/FLAMEGPU/FLAMEGPU2/issues/727
782+
* Message output, agent output and/or agent death should all trigger compactions which might highlight this issues.
783+
* Additionally other uses of CUDAScanCompaction in general should trigger this issue.
784+
* @todo - similar tests may be useful for most / all features which can occur in streams
785+
*/
786+
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerAgentDeath, MessageNone, MessageNone) {
787+
if (FLAMEGPU->getID() % 2) {
788+
return flamegpu::DEAD;
789+
}
790+
return flamegpu::ALIVE;
791+
}
792+
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerAgentBirth, MessageNone, MessageNone) {
793+
if (FLAMEGPU->getID() % 2) {
794+
FLAMEGPU->agent_out.setVariable<unsigned int>("b", FLAMEGPU->getVariable<unsigned int>("b"));
795+
}
796+
return flamegpu::ALIVE;
797+
}
798+
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerBucketOut, MessageNone, MessageBucket) {
799+
if (FLAMEGPU->getID() % 2) {
800+
FLAMEGPU->message_out.setVariable<flamegpu::id_t>("id", FLAMEGPU->getID());
801+
FLAMEGPU->message_out.setKey(FLAMEGPU->getVariable<unsigned int>("b"));
802+
}
803+
return flamegpu::ALIVE;
804+
}
805+
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerBucketIn, MessageBucket, MessageNone) {
806+
unsigned int c = 0;
807+
for (auto &message : FLAMEGPU->message_in(FLAMEGPU->getVariable<unsigned int>("b"))) {
808+
c++;
809+
}
810+
FLAMEGPU->setVariable<unsigned int>("c", c);
811+
return flamegpu::ALIVE;
812+
}
813+
TEST(TestCUDASimulation, VeryWideLayerDeath) {
814+
// The width of the model, i.e. the number of agent types
815+
constexpr unsigned int MODEL_WIDTH = 129;
816+
// How many individuals to place in each population. This should be irrelevant.
817+
constexpr unsigned int POP_SIZE = 32;
818+
// Create a model containing a very large number of different agent types, which all operate in the same layer.
819+
ModelDescription model("VeryWideLayerDeath");
820+
// Create the layer for all the agent functions to reside in
821+
auto& layer = model.newLayer();
822+
// Define many agent types
823+
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
824+
std::string agent_name("agent_" + std::to_string(idx));
825+
std::string agent_function_name(agent_name + "_VeryWideLayerAgentDeath");
826+
// Define the agent
827+
AgentDescription& agent = model.newAgent(agent_name);
828+
// Add a variable, to allow birth / bucket messaging
829+
agent.newVariable<unsigned int>("b", 0);
830+
// Define an agent function which allows death
831+
auto& f = agent.newFunction(agent_function_name, VeryWideLayerAgentDeath);
832+
f.setAllowAgentDeath(true);
833+
// Add the function to the single model layer
834+
layer.addAgentFunction(f);
835+
}
836+
// Create the simulation
837+
CUDASimulation simulation(model);
838+
// Populate the simulation
839+
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
840+
std::string agent_name("agent_" + std::to_string(idx));
841+
AgentDescription& agent = model.Agent(agent_name);
842+
AgentVector population(agent, POP_SIZE);
843+
simulation.setPopulationData(population);
844+
}
845+
// Run a single simulation step.
846+
simulation.SimulationConfig().steps = 1;
847+
simulation.applyConfig();
848+
EXPECT_NO_THROW(simulation.simulate());
849+
// If this runs then the test can be assumed to have passed, i.e. an error did not occur.
850+
}
851+
TEST(TestCUDASimulation, VeryWideLayerBirth) {
852+
// The width of the model, i.e. the number of agent types
853+
constexpr unsigned int MODEL_WIDTH = 129;
854+
// How many individuals to place in each population. This should be irrelevant.
855+
constexpr unsigned int POP_SIZE = 32;
856+
// Create a model containing a very large number of different agent types, which all operate in the same layer.
857+
ModelDescription model("VeryWideLayerBirth");
858+
// Create the layer for all the agent functions to reside in
859+
auto& layer = model.newLayer();
860+
// Define many agent types
861+
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
862+
std::string agent_name("agent_" + std::to_string(idx));
863+
std::string agent_function_name(agent_name + "_VeryWideLayerAgentBirth");
864+
// Define the agent
865+
AgentDescription& agent = model.newAgent(agent_name);
866+
// Add a variable, to allow birth / bucket messaging
867+
agent.newVariable<unsigned int>("b", 0);
868+
// Define an agent function which allows birth
869+
auto& f = agent.newFunction(agent_function_name, VeryWideLayerAgentBirth);
870+
f.setAgentOutput(agent);
871+
// Add the function to the single model layer
872+
layer.addAgentFunction(f);
873+
}
874+
// Create the simulation
875+
CUDASimulation simulation(model);
876+
// Populate the simulation
877+
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
878+
std::string agent_name("agent_" + std::to_string(idx));
879+
AgentDescription& agent = model.Agent(agent_name);
880+
AgentVector population(agent, POP_SIZE);
881+
simulation.setPopulationData(population);
882+
}
883+
// Run a single simulation step.
884+
simulation.SimulationConfig().steps = 1;
885+
simulation.applyConfig();
886+
EXPECT_NO_THROW(simulation.simulate());
887+
// If this runs then the test can be assumed to have passed, i.e. an error did not occur.
888+
}
889+
TEST(TestCUDASimulation, VeryWideLayerBucketMessaging) {
890+
// The width of the model, i.e. the number of agent types
891+
constexpr unsigned int MODEL_WIDTH = 129;
892+
// How many individuals to place in each population. This should be irrelevant.
893+
constexpr unsigned int POP_SIZE = 32;
894+
// How many buckets exist.
895+
constexpr unsigned int BUCKET_COUNT = POP_SIZE / 2;
896+
// Create a model containing a very large number of different agent types, which all operate in the same layer.
897+
ModelDescription model("VeryWideLayerBucketMessaging");
898+
// Create the layers for all the agent functions to reside in
899+
auto& layer_out = model.newLayer();
900+
auto& layer_in = model.newLayer();
901+
// Define many agent types and message types.
902+
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
903+
std::string agent_name("agent_" + std::to_string(idx));
904+
std::string agent_function_out_name(agent_name + "_VeryWideLayerBucketOut");
905+
std::string agent_function_in_name(agent_name + "_VeryWideLayerBucketIn");
906+
std::string message_name("message_" + std::to_string(idx));
907+
// Define the message list
908+
MessageBucket::Description &message = model.newMessage<MessageBucket>(message_name);
909+
message.setBounds(0, BUCKET_COUNT);
910+
message.newVariable<flamegpu::id_t>("id");
911+
// Define the agent
912+
AgentDescription& agent = model.newAgent(agent_name);
913+
// Add a variable, to allow bucket messaging
914+
agent.newVariable<unsigned int>("b", 0);
915+
agent.newVariable<unsigned int>("c", 0);
916+
// Define the message output and input agent functions
917+
auto& f_out = agent.newFunction(agent_function_out_name, VeryWideLayerBucketOut);
918+
f_out.setMessageOutput(message_name);
919+
auto& f_in = agent.newFunction(agent_function_in_name, VeryWideLayerBucketIn);
920+
f_in.setMessageInput(message_name);
921+
// Add the function to the single model layer
922+
layer_out.addAgentFunction(f_out);
923+
layer_in.addAgentFunction(f_in);
924+
}
925+
// Create the simulation
926+
CUDASimulation simulation(model);
927+
// Populate the simulation
928+
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
929+
std::string agent_name("agent_" + std::to_string(idx));
930+
AgentDescription& agent = model.Agent(agent_name);
931+
AgentVector population(agent, POP_SIZE);
932+
for (auto instance : population) {
933+
instance.setVariable<unsigned int>("b", instance.getID() / BUCKET_COUNT);
934+
instance.setVariable<unsigned int>("c", 0);
935+
}
936+
simulation.setPopulationData(population);
937+
}
938+
// Run a single simulation step.
939+
simulation.SimulationConfig().steps = 1;
940+
simulation.applyConfig();
941+
EXPECT_NO_THROW(simulation.simulate());
942+
// If this runs then the test can be assumed to have passed, i.e. an error did not occur.
943+
}
944+
777945
} // namespace test_cuda_simulation
778946
} // namespace tests
779947
} // namespace flamegpu

0 commit comments

Comments
 (0)