pelib
2.0.0
|
00001 /* 00002 Copyright 2015 Nicolas Melot, Johan Janzén 00003 00004 This file is part of Pelib. 00005 00006 Pelib is free software: you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation, either version 3 of the License, or 00009 (at your option) any later version. 00010 00011 Pelib is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with Pelib. If not, see <http://www.gnu.org/licenses/>. 00018 */ 00019 00020 00021 #include <libxml++/libxml++.h> 00022 #include <exception> 00023 #include <vector> 00024 #include <sstream> 00025 #include <cstdlib> 00026 #include <boost/algorithm/string.hpp> 00027 00028 #include <signal.h> 00029 #include <pthread.h> 00030 extern "C"{ 00031 #include <igraph.h> 00032 } 00033 00034 #include <pelib/Taskgraph.hpp> 00035 #include <pelib/GraphML.hpp> 00036 00037 #include <pelib/Scalar.hpp> 00038 #include <pelib/Vector.hpp> 00039 #include <pelib/Matrix.hpp> 00040 #include <pelib/Set.hpp> 00041 #include <pelib/Task.hpp> 00042 #include <pelib/Link.hpp> 00043 00044 #include <pelib/CastException.hpp> 00045 #include <pelib/ParseException.hpp> 00046 00047 #ifndef debug 00048 #define debug(expr) cout << "[" << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << "] " << #expr << " = \"" << expr << "\"." << endl; 00049 #endif 00050 00051 using namespace pelib; 00052 using namespace std; 00053 using namespace xmlpp; 00054 00055 struct reader_args 00056 { 00057 FILE *instream; 00058 ostream *o; 00059 }; 00060 typedef struct reader_args reader_args_t; 00061 00062 const string GraphML::producerName = "producer_name"; 00063 const string GraphML::consumerName = "consumer_name"; 00064 const string GraphML::type = "type"; 00065 const string GraphML::producer_rate = "producer_rate"; 00066 const string GraphML::consumer_rate = "consumer_rate"; 00067 00068 static void* 00069 thread_reader(void* aux) 00070 { 00071 reader_args_t *args = (reader_args_t*)aux; 00072 00073 char c; 00074 while ((c = fgetc (args->instream)) != EOF) 00075 { 00076 (*args->o) << c; 00077 } 00078 00079 fclose(args->instream); 00080 return NULL; 00081 } 00082 00083 struct writer_args 00084 { 00085 int file_descr; 00086 istream *i; 00087 }; 00088 typedef struct writer_args writer_args_t; 00089 00090 static void* 00091 thread_writer(void* aux) 00092 { 00093 writer_args_t *args = (writer_args_t*)aux; 00094 00095 char c = args->i->get(); 00096 while(!args->i->eof()) 00097 { 00098 size_t ans = write(args->file_descr, &c, 1); 00099 00100 if(ans < 0) 00101 { 00102 throw runtime_error("Error: Pipe has been possessed X-("); 00103 } 00104 00105 c = args->i->get(); 00106 } 00107 00108 close(args->file_descr); 00109 return NULL; 00110 } 00111 00112 GraphML::~GraphML() 00113 { 00114 // Do nothing 00115 } 00116 00117 void 00118 GraphML::dump(ostream& os, const Taskgraph *data, const Platform *arch) const 00119 { 00120 const Taskgraph *tg = dynamic_cast<const Taskgraph* >(data); 00121 if(tg == NULL) throw CastException("Parameter \"data\" was not of type \"Taskgraph*\"."); 00122 00123 igraph_t *graph = new igraph_t(); 00124 igraph_i_set_attribute_table(&igraph_cattribute_table); //do this to enable attribute fetching 00125 00126 /* 00127 igraph_vector_init(tg, 8); 00128 00129 VECTOR(v1)[2]=1; VECTOR(v1)[3]=2; 00130 VECTOR(v1)[4]=2; VECTOR(v1)[5]=3; 00131 VECTOR(v1)[6]=2; VECTOR(v1)[7]=2; 00132 */ 00133 igraph_vector_t edges; 00134 igraph_vector_init(&edges, 0); 00135 //VECTOR(edges)[0]=0; VECTOR(edges)[1]=1; 00136 igraph_create(graph, &edges, 0, true); 00137 00138 // Add graph attributes 00139 SETGAS(graph,"name", tg->getName().c_str()); 00140 SETGAS(graph,"deadline", tg->getDeadlineCalculator().c_str()); 00141 00142 // Add vertices 00143 int ret = igraph_add_vertices(graph, tg->getTasks().size(), 0); 00144 if(ret == IGRAPH_EINVAL) throw CastException("Could not add vertices to igraph."); 00145 if(arch != NULL && !(arch->isHomogeneous())) 00146 { 00147 throw CastException("Cannot output discretive efficiency function for a heterogeneous platform."); 00148 } 00149 00150 size_t counter = 0; 00151 for(set<Task>::const_iterator i = tg->getTasks().begin(); i != tg->getTasks().end(); i++, counter++) 00152 { 00153 Task task = *i; 00154 00155 SETVAS(graph, "name", counter, task.getName().c_str()); 00156 SETVAS(graph, "module", counter, task.getModule().c_str()); 00157 SETVAN(graph, "workload", counter, task.getWorkload()); 00158 SETVAN(graph, "streaming", counter, task.isStreaming()); 00159 stringstream max_width; 00160 00161 // If no platform is provided, just dump efficiency and max width as is. 00162 // Otherwise, compute and output each possible value one by one, and 00163 // replace any larger value of max_width than number of core, by number of cores 00164 if (arch == NULL) 00165 { 00166 SETVAS(graph, "efficiency", counter, task.getEfficiencyString().c_str()); 00167 max_width << task.getMaxWidth(); 00168 } 00169 else 00170 { 00171 stringstream ss; 00172 for(size_t j = 1; j <= arch->getCores().size(); j++) 00173 { 00174 ss << task.getEfficiency(j) << " "; 00175 } 00176 00177 SETVAS(graph, "efficiency", counter, ss.str().c_str()); 00178 if(task.getMaxWidth() >= arch->getCores().size()) 00179 { 00180 max_width << arch->getCores().size(); 00181 } 00182 else 00183 { 00184 max_width << task.getMaxWidth(); 00185 } 00186 } 00187 00188 SETVAS(graph, "max_width", counter, max_width.str().c_str()); 00189 } 00190 00191 counter = 0; 00192 for(set<Link>::const_iterator i = tg->getLinks().begin(); i != tg->getLinks().end(); i++, counter++) 00193 { 00194 int ret = igraph_add_edge(graph, std::distance(tg->getTasks().begin(), tg->getTasks().find(*i->getProducer())), std::distance(tg->getTasks().begin(), tg->getTasks().find(*i->getConsumer()))); 00195 if(ret == IGRAPH_EINVAL) throw CastException("Could not add vertices to igraph."); 00196 SETEAS(graph, producerName.c_str(), counter, i->getProducerName().c_str()); 00197 SETEAS(graph, consumerName.c_str(), counter, i->getConsumerName().c_str()); 00198 if(i->getDataType().compare(string()) != 0) 00199 { 00200 SETEAS(graph, type.c_str(), counter, i->getDataType().c_str()); 00201 } 00202 if(i->getProducerRate() > 0) 00203 { 00204 SETEAN(graph, GraphML::producer_rate.c_str(), counter, i->getProducerRate()); 00205 } 00206 if(i->getConsumerRate() > 0) 00207 { 00208 SETEAN(graph, GraphML::consumer_rate.c_str(), counter, i->getConsumerRate()); 00209 } 00210 } 00211 00212 // Dump data to stream os 00213 int p[2]; 00214 int ans = pipe(p); 00215 if(ans) 00216 { 00217 throw runtime_error("Error: no pipe :/"); 00218 } 00219 00220 FILE *fake_fileptr = fdopen(p[1], "w"); 00221 FILE *instream = fdopen (p[0], "r"); 00222 00223 // Spawn a thread that reads from the pipe as fast as it can. 00224 // This is required if igraph indend to output massive amounts of data 00225 pthread_attr_t attr; 00226 pthread_t thread; 00227 reader_args_t args; 00228 args.instream = instream; 00229 args.o = &os; 00230 00231 pthread_attr_init(&attr); 00232 pthread_create(&thread, &attr, &thread_reader, (void*) &args); 00233 00234 igraph_write_graph_graphml(graph, fake_fileptr, true); 00235 fclose(fake_fileptr); 00236 00237 pthread_join(thread, NULL); 00238 //fclose (instream); 00239 close(p[0]); 00240 00241 igraph_destroy(graph); 00242 delete graph; 00243 } 00244 00245 void 00246 GraphML::dump(ostream& os, const Taskgraph *data) const 00247 { 00248 dump(os, data, NULL); 00249 } 00250 00251 void 00252 GraphML::dump(ostream& os, const Taskgraph &data) const 00253 { 00254 dump(os, &data, NULL); 00255 } 00256 00257 void 00258 GraphML::dump(ostream& os, const Taskgraph &data, const Platform &arch) const 00259 { 00260 dump(os, &data, &arch); 00261 } 00262 00263 Taskgraph* 00264 GraphML::parse(istream &is) const 00265 { 00266 // Open the file and get an igraph record 00267 // Initialize igraph and build a new object 00268 igraph_i_set_attribute_table(&igraph_cattribute_table); //do this to enable attribute fetching 00269 igraph_t *the_graph = new igraph_t(); 00270 00271 // Create a FILE* from istream by building a posix pipe. This wont work in windows... 00272 int p[2]; 00273 int ans = pipe(p); 00274 if(ans) 00275 { 00276 throw runtime_error("Error: no pipe :/"); 00277 } 00278 00279 FILE *fake_fileptr = fdopen(p[0], "r"); 00280 00281 pthread_attr_t attr; 00282 pthread_t thread; 00283 writer_args_t args; 00284 args.file_descr = p[1]; 00285 args.i = &is; 00286 00287 pthread_attr_init(&attr); 00288 pthread_create(&thread, &attr, &thread_writer, (void*) &args); 00289 00290 // char ch; 00291 // while( ( ch = fgetc(fake_fileptr) ) != EOF ) 00292 // printf("%c",ch); 00293 // Parse input file 00294 igraph_read_graph_graphml(the_graph, fake_fileptr, 0); 00295 00296 // Clone the file and wait for the pipe to finish 00297 close(p[0]); 00298 pthread_join(thread, NULL); 00299 00300 set<Task> tasks; 00301 for(int id = 0; id < igraph_vcount(the_graph); id++) 00302 { 00303 stringstream estr; 00304 estr << "task_" << id; 00305 bool streaming = true; 00306 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_VERTEX, "streaming")) 00307 { 00308 streaming = (bool)VAN(the_graph, "streaming", id); 00309 } 00310 Task task(strcmp(VAS(the_graph, "name", id), "") != 0 ? VAS(the_graph, "name", id) : estr.str(), streaming); 00311 task.setModule(strcmp(VAS(the_graph, "module", id),"") != 0 ? VAS(the_graph, "module", id) : "dummy"); 00312 task.setWorkload(!isnan((float)VAN(the_graph, "workload", id)) ? VAN(the_graph, "workload", id): 1.0); 00313 00314 const char *str = VAS(the_graph, "max_width", id); 00315 string max_width_str(str); 00316 boost::algorithm::to_lower(max_width_str); 00317 boost::algorithm::trim(max_width_str); 00318 float max_width; 00319 00320 if(max_width_str.compare("inf") == 0 || max_width_str.compare("+inf") == 0) 00321 { 00322 max_width = std::numeric_limits<float>::infinity(); 00323 } 00324 else 00325 { 00326 if(max_width_str.compare("-inf") == 0) 00327 { 00328 max_width = 1; 00329 } 00330 else 00331 { 00332 std::istringstream converter(max_width_str); 00333 converter >> max_width; 00334 00335 if(converter.fail()) 00336 { 00337 throw ParseException(std::string("Couln't convert literal \"").append(max_width_str).append("\" into type \"").append(typeid(max_width).name()).append("\".")); 00338 } 00339 } 00340 } 00341 task.setMaxWidth(max_width); 00342 //task.setMaxWidth((int)VAN(the_graph, "max_width", id) != INT_MIN ? VAN(the_graph, "max_width", id) : 1); 00343 00344 if (strcmp(VAS(the_graph, "efficiency", id), "") != 0) 00345 { 00346 task.setEfficiencyString(VAS(the_graph, "efficiency", id)); 00347 } 00348 else 00349 { 00350 stringstream ss; 00351 ss << task.getMaxWidth(); 00352 task.setEfficiencyString(string("exprtk:p <= ") + ss.str() + "? 1 : 1e-6"); 00353 } 00354 00355 tasks.insert(Task(task)); 00356 } 00357 00358 // Add edges between tasks 00359 set<Link> links; 00360 for(int i = 0; i < igraph_ecount(the_graph); i++) 00361 { 00362 //printf("[%s:%s:%d] Edge number %d.\n", __FILE__, __FUNCTION__, __LINE__, i); 00363 int producer_id, consumer_id; 00364 00365 igraph_edge(the_graph, i, &producer_id, &consumer_id); 00366 Task producer(VAS(the_graph, "name", producer_id)); 00367 Task consumer(VAS(the_graph, "name", consumer_id)); 00368 string producerName = string(EAS(the_graph, GraphML::producerName.c_str(), i)); 00369 string consumerName = string(EAS(the_graph, GraphML::consumerName.c_str(), i)); 00370 string type(""); 00371 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_EDGE, GraphML::type.c_str())) 00372 { 00373 type = string(EAS(the_graph, GraphML::type.c_str(), i)); 00374 } 00375 size_t producer_rate = 0, consumer_rate = 0; 00376 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_EDGE, GraphML::producer_rate.c_str())) 00377 { 00378 producer_rate = EAN(the_graph, GraphML::producer_rate.c_str(), i); 00379 } 00380 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_EDGE, GraphML::consumer_rate.c_str())) 00381 { 00382 consumer_rate = EAN(the_graph, GraphML::consumer_rate.c_str(), i); 00383 } 00384 00385 Link link(*tasks.find(producer), *tasks.find(consumer), producerName, consumerName, type, producer_rate, consumer_rate); 00386 links.insert(link); 00387 00388 const Link &link_ref = *links.find(link); 00389 00390 // Add the link as producer and consumer links of both endpoint tasks 00391 Task &producer_ref = (Task&)*tasks.find(producer); 00392 producer_ref.getConsumers().insert(&link_ref); 00393 Task &consumer_ref = (Task&)*tasks.find(consumer); 00394 consumer_ref.getProducers().insert(&link_ref); 00395 00396 /* 00397 trace(producer_ref.getName()); 00398 trace(consumer_ref.getName()); 00399 trace(producer_ref.getConsumers().size()); 00400 trace(consumer_ref.getConsumers().size()); 00401 trace(producer_ref.getProducers().size()); 00402 trace(consumer_ref.getProducers().size()); 00403 */ 00404 } 00405 00406 // Finally build the taskgraph 00407 Taskgraph *tg = new Taskgraph(tasks, links); 00408 tg->setName(GAS(the_graph, "name")); 00409 tg->setDeadlineCalculator(GAS(the_graph, "deadline")); 00410 00411 // Cleanup 00412 igraph_destroy(the_graph); 00413 delete the_graph; 00414 00415 return tg; 00416 } 00417 00418 GraphML* 00419 GraphML::clone() const 00420 { 00421 return new GraphML(); 00422 }