| 1 | /*
 | 
| 2 |  * Souffle - A Datalog Compiler
 | 
| 3 |  * Copyright (c) 2016, The Souffle Developers. All rights reserved
 | 
| 4 |  * Licensed under the Universal Permissive License v 1.0 as shown at:
 | 
| 5 |  * - https://opensource.org/licenses/UPL
 | 
| 6 |  * - <souffle root>/licenses/SOUFFLE-UPL.txt
 | 
| 7 |  */
 | 
| 8 | 
 | 
| 9 | #pragma once
 | 
| 10 | 
 | 
| 11 | #include "souffle/profile/Iteration.h"
 | 
| 12 | #include "souffle/profile/ProfileDatabase.h"
 | 
| 13 | #include "souffle/profile/ProfileEvent.h"
 | 
| 14 | #include "souffle/profile/ProgramRun.h"
 | 
| 15 | #include "souffle/profile/Relation.h"
 | 
| 16 | #include "souffle/profile/Rule.h"
 | 
| 17 | #include "souffle/profile/StringUtils.h"
 | 
| 18 | #include <cassert>
 | 
| 19 | #include <chrono>
 | 
| 20 | #include <cstdlib>
 | 
| 21 | #include <ctime>
 | 
| 22 | #include <fstream>
 | 
| 23 | #include <iostream>
 | 
| 24 | #include <memory>
 | 
| 25 | #include <string>
 | 
| 26 | #include <thread>
 | 
| 27 | #include <unordered_map>
 | 
| 28 | #include <utility>
 | 
| 29 | #include <vector>
 | 
| 30 | #ifndef _MSC_VER
 | 
| 31 | #include <dirent.h>
 | 
| 32 | #endif
 | 
| 33 | #include <sys/stat.h>
 | 
| 34 | 
 | 
