GeNN  4.9.0
GPU enhanced Neuronal Networks (GeNN)
generateRun.h
Go to the documentation of this file.
1 #pragma once
2 
3 // Standard C++ includes
4 #include <algorithm>
5 #include <fstream>
6 #include <iostream>
7 #include <string>
8 #include <thread>
9 
10 // Standard C includes
11 #include <cstdlib>
12 
13 
14 // Windows includes
15 #ifdef _WIN32
16 #include <direct.h>
17 #endif
18 
19 // CLI11 includes
20 #include "../../include/genn/third_party/CLI11.hpp"
21 
22 //------------------------------------------------------------------------
23 // GenerateRunBase
24 //------------------------------------------------------------------------
26 {
27 public:
28  GenerateRunBase(const std::string &projectName)
29  : m_App{"Generate run application for '" + projectName + "' user project"},
30  m_Debug(false), m_CPUOnly(false), m_Timing(false), m_ScalarType("float"), m_GPUDevice(-1), m_ProjectName(projectName)
31  {
32  m_App.add_flag("--debug", m_Debug, "Whether to run in a debugger");
33  auto *cpuOnly = m_App.add_flag("--cpu-only", m_CPUOnly, "Whether to build against single-threaded CPU backend");
34  m_App.add_flag("--timing", m_Timing, "Whether to use GeNN's timing mechanism to measure performance");
35  m_App.add_set("--ftype", m_ScalarType, {"float", "double"}, "What floating point type to use", true);
36  m_App.add_option("--gpu-device", m_GPUDevice, "What GPU device ID to use (-1 = select automatically)", true)->excludes(cpuOnly);
37  m_App.add_option("experimentName", m_ExperimentName, "Experiment name")->required();
38  }
39 
40  //------------------------------------------------------------------------
41  // Declared virtuals
42  //------------------------------------------------------------------------
43  // Write sizes header - override to write extra parameters - remember to call superclass first!
44  virtual void writeSizes(std::ofstream &sizes) const
45  {
46  std::string upperCaseScalarType;
47  std::transform(m_ScalarType.begin(), m_ScalarType.end(), std::back_inserter(upperCaseScalarType), ::toupper);
48 
49  sizes << "#pragma once" << std::endl;
50  sizes << "#define _FTYPE " << "GENN_" << upperCaseScalarType << std::endl;
51  sizes << "#define _TIMING " << m_Timing << std::endl;
52 
53  if(m_GPUDevice != -1) {
54  sizes << "#define _GPU_DEVICE " << m_GPUDevice << std::endl;
55  }
56  }
57 
58  //------------------------------------------------------------------------
59  // Public API
60  //------------------------------------------------------------------------
61  int getExitCode(const CLI::ParseError &e) {
62  return m_App.exit(e);
63  }
64 
65  void parseCommandLine(int argc, char **argv)
66  {
67  m_App.parse(argc, argv);
68  }
69 
70  int buildAndRun(std::initializer_list<std::string> runParams = {}) const
71  {
72  // create output directory
73 #ifdef _WIN32
74  _mkdir(getOutDir().c_str());
75 #else // UNIX
76  if (mkdir(getOutDir().c_str(), S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
77  std::cerr << "Directory cannot be created. It may exist already." << std::endl;
78  }
79 #endif
80 
81  // Run any additional tools
82  const int runToolsRetVal = runTools();
83  if(runToolsRetVal != EXIT_SUCCESS) {
84  return runToolsRetVal;
85  }
86 
87  // build it
88 #ifdef _WIN32
89  const std::string buildCmd = getBuildCommandWindows();
90 #else // UNIX
91  const std::string buildCmd = getBuildCommandUnix();
92 #endif
93 
94  const int buildRetVal = system(buildCmd.c_str());
95  if (buildRetVal != 0){
96  std::cerr << "ERROR: Following call failed with status " << buildRetVal << ":" << std::endl << buildCmd << std::endl;
97  std::cerr << "Exiting..." << std::endl;
98  return EXIT_FAILURE;
99  }
100 
101  // run it!
102  std::cout << "running test..." << std::endl;
103 #ifdef _WIN32
104  std::string runCmd = getRunCommandWindows();
105 #else // UNIX
106  std::string runCmd = getRunCommandUnix();
107 #endif
108  // Add out directory parameter
109  runCmd += (" " + m_ExperimentName);
110 
111  // Add additional parameters
112  for(const auto &p: runParams) {
113  runCmd += (" " + p);
114  }
115 
116  const int runRetVal = system(runCmd.c_str());
117  if (runRetVal != 0){
118  std::cerr << "ERROR: Following call failed with status " << runRetVal << ":" << std::endl << runCmd << std::endl;
119  std::cerr << "Exiting..." << std::endl;
120  return EXIT_FAILURE;
121  }
122 
123  return EXIT_SUCCESS;
124  }
125 
126 protected:
127  //------------------------------------------------------------------------
128  // Declared virtuals
129  //------------------------------------------------------------------------
130  virtual int runTools() const
131  {
132  return EXIT_SUCCESS;
133  }
134 
135  //------------------------------------------------------------------------
136  // Protected API
137  //------------------------------------------------------------------------
138  CLI::App &getApp(){ return m_App; }
139 
140  std::string getOutDir() const{ return m_ExperimentName + "_output"; }
141  const std::string &getExperimentName() const{ return m_ExperimentName; }
142 
143 private:
144  //------------------------------------------------------------------------
145  // Private methods
146  //------------------------------------------------------------------------
147  std::string getBuildCommandUnix() const
148  {
149  std::string cmd = "cd model && genn-buildmodel.sh ";
150  cmd += m_ProjectName + ".cc";
151  if (m_Debug) {
152  cmd += " -d";
153  }
154  if (m_CPUOnly) {
155  cmd += " -c";
156  }
157 
158  const unsigned int numThreads = std::thread::hardware_concurrency();
159  cmd += " && make -j " + std::to_string(numThreads) + " clean all";
160  const int retval = system(cmd.c_str());
161  if (retval != 0){
162  std::cerr << "ERROR: Following call failed with status " << retval << ":" << std::endl << cmd << std::endl;
163  std::cerr << "Exiting..." << std::endl;
164  exit(1);
165  }
166 
167  return cmd;
168  }
169 
170  std::string getBuildCommandWindows() const
171  {
172  std::string cmd = "cd model && genn-buildmodel.bat ";
173  cmd += m_ProjectName + ".cc";
174  if (m_Debug) {
175  cmd += " -d";
176  }
177  if (m_CPUOnly) {
178  cmd += " -c";
179  }
180 
181  cmd += " && msbuild /verbosity:minimal /m " + m_ProjectName + ".sln /t:" + m_ProjectName + " /p:Configuration=";
182  if (m_Debug) {
183  cmd += "Debug";
184  }
185  else {
186  cmd += "Release";
187  }
188  return cmd;
189  }
190 
191  std::string getRunCommandUnix() const
192  {
193  // **TODO** cpu-only debugger
194  if (m_Debug) {
195  return "cd model && gdb -tui --args " + m_ProjectName;
196  }
197  else {
198  return "cd model && ./" + m_ProjectName;
199  }
200  }
201 
202  std::string getRunCommandWindows() const
203  {
204  if (m_Debug) {
205  return "cd model && devenv /debugexe " + m_ProjectName + "_Debug.exe";
206  }
207  else {
208  return "cd model && " + m_ProjectName + "_Release.exe";
209  }
210  }
211 
212  //------------------------------------------------------------------------
213  // Members
214  //------------------------------------------------------------------------
215  CLI::App m_App;
216  bool m_Debug;
217  bool m_CPUOnly;
218  bool m_Timing;
219  std::string m_ScalarType;
220  int m_GPUDevice;
221  std::string m_ExperimentName;
222  const std::string m_ProjectName;
223 };
int buildAndRun(std::initializer_list< std::string > runParams={}) const
Definition: generateRun.h:70
int getExitCode(const CLI::ParseError &e)
Definition: generateRun.h:61
virtual void writeSizes(std::ofstream &sizes) const
Definition: generateRun.h:44
GenerateRunBase(const std::string &projectName)
Definition: generateRun.h:28
virtual int runTools() const
Definition: generateRun.h:130
CLI::App & getApp()
Definition: generateRun.h:138
const std::string & getExperimentName() const
Definition: generateRun.h:141
Definition: generateRun.h:25
void parseCommandLine(int argc, char **argv)
Definition: generateRun.h:65
std::string getOutDir() const
Definition: generateRun.h:140