Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Very wide model support #729

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions tests/test_cases/gpu/test_cuda_simulation.cu
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,174 @@ TEST(TestCUDASimulation, setEnvironmentProperty) {
s.simulate();
}


/**
* Test that simullations work with a large number of agent functions in a single layer (i.e. > 128).
* This needs to be a non-trivial model in order to trigger the encountered issue described by #727
* https://github.com/FLAMEGPU/FLAMEGPU2/issues/727
* Message output, agent output and/or agent death should all trigger compactions which might highlight this issues.
* Additionally other uses of CUDAScanCompaction in general should trigger this issue.
* @todo - similar tests may be useful for most / all features which can occur in streams
*/
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerAgentDeath, MessageNone, MessageNone) {
if (FLAMEGPU->getID() % 2) {
return flamegpu::DEAD;
}
return flamegpu::ALIVE;
}
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerAgentBirth, MessageNone, MessageNone) {
if (FLAMEGPU->getID() % 2) {
FLAMEGPU->agent_out.setVariable<unsigned int>("b", FLAMEGPU->getVariable<unsigned int>("b"));
}
return flamegpu::ALIVE;
}
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerBucketOut, MessageNone, MessageBucket) {
if (FLAMEGPU->getID() % 2) {
FLAMEGPU->message_out.setVariable<flamegpu::id_t>("id", FLAMEGPU->getID());
FLAMEGPU->message_out.setKey(FLAMEGPU->getVariable<unsigned int>("b"));
}
return flamegpu::ALIVE;
}
FLAMEGPU_AGENT_FUNCTION(VeryWideLayerBucketIn, MessageBucket, MessageNone) {
unsigned int c = 0;
for (auto &message : FLAMEGPU->message_in(FLAMEGPU->getVariable<unsigned int>("b"))) {
c++;
}
FLAMEGPU->setVariable<unsigned int>("c", c);
return flamegpu::ALIVE;
}
TEST(TestCUDASimulation, VeryWideLayerDeath) {
// The width of the model, i.e. the number of agent types
constexpr unsigned int MODEL_WIDTH = 129;
// How many individuals to place in each population. This should be irrelevant.
constexpr unsigned int POP_SIZE = 32;
// Create a model containing a very large number of different agent types, which all operate in the same layer.
ModelDescription model("VeryWideLayerDeath");
// Create the layer for all the agent functions to reside in
auto& layer = model.newLayer();
// Define many agent types
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
std::string agent_name("agent_" + std::to_string(idx));
std::string agent_function_name(agent_name + "_VeryWideLayerAgentDeath");
// Define the agent
AgentDescription& agent = model.newAgent(agent_name);
// Add a variable, to allow birth / bucket messaging
agent.newVariable<unsigned int>("b", 0);
// Define an agent function which allows death
auto& f = agent.newFunction(agent_function_name, VeryWideLayerAgentDeath);
f.setAllowAgentDeath(true);
// Add the function to the single model layer
layer.addAgentFunction(f);
}
// Create the simulation
CUDASimulation simulation(model);
// Populate the simulation
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
std::string agent_name("agent_" + std::to_string(idx));
AgentDescription& agent = model.Agent(agent_name);
AgentVector population(agent, POP_SIZE);
simulation.setPopulationData(population);
}
// Run a single simulation step.
simulation.SimulationConfig().steps = 1;
simulation.applyConfig();
EXPECT_NO_THROW(simulation.simulate());
// If this runs then the test can be assumed to have passed, i.e. an error did not occur.
}
TEST(TestCUDASimulation, VeryWideLayerBirth) {
// The width of the model, i.e. the number of agent types
constexpr unsigned int MODEL_WIDTH = 129;
// How many individuals to place in each population. This should be irrelevant.
constexpr unsigned int POP_SIZE = 32;
// Create a model containing a very large number of different agent types, which all operate in the same layer.
ModelDescription model("VeryWideLayerBirth");
// Create the layer for all the agent functions to reside in
auto& layer = model.newLayer();
// Define many agent types
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
std::string agent_name("agent_" + std::to_string(idx));
std::string agent_function_name(agent_name + "_VeryWideLayerAgentBirth");
// Define the agent
AgentDescription& agent = model.newAgent(agent_name);
// Add a variable, to allow birth / bucket messaging
agent.newVariable<unsigned int>("b", 0);
// Define an agent function which allows birth
auto& f = agent.newFunction(agent_function_name, VeryWideLayerAgentBirth);
f.setAgentOutput(agent);
// Add the function to the single model layer
layer.addAgentFunction(f);
}
// Create the simulation
CUDASimulation simulation(model);
// Populate the simulation
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
std::string agent_name("agent_" + std::to_string(idx));
AgentDescription& agent = model.Agent(agent_name);
AgentVector population(agent, POP_SIZE);
simulation.setPopulationData(population);
}
// Run a single simulation step.
simulation.SimulationConfig().steps = 1;
simulation.applyConfig();
EXPECT_NO_THROW(simulation.simulate());
// If this runs then the test can be assumed to have passed, i.e. an error did not occur.
}
TEST(TestCUDASimulation, VeryWideLayerBucketMessaging) {
// The width of the model, i.e. the number of agent types
constexpr unsigned int MODEL_WIDTH = 129;
// How many individuals to place in each population. This should be irrelevant.
constexpr unsigned int POP_SIZE = 32;
// How many buckets exist.
constexpr unsigned int BUCKET_COUNT = POP_SIZE / 2;
// Create a model containing a very large number of different agent types, which all operate in the same layer.
ModelDescription model("VeryWideLayerBucketMessaging");
// Create the layers for all the agent functions to reside in
auto& layer_out = model.newLayer();
auto& layer_in = model.newLayer();
// Define many agent types and message types.
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
std::string agent_name("agent_" + std::to_string(idx));
std::string agent_function_out_name(agent_name + "_VeryWideLayerBucketOut");
std::string agent_function_in_name(agent_name + "_VeryWideLayerBucketIn");
std::string message_name("message_" + std::to_string(idx));
// Define the message list
MessageBucket::Description &message = model.newMessage<MessageBucket>(message_name);
message.setBounds(0, BUCKET_COUNT);
message.newVariable<flamegpu::id_t>("id");
// Define the agent
AgentDescription& agent = model.newAgent(agent_name);
// Add a variable, to allow bucket messaging
agent.newVariable<unsigned int>("b", 0);
agent.newVariable<unsigned int>("c", 0);
// Define the message output and input agent functions
auto& f_out = agent.newFunction(agent_function_out_name, VeryWideLayerBucketOut);
f_out.setMessageOutput(message_name);
auto& f_in = agent.newFunction(agent_function_in_name, VeryWideLayerBucketIn);
f_in.setMessageInput(message_name);
// Add the function to the single model layer
layer_out.addAgentFunction(f_out);
layer_in.addAgentFunction(f_in);
}
// Create the simulation
CUDASimulation simulation(model);
// Populate the simulation
for (unsigned int idx = 0; idx < MODEL_WIDTH; idx++) {
std::string agent_name("agent_" + std::to_string(idx));
AgentDescription& agent = model.Agent(agent_name);
AgentVector population(agent, POP_SIZE);
for (auto instance : population) {
instance.setVariable<unsigned int>("b", instance.getID() / BUCKET_COUNT);
instance.setVariable<unsigned int>("c", 0);
}
simulation.setPopulationData(population);
}
// Run a single simulation step.
simulation.SimulationConfig().steps = 1;
simulation.applyConfig();
EXPECT_NO_THROW(simulation.simulate());
// If this runs then the test can be assumed to have passed, i.e. an error did not occur.
}

} // namespace test_cuda_simulation
} // namespace tests
} // namespace flamegpu