| 35 | namespace souffle {
 | 
| 36 | namespace profile {
 | 
| 37 | 
 | 
| 38 | namespace {
 | 
| 39 | template <typename T>
 | 
| 40 | class DSNVisitor : public Visitor {
 | 
| 41 | public:
 | 
| 42 |     DSNVisitor(T& base) : base(base) {}
 | 
| 43 |     void visit(TextEntry& text) override {
 | 
| 44 |         if (text.getKey() == "source-locator") {
 | 
| 45 |             base.setLocator(text.getText());
 | 
| 46 |         }
 | 
| 47 |     }
 | 
| 48 |     void visit(DurationEntry& duration) override {
 | 
| 49 |         if (duration.getKey() == "runtime") {
 | 
| 50 |             base.setStarttime(duration.getStart());
 | 
| 51 |             base.setEndtime(duration.getEnd());
 | 
| 52 |         }
 | 
| 53 |     }
 | 
| 54 |     void visit(SizeEntry& size) override {
 | 
| 55 |         if (size.getKey() == "num-tuples") {
 | 
| 56 |             base.setNumTuples(size.getSize());
 | 
| 57 |         }
 | 
| 58 |     }
 | 
| 59 |     void visit(DirectoryEntry& /* ruleEntry */) override {}
 | 
| 60 | 
 | 
| 61 | protected:
 | 
| 62 |     T& base;
 | 
| 63 | };
 | 
| 64 | 
 | 
| 65 | /**
 | 
| 66 |  * Visit ProfileDB atom frequencies.
 | 
| 67 |  * atomrule : {atom: {num-tuples: num}}
 | 
| 68 |  */
 | 
| 69 | class AtomFrequenciesVisitor : public Visitor {
 | 
| 70 | public:
 | 
| 71 |     AtomFrequenciesVisitor(Rule& rule) : rule(rule) {}
 | 
| 72 |     void visit(DirectoryEntry& directory) override {
 | 
| 73 |         const std::string& clause = directory.getKey();
 | 
| 74 | 
 | 
| 75 |         for (auto& key : directory.getKeys()) {
 | 
| 76 |             auto* level = as<SizeEntry>(directory.readDirectoryEntry(key)->readEntry("level"));
 | 
| 77 |             auto* frequency = as<SizeEntry>(directory.readDirectoryEntry(key)->readEntry("num-tuples"));
 | 
| 78 |             // Handle older logs
 | 
| 79 |             std::size_t intFreq = frequency == nullptr ? 0 : frequency->getSize();
 | 
| 80 |             std::size_t intLevel = level == nullptr ? 0 : level->getSize();
 | 
| 81 |             rule.addAtomFrequency(clause, key, intLevel, intFreq);
 | 
| 82 |         }
 | 
| 83 |     }
 | 
| 84 | 
 | 
| 85 | private:
 | 
| 86 |     Rule& rule;
 | 
| 87 | };
 | 
| 88 | 
 | 
| 89 | /**
 | 
| 90 |  * Visit ProfileDB recursive rule.
 | 
| 91 |  * ruleversion: {DSN}
 | 
| 92 |  */
 | 
| 93 | class RecursiveRuleVisitor : public DSNVisitor<Rule> {
 | 
| 94 | public:
 | 
| 95 |     RecursiveRuleVisitor(Rule& rule) : DSNVisitor(rule) {}
 | 
| 96 |     void visit(DirectoryEntry& directory) override {
 | 
| 97 |         if (directory.getKey() == "atom-frequency") {
 | 
| 98 |             AtomFrequenciesVisitor atomFrequenciesVisitor(base);
 | 
| 99 |             for (auto& key : directory.getKeys()) {
 | 
| 100 |                 directory.readDirectoryEntry(key)->accept(atomFrequenciesVisitor);
 | 
| 101 |             }
 | 
| 102 |         }
 | 
| 103 |     }
 | 
| 104 | };
 | 
| 105 | 
 | 
| 106 | /**
 | 
| 107 |  * Visit ProfileDB non-recursive rules.
 | 
| 108 |  * rule: {versionNum : {DSN}, versionNum+1: {DSN}}
 | 
| 109 |  */
 | 
| 110 | class RecursiveRulesVisitor : public Visitor {
 | 
| 111 | public:
 | 
| 112 |     RecursiveRulesVisitor(Iteration& iteration, Relation& relation)
 | 
| 113 |             : iteration(iteration), relation(relation) {}
 | 
| 114 |     void visit(DirectoryEntry& ruleEntry) override {
 | 
| 115 |         for (const auto& key : ruleEntry.getKeys()) {
 | 
| 116 |             auto& versions = *ruleEntry.readDirectoryEntry(key);
 | 
| 117 |             auto rule = std::make_shared<Rule>(
 | 
| 118 |                     ruleEntry.getKey(), std::stoi(key), relation.createRecID(ruleEntry.getKey()));
 | 
| 119 |             RecursiveRuleVisitor visitor(*rule);
 | 
| 120 |             for (const auto& versionKey : versions.getKeys()) {
 | 
| 121 |                 versions.readEntry(versionKey)->accept(visitor);
 | 
| 122 |             }
 | 
| 123 |             // To match map keys defined in Iteration::addRule()
 | 
| 124 |             std::string ruleKey = key + rule->getLocator() + key;
 | 
| 125 |             iteration.addRule(ruleKey, rule);
 | 
| 126 |         }
 | 
| 127 |     }
 | 
| 128 | 
 | 
| 129 | protected:
 | 
| 130 |     Iteration& iteration;
 | 
| 131 |     Relation& relation;
 | 
| 132 | };
 | 
| 133 | 
 | 
| 134 | /**
 | 
| 135 |  * Visit ProfileDB non-recursive rule.
 | 
| 136 |  * rule: {DSN}
 | 
| 137 |  */
 | 
| 138 | class NonRecursiveRuleVisitor : public DSNVisitor<Rule> {
 | 
| 139 | public:
 | 
| 140 |     NonRecursiveRuleVisitor(Rule& rule) : DSNVisitor(rule) {}
 | 
| 141 |     void visit(DirectoryEntry& directory) override {
 | 
| 142 |         if (directory.getKey() == "atom-frequency") {
 | 
| 143 |             AtomFrequenciesVisitor atomFrequenciesVisitor(base);
 | 
| 144 |             for (auto& key : directory.getKeys()) {
 | 
| 145 |                 directory.readDirectoryEntry(key)->accept(atomFrequenciesVisitor);
 | 
| 146 |             }
 | 
| 147 |         }
 | 
| 148 |     }
 | 
| 149 | };
 | 
| 150 | 
 | 
| 151 | /**
 | 
| 152 |  * Visit ProfileDB non-recursive rules.
 | 
| 153 |  * non-recursive-rule: {rule1: {DSN}, ...}
 | 
| 154 |  */
 | 
| 155 | class NonRecursiveRulesVisitor : public Visitor {
 | 
| 156 | public:
 | 
| 157 |     NonRecursiveRulesVisitor(Relation& relation) : relation(relation) {}
 | 
| 158 |     void visit(DirectoryEntry& ruleEntry) override {
 | 
| 159 |         auto rule = std::make_shared<Rule>(ruleEntry.getKey(), relation.createID());
 | 
| 160 |         NonRecursiveRuleVisitor visitor(*rule);
 | 
| 161 |         for (const auto& key : ruleEntry.getKeys()) {
 | 
| 162 |             ruleEntry.readEntry(key)->accept(visitor);
 | 
| 163 |         }
 | 
| 164 |         relation.addRule(rule);
 | 
| 165 |     }
 | 
| 166 | 
 | 
| 167 | protected:
 | 
| 168 |     Relation& relation;
 | 
| 169 | };
 | 
| 170 | 
 | 
| 171 | /**
 | 
| 172 |  * Visit a ProfileDB relation iteration.
 | 
| 173 |  * iterationNumber: {DSN, recursive-rule: {}}
 | 
| 174 |  */
 | 
| 175 | class IterationVisitor : public DSNVisitor<Iteration> {
 | 
| 176 | public:
 | 
| 177 |     IterationVisitor(Iteration& iteration, Relation& relation) : DSNVisitor(iteration), relation(relation) {}
 | 
| 178 |     void visit(DurationEntry& duration) override {
 | 
| 179 |         if (duration.getKey() == "copytime") {
 | 
| 180 |             auto copytime = (duration.getEnd() - duration.getStart());
 | 
| 181 |             base.setCopytime(copytime);
 | 
| 182 |         }
 | 
| 183 |         DSNVisitor::visit(duration);
 | 
| 184 |     }
 | 
| 185 |     void visit(DirectoryEntry& directory) override {
 | 
| 186 |         if (directory.getKey() == "recursive-rule") {
 | 
| 187 |             RecursiveRulesVisitor rulesVisitor(base, relation);
 | 
| 188 |             for (const auto& key : directory.getKeys()) {
 | 
| 189 |                 directory.readEntry(key)->accept(rulesVisitor);
 | 
| 190 |             }
 | 
| 191 |         }
 | 
| 192 |         if (directory.getKey() == "maxRSS") {
 | 
| 193 |             auto* preMaxRSS = as<SizeEntry>(directory.readEntry("pre"));
 | 
| 194 |             auto* postMaxRSS = as<SizeEntry>(directory.readEntry("post"));
 | 
| 195 |             relation.setPreMaxRSS(preMaxRSS->getSize());
 | 
| 196 |             relation.setPostMaxRSS(postMaxRSS->getSize());
 | 
| 197 |         }
 | 
| 198 |     }
 | 
| 199 | 
 | 
| 200 | protected:
 | 
| 201 |     Relation& relation;
 | 
| 202 | };
 | 
| 203 | 
 | 
| 204 | /**
 | 
| 205 |  * Visit ProfileDB iterations.
 | 
| 206 |  * iteration: {num: {}, num2: {}, ...}
 | 
| 207 |  */
 | 
| 208 | class IterationsVisitor : public Visitor {
 | 
| 209 | public:
 | 
| 210 |     IterationsVisitor(Relation& relation) : relation(relation) {}
 | 
| 211 |     void visit(DirectoryEntry& ruleEntry) override {
 | 
| 212 |         auto iteration = std::make_shared<Iteration>();
 | 
| 213 |         relation.addIteration(iteration);
 | 
| 214 |         IterationVisitor visitor(*iteration, relation);
 | 
| 215 |         for (const auto& key : ruleEntry.getKeys()) {
 | 
| 216 |             ruleEntry.readEntry(key)->accept(visitor);
 | 
| 217 |         }
 | 
| 218 |     }
 | 
| 219 | 
 | 
| 220 | protected:
 | 
| 221 |     Relation& relation;
 | 
| 222 | };
 | 
| 223 | 
 | 
| 224 | /**
 | 
| 225 |  * Visit ProfileDB relations.
 | 
| 226 |  * relname: {DSN, non-recursive-rule: {}, iteration: {...}}
 | 
| 227 |  */
 | 
| 228 | class RelationVisitor : public DSNVisitor<Relation> {
 | 
| 229 | public:
 | 
| 230 |     RelationVisitor(Relation& relation) : DSNVisitor(relation) {}
 | 
| 231 |     void visit(DurationEntry& duration) override {
 | 
| 232 |         if (duration.getKey() == "loadtime") {
 | 
| 233 |             base.setLoadtime(duration.getStart(), duration.getEnd());
 | 
| 234 |         } else if (duration.getKey() == "savetime") {
 | 
| 235 |             auto savetime = (duration.getEnd() - duration.getStart());
 | 
| 236 |             base.setSavetime(savetime);
 | 
| 237 |         }
 | 
| 238 |         DSNVisitor::visit(duration);
 | 
| 239 |     }
 | 
| 240 |     void visit(DirectoryEntry& directory) override {
 | 
| 241 |         if (directory.getKey() == "iteration") {
 | 
| 242 |             IterationsVisitor iterationsVisitor(base);
 | 
| 243 |             for (const auto& key : directory.getKeys()) {
 | 
| 244 |                 directory.readEntry(key)->accept(iterationsVisitor);
 | 
| 245 |             }
 | 
| 246 |         } else if (directory.getKey() == "non-recursive-rule") {
 | 
| 247 |             NonRecursiveRulesVisitor rulesVisitor(base);
 | 
| 248 |             for (const auto& key : directory.getKeys()) {
 | 
| 249 |                 directory.readEntry(key)->accept(rulesVisitor);
 | 
| 250 |             }
 | 
| 251 |         } else if (directory.getKey() == "maxRSS") {
 | 
| 252 |             auto* preMaxRSS = as<SizeEntry>(directory.readEntry("pre"));
 | 
| 253 |             auto* postMaxRSS = as<SizeEntry>(directory.readEntry("post"));
 | 
| 254 |             base.setPreMaxRSS(preMaxRSS->getSize());
 | 
| 255 |             base.setPostMaxRSS(postMaxRSS->getSize());
 | 
| 256 |         }
 | 
| 257 |     }
 | 
| 258 |     void visit(SizeEntry& size) override {
 | 
| 259 |         if (size.getKey() == "reads") {
 | 
| 260 |             base.addReads(size.getSize());
 | 
| 261 |         } else {
 | 
| 262 |             DSNVisitor::visit(size);
 | 
| 263 |         }
 | 
| 264 |     }
 | 
| 265 | };
 | 
| 266 | }  // namespace
 | 
| 267 | 
 | 
| 268 | /*
 | 
| 269 |  * Input reader and processor for log files
 | 
| 270 |  */
 | 
| 271 | class Reader {
 | 
| 272 | private:
 | 
| 273 |     std::string file_loc;
 | 
| 274 |     std::streampos gpos;
 | 
| 275 |     const ProfileDatabase& db = ProfileEventSingleton::instance().getDB();
 | 
| 276 |     bool loaded = false;
 | 
| 277 |     bool online{true};
 | 
| 278 | 
 | 
| 279 |     std::unordered_map<std::string, std::shared_ptr<Relation>> relationMap{};
 | 
| 280 |     std::unordered_map<std::string, std::unordered_map<std::string, double>> countRecursiveJoinSizeMap{};
 | 
| 281 |     std::unordered_map<std::string, double> countNonRecursiveJoinSizeMap{};
 | 
| 282 |     int rel_id{0};
 | 
| 283 | 
 | 
| 284 | public:
 | 
| 285 |     std::shared_ptr<ProgramRun> run;
 | 
| 286 | 
 | 
| 287 |     Reader(std::string filename, std::shared_ptr<ProgramRun> run)
 | 
| 288 |             : file_loc(std::move(filename)), run(std::move(run)) {
 | 
| 289 |         try {
 | 
| 290 |             ProfileEventSingleton::instance().setDBFromFile(file_loc);
 | 
| 291 |         } catch (const std::exception& e) {
 | 
| 292 |             fatal("exception whilst reading profile DB: %s", e.what());
 | 
| 293 |         }
 | 
| 294 |     }
 | 
| 295 | 
 | 
| 296 |     Reader(std::shared_ptr<ProgramRun> run) : run(std::move(run)) {}
 | 
| 297 |     /**
 | 
| 298 |      * Read the contents from file into the class
 | 
| 299 |      */
 | 
| 300 |     void processFile() {
 | 
| 301 |         rel_id = 0;
 | 
| 302 |         relationMap.clear();
 | 
| 303 |         auto programDuration = as<DurationEntry>(db.lookupEntry({"program", "runtime"}));
 | 
| 304 |         if (programDuration == nullptr) {
 | 
| 305 |             auto startTimeEntry = as<TimeEntry>(db.lookupEntry({"program", "starttime"}));
 | 
| 306 |             if (startTimeEntry != nullptr) {
 | 
| 307 |                 run->setStarttime(startTimeEntry->getTime());
 | 
| 308 |                 run->setEndtime(std::chrono::duration_cast<microseconds>(now().time_since_epoch()));
 | 
| 309 |                 loaded = true;
 | 
| 310 |             }
 | 
| 311 |         } else {
 | 
| 312 |             run->setStarttime(programDuration->getStart());
 | 
| 313 |             run->setEndtime(programDuration->getEnd());
 | 
| 314 |             online = false;
 | 
| 315 |         }
 | 
| 316 | 
 | 
| 317 |         auto prefix = as<DirectoryEntry>(db.lookupEntry({"program", "statistics", "relation"}));
 | 
| 318 |         if (prefix != nullptr) {
 | 
| 319 |             for (const auto& rel : prefix->getKeys()) {
 | 
| 320 |                 auto prefixWithRel = as<DirectoryEntry>(
 | 
| 321 |                         db.lookupEntry({"program", "statistics", "relation", rel, "attributes"}));
 | 
| 322 |                 if (prefixWithRel != nullptr) {
 | 
| 323 |                     for (const auto& attributes : prefixWithRel->getKeys()) {
 | 
| 324 |                         auto prefixWithAttributes = as<DirectoryEntry>(db.lookupEntry({"program",
 | 
| 325 |                                 "statistics", "relation", rel, "attributes", attributes, "constants"}));
 | 
| 326 |                         if (prefixWithAttributes == nullptr) {
 | 
| 327 |                             continue;
 | 
| 328 |                         }
 | 
| 329 |                         for (const auto& constants : prefixWithAttributes->getKeys()) {
 | 
| 330 |                             auto fullKey = as<TextEntry>(db.lookupEntry({"program", "statistics", "relation",
 | 
| 331 |                                     rel, "attributes", attributes, "constants", constants}));
 | 
| 332 |                             if (fullKey != nullptr) {
 | 
| 333 |                                 double joinSize = std::stod(fullKey->getText());
 | 
| 334 |                                 std::string key = rel + " " + attributes + " " + constants;
 | 
| 335 |                                 countNonRecursiveJoinSizeMap[key] = joinSize;
 | 
| 336 |                             }
 | 
| 337 |                         }
 | 
| 338 |                     }
 | 
| 339 |                 }
 | 
| 340 | 
 | 
| 341 |                 auto prefixWithRecursiveRel = as<DirectoryEntry>(
 | 
| 342 |                         db.lookupEntry({"program", "statistics", "relation", rel, "iteration"}));
 | 
| 343 |                 if (prefixWithRecursiveRel != nullptr) {
 | 
| 344 |                     for (const auto& iteration : prefixWithRecursiveRel->getKeys()) {
 | 
| 345 |                         auto prefixWithIteration = as<DirectoryEntry>(db.lookupEntry({"program", "statistics",
 | 
| 346 |                                 "relation", rel, "iteration", iteration, "attributes"}));
 | 
| 347 |                         if (prefixWithIteration == nullptr) {
 | 
| 348 |                             continue;
 | 
| 349 |                         }
 | 
| 350 | 
 | 
| 351 |                         for (const auto& attributes : prefixWithIteration->getKeys()) {
 | 
| 352 |                             auto prefixWithAttributes = as<DirectoryEntry>(
 | 
| 353 |                                     db.lookupEntry({"program", "statistics", "relation", rel, "iteration",
 | 
| 354 |                                             iteration, "attributes", attributes, "constants"}));
 | 
| 355 |                             if (prefixWithAttributes == nullptr) {
 | 
| 356 |                                 continue;
 | 
| 357 |                             }
 | 
| 358 |                             for (const auto& constants : prefixWithAttributes->getKeys()) {
 | 
| 359 |                                 auto fullKey = as<TextEntry>(db.lookupEntry(
 | 
| 360 |                                         {"program", "statistics", "relation", rel, "iteration", iteration,
 | 
| 361 |                                                 "attributes", attributes, "constants", constants}));
 | 
| 362 |                                 double joinSize = std::stod(fullKey->getText());
 | 
| 363 |                                 if (fullKey != nullptr) {
 | 
| 364 |                                     std::string key = rel + " " + attributes + " " + constants;
 | 
| 365 |                                     countRecursiveJoinSizeMap[key][iteration] = joinSize;
 | 
| 366 |                                 }
 | 
| 367 |                             }
 | 
| 368 |                         }
 | 
| 369 |                     }
 | 
| 370 |                 }
 | 
| 371 |             }
 | 
| 372 |         }
 | 
| 373 | 
 | 
| 374 |         auto relations = as<DirectoryEntry>(db.lookupEntry({"program", "relation"}));
 | 
| 375 |         if (relations == nullptr) {
 | 
| 376 |             // Souffle hasn't generated any profiling information yet
 | 
| 377 |             // or program is empty.
 | 
| 378 |             return;
 | 
| 379 |         }
 | 
| 380 |         for (const auto& cur : relations->getKeys()) {
 | 
| 381 |             auto relation = as<DirectoryEntry>(db.lookupEntry({"program", "relation", cur}));
 | 
| 382 |             if (relation != nullptr) {
 | 
| 383 |                 addRelation(*relation);
 | 
| 384 |             }
 | 
| 385 |         }
 | 
| 386 |         for (const auto& relation : relationMap) {
 | 
| 387 |             for (const auto& rule : relation.second->getRuleMap()) {
 | 
| 388 |                 for (const auto& atom : rule.second->getAtoms()) {
 | 
| 389 |                     std::string relationName = extractRelationNameFromAtom(atom);
 | 
| 390 |                     relationMap[relationName]->addReads(atom.frequency);
 | 
| 391 |                 }
 | 
| 392 |             }
 | 
| 393 |             for (const auto& iteration : relation.second->getIterations()) {
 | 
| 394 |                 for (const auto& rule : iteration->getRules()) {
 | 
| 395 |                     for (const auto& atom : rule.second->getAtoms()) {
 | 
| 396 |                         std::string relationName = extractRelationNameFromAtom(atom);
 | 
| 397 |                         if (relationName.substr(0, 6) == "@delta") {
 | 
| 398 |                             relationName = relationName.substr(7);
 | 
| 399 |                         }
 | 
| 400 |                         if (relationName.substr(0, 4) == "@new") {
 | 
| 401 |                             relationName = relationName.substr(5);
 | 
| 402 |                         }
 | 
| 403 |                         assert(relationMap.count(relationName) > 0 || "Relation name for atom not found");
 | 
| 404 |                         relationMap[relationName]->addReads(atom.frequency);
 | 
| 405 |                     }
 | 
| 406 |                 }
 | 
| 407 |             }
 | 
| 408 |         }
 | 
| 409 |         run->setRelationMap(this->relationMap);
 | 
| 410 |         loaded = true;
 | 
| 411 |     }
 | 
| 412 | 
 | 
| 413 |     void save(std::string f_name);
 | 
| 414 | 
 | 
| 415 |     inline bool isLive() {
 | 
| 416 |         return online;
 | 
| 417 |     }
 | 
| 418 | 
 | 
| 419 |     bool hasAutoSchedulerStats() {
 | 
| 420 |         return !countNonRecursiveJoinSizeMap.empty() || !countRecursiveJoinSizeMap.empty();
 | 
| 421 |     }
 | 
| 422 | 
 | 
| 423 |     double getNonRecursiveEstimateJoinSize(
 | 
| 424 |             const std::string& rel, const std::string& attributes, const std::string& constants) {
 | 
| 425 |         auto key = rel + " " + attributes + " " + constants;
 | 
| 426 |         return countNonRecursiveJoinSizeMap.at(key);
 | 
| 427 |     }
 | 
| 428 | 
 | 
| 429 |     std::size_t getIterations(const std::string& rel) {
 | 
| 430 |         for (auto& [key, m] : countRecursiveJoinSizeMap) {
 | 
| 431 |             std::string token = key.substr(0, key.find(" "));
 | 
| 432 |             if (token == rel) {
 | 
| 433 |                 return m.size();
 | 
| 434 |             }
 | 
| 435 |         }
 | 
| 436 |         assert(false);
 | 
| 437 |         return 0;
 | 
| 438 |     }
 | 
| 439 | 
 | 
| 440 |     double getRecursiveEstimateJoinSize(const std::string& rel, const std::string& attributes,
 | 
| 441 |             const std::string& constants, const std::string& iteration) {
 | 
| 442 |         auto key = rel + " " + attributes + " " + constants;
 | 
| 443 |         auto& m = countRecursiveJoinSizeMap.at(key);
 | 
| 444 |         return m.at(iteration);
 | 
| 445 |     }
 | 
| 446 | 
 | 
| 447 |     void addRelation(const DirectoryEntry& relation) {
 | 
| 448 |         const std::string& name = cleanRelationName(relation.getKey());
 | 
| 449 | 
 | 
| 450 |         relationMap.emplace(name, std::make_shared<Relation>(name, createId()));
 | 
| 451 |         auto& rel = *relationMap[name];
 | 
| 452 |         RelationVisitor relationVisitor(rel);
 | 
| 453 | 
 | 
| 454 |         for (const auto& key : relation.getKeys()) {
 | 
| 455 |             relation.readEntry(key)->accept(relationVisitor);
 | 
| 456 |         }
 | 
| 457 |     }
 | 
| 458 | 
 | 
| 459 |     inline bool isLoaded() {
 | 
| 460 |         return loaded;
 | 
| 461 |     }
 | 
| 462 | 
 | 
| 463 |     std::string RelationcreateId() {
 | 
| 464 |         return "R" + std::to_string(++rel_id);
 | 
| 465 |     }
 | 
| 466 | 
 | 
| 467 |     std::string createId() {
 | 
| 468 |         return "R" + std::to_string(++rel_id);
 | 
| 469 |     }
 | 
| 470 | 
 | 
| 471 | protected:
 | 
| 472 |     std::string cleanRelationName(const std::string& relationName) {
 | 
| 473 |         std::string cleanName = relationName;
 | 
| 474 |         for (auto& cur : cleanName) {
 | 
| 475 |             if (cur == '-') {
 | 
| 476 |                 cur = '.';
 | 
| 477 |             }
 | 
| 478 |         }
 | 
| 479 |         return cleanName;
 | 
| 480 |     }
 | 
| 481 |     std::string extractRelationNameFromAtom(const Atom& atom) {
 | 
| 482 |         return cleanRelationName(atom.identifier.substr(0, atom.identifier.find('(')));
 | 
| 483 |     }
 | 
| 484 | };
 | 
| 485 | 
 | 
| 486 | }  // namespace profile
 | 
| 487 | }  // namespace souffle
 |