diff --git a/2/Makefile b/2/Makefile index e44dafa..d0c1063 100644 --- a/2/Makefile +++ b/2/Makefile @@ -4,13 +4,46 @@ CXXFLAGS = -O2 -std=c++17 -pthread TARGET = lab2 SRC = main.cpp +LOG = log.txt +PY = python3 + all: $(TARGET) $(TARGET): $(SRC) $(CXX) $(CXXFLAGS) $< -o $@ +# обычный запуск run: $(TARGET) ./$(TARGET) 20000 4 +# запуск с сохранением лога +log: $(TARGET) + ./$(TARGET) 20000 4 > $(LOG) + +# анализ лога (таблица + график) +analyze: log + $(PY) analyze_log.py $(LOG) + +# быстрый полный цикл +bench: $(TARGET) + @echo "Running benchmark..." + ./$(TARGET) 20000 0 > log_0.txt + ./$(TARGET) 20000 2 > log_2.txt + ./$(TARGET) 20000 4 > log_4.txt + ./$(TARGET) 20000 8 > log_8.txt + +# анализ конкретного лога +analyze0: + $(PY) analyze_log.py log_0.txt + +analyze2: + $(PY) analyze_log.py log_2.txt + +analyze4: + $(PY) analyze_log.py log_4.txt + +analyze8: + $(PY) analyze_log.py log_8.txt + clean: - rm -f $(TARGET) + rm -f $(TARGET) *.txt diff --git a/2/analyze_log.py b/2/analyze_log.py new file mode 100644 index 0000000..934015f --- /dev/null +++ b/2/analyze_log.py @@ -0,0 +1,100 @@ +import re +import sys +from collections import defaultdict + +# ================= BACKEND FIX (IMPORTANT FOR HYPRLAND) ================= +import matplotlib + +try: + # Wayland / Hyprland recommended backend + matplotlib.use("QtAgg") +except Exception: + # fallback for headless systems + matplotlib.use("Agg") + +import matplotlib.pyplot as plt + +# ================= INPUT ================= +if len(sys.argv) < 2: + print("Usage: python analyze_log.py log.txt") + sys.exit(1) + +logfile = sys.argv[1] + +# ================= PARSE LOG ================= +pattern = re.compile(r"(START|END).*TID=(\d+).*range=\[(\d+),(\d+)\].*time=([\d.]+)") + +events = defaultdict(dict) + +with open(logfile) as f: + for line in f: + m = pattern.search(line) + if not m: + continue + + typ, tid, l, r, t = m.groups() + key = (tid, int(l), int(r)) + events[key][typ] = float(t) + +# ================= BUILD ROWS ================= +rows = [] + +for (tid, l, r), v in events.items(): + if "START" in v and "END" in v: + start = v["START"] + end = v["END"] + duration = end - start + + rows.append( + { + "tid": tid, + "range": f"[{l},{r}]", + "start": start, + "end": end, + "duration": duration, + } + ) + +rows.sort(key=lambda x: x["start"]) + +if not rows: + print("No valid events found") + sys.exit(1) + +# ================= OFFSET ================= +t0 = rows[0]["start"] + +for r in rows: + r["offset"] = r["start"] - t0 + +# ================= PRINT TABLE ================= +print("\n## TABLE\n") +print("| TID | Range | Start | End | Duration | Offset |") +print("|-----|-------|-------|-----|----------|--------|") + +for r in rows: + print( + f"| {r['tid']} | {r['range']} | " + f"{r['start']:.6f} | {r['end']:.6f} | " + f"{r['duration']:.6f} | {r['offset']:.6f} |" + ) + +# ================= PLOT ================= +plt.figure(figsize=(10, 6)) + +for i, r in enumerate(rows): + plt.plot([r["offset"], r["offset"] + r["duration"]], [i, i], linewidth=4) + +plt.xlabel("Time (seconds from start)") +plt.ylabel("Tasks") +plt.title("Execution Timeline") +plt.grid(True) +plt.tight_layout() + +# ================= SAVE (IMPORTANT FOR LAB) ================= +plt.savefig("timeline.png", dpi=200) + +print("\nSaved: timeline.png") + +# ================= SHOW (HYPRLAND WINDOW) ================= +plt.show() diff --git a/2/lab2 b/2/lab2 deleted file mode 100755 index 4bb7c3c..0000000 Binary files a/2/lab2 and /dev/null differ diff --git a/2/main.cpp b/2/main.cpp index 246deb6..648ef8d 100644 --- a/2/main.cpp +++ b/2/main.cpp @@ -7,18 +7,21 @@ #include #include #include -#include // ✅ getpid +#include #include #include +#include // ================= CONFIG ================= constexpr int kDefaultN = 20000; constexpr int kPreviewCount = 20; -// ================= GLOBALS ================= +// ================= GLOBAL CONTROL ================= int active_threads = 0; int max_threads = 4; -pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; +std::mutex log_mutex; // ================= TIME ================= double get_time() { @@ -36,38 +39,44 @@ std::string now() { return oss.str(); } -// ================= LOG ================= +// ================= SAFE LOGGING ================= void log_start(int l, int r, int depth) { + std::lock_guard lock(log_mutex); + std::cout << "START PID=" << getpid() << " TID=" << pthread_self() << " depth=" << depth - << " range=[" << l << "," << r << "] time=" << now() << '\n'; + << " range=[" << l << "," << r << "] time=" << now() + << '\n'; } void log_end(int l, int r, int depth) { + std::lock_guard lock(log_mutex); + std::cout << "END PID=" << getpid() << " TID=" << pthread_self() << " depth=" << depth - << " range=[" << l << "," << r << "] time=" << now() << '\n'; + << " range=[" << l << "," << r << "] time=" << now() + << '\n'; } // ================= MERGE ================= void merge_range(int* arr, int l, int m, int r) { - std::vector temp; - temp.reserve(r - l + 1); + std::vector tmp; + tmp.reserve(r - l + 1); int i = l; int j = m + 1; while (i <= m && j <= r) { - if (arr[i] <= arr[j]) temp.push_back(arr[i++]); - else temp.push_back(arr[j++]); + if (arr[i] <= arr[j]) tmp.push_back(arr[i++]); + else tmp.push_back(arr[j++]); } - while (i <= m) temp.push_back(arr[i++]); - while (j <= r) temp.push_back(arr[j++]); + while (i <= m) tmp.push_back(arr[i++]); + while (j <= r) tmp.push_back(arr[j++]); - std::copy(temp.begin(), temp.end(), arr + l); + std::copy(tmp.begin(), tmp.end(), arr + l); } // ================= LOCAL SORT ================= @@ -80,7 +89,7 @@ void local_sort(int* arr, int l, int r) { merge_range(arr, l, m, r); } -// ================= THREAD ARGS ================= +// ================= THREAD ARG ================= struct Args { int* arr; int l; @@ -88,10 +97,8 @@ struct Args { int depth; }; -// forward void parallel_sort(int* arr, int l, int r, int depth); -// ================= THREAD FUNC ================= void* thread_func(void* arg) { Args* a = (Args*)arg; parallel_sort(a->arr, a->l, a->r, a->depth); @@ -113,31 +120,27 @@ void parallel_sort(int* arr, int l, int r, int depth) { pthread_t tid; bool spawned = false; - // ===== ограничение потоков ===== - pthread_mutex_lock(&mutex); + // ===== thread limit control ===== + pthread_mutex_lock(&thread_mutex); if (active_threads < max_threads) { active_threads++; spawned = true; } - pthread_mutex_unlock(&mutex); + pthread_mutex_unlock(&thread_mutex); if (spawned) { - // правая часть → новый поток Args* args = new Args{arr, m + 1, r, depth + 1}; pthread_create(&tid, nullptr, thread_func, args); - // левая → текущий поток parallel_sort(arr, l, m, depth + 1); - // ждём pthread_join(tid, nullptr); - pthread_mutex_lock(&mutex); + pthread_mutex_lock(&thread_mutex); active_threads--; - pthread_mutex_unlock(&mutex); + pthread_mutex_unlock(&thread_mutex); } else { - // без потоков parallel_sort(arr, l, m, depth + 1); parallel_sort(arr, m + 1, r, depth + 1); } @@ -147,13 +150,13 @@ void parallel_sort(int* arr, int l, int r, int depth) { log_end(l, r, depth); } -// ================= UTILS ================= -bool parse_positive_int(const char* value, int& out) { +// ================= UTIL ================= +bool parse_int(const char* s, int& out) { try { - size_t consumed = 0; - int parsed = std::stoi(value, &consumed); - if (value[consumed] != '\0' || parsed < 0) return false; - out = parsed; + size_t p; + int v = std::stoi(s, &p); + if (s[p] != '\0' || v < 0) return false; + out = v; return true; } catch (...) { return false; @@ -164,13 +167,13 @@ bool parse_positive_int(const char* value, int& out) { int main(int argc, char* argv[]) { int n = kDefaultN; - if (argc >= 2 && !parse_positive_int(argv[1], n)) { - std::cerr << "Invalid array size\n"; + if (argc >= 2 && !parse_int(argv[1], n)) { + std::cerr << "Invalid N\n"; return 1; } - if (argc >= 3 && !parse_positive_int(argv[2], max_threads)) { - std::cerr << "Invalid max_threads\n"; + if (argc >= 3 && !parse_int(argv[2], max_threads)) { + std::cerr << "Invalid threads\n"; return 1; } @@ -179,11 +182,11 @@ int main(int argc, char* argv[]) { std::mt19937 rng(std::random_device{}()); std::uniform_int_distribution dist(0, 99999); - for (int i = 0; i < n; ++i) arr[i] = dist(rng); + for (int i = 0; i < n; i++) arr[i] = dist(rng); - std::cout << "Before:\n"; - for (int i = 0; i < std::min(n, kPreviewCount); ++i) - std::cout << arr[i] << ' '; + std::cout << "Before: "; + for (int i = 0; i < std::min(n, kPreviewCount); i++) + std::cout << arr[i] << " "; std::cout << "\n\n"; double t1 = get_time(); @@ -192,10 +195,10 @@ int main(int argc, char* argv[]) { double t2 = get_time(); - std::cout << "\nAfter:\n"; - for (int i = 0; i < std::min(n, kPreviewCount); ++i) - std::cout << arr[i] << ' '; - std::cout << '\n'; + std::cout << "\nAfter: "; + for (int i = 0; i < std::min(n, kPreviewCount); i++) + std::cout << arr[i] << " "; + std::cout << "\n"; std::cout << "\nTime: " << (t2 - t1) << " sec\n"; diff --git a/req.txt b/req.txt index 794cc3d..f089e8e 100644 --- a/req.txt +++ b/req.txt @@ -1 +1,3 @@ openpyxl +matplotlib +PyQt6