Compare commits

...

15 Commits

Author SHA1 Message Date
ag
1916781bbb variation of tests 2024-09-23 14:58:08 +03:00
ag
539df093a3 final comit 2024-09-22 15:54:31 +03:00
ag
49ef72e1ea final comit 2024-09-22 15:51:02 +03:00
49410552c6 dont work on windows
i dont know why
2024-09-22 15:23:43 +03:00
ag
1a84f77192 ideal_cache 2024-09-22 03:26:07 +03:00
ag
552953591e ideal_cache 2024-09-22 03:25:10 +03:00
ag
55345b7c76 ideal_cache_1_ver 2024-09-21 16:19:03 +03:00
ag
a21fc9e08d bugs fixed 2024-09-21 11:53:10 +03:00
ag
1860adceff bugs fixed 2024-09-21 11:51:04 +03:00
ag
b577a5fe02 test_gen 2024-09-21 01:39:53 +03:00
ag
c742d6b58d testing debug 2024-09-20 23:29:56 +03:00
ag
21ba17c7b6 test gen without exept of overflow 2024-09-20 00:21:06 +03:00
ag
851f43206e readme 2024-09-17 12:24:31 +03:00
ag
74cad036f8 deleted .vscode 2024-09-17 12:21:53 +03:00
ag
03036ff185 last commit 2024-09-17 10:24:26 +03:00
10 changed files with 280 additions and 100 deletions

6
.gitignore vendored
View File

@ -1,2 +1,6 @@
*.o *.o
* *.
*.txt
*.vscode
*.out
*.exe

51
.vscode/settings.json vendored
View File

@ -1,51 +0,0 @@
{
"files.associations": {
"iostream": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"typeinfo": "cpp",
"fstream": "cpp",
"sstream": "cpp"
}
}

View File

@ -18,43 +18,83 @@ private:
std::list <int> q2; //slow queue - if page asked at the first time put it to q2, if itasked from q2 it put it to q1, std::list <int> q2; //slow queue - if page asked at the first time put it to q2, if itasked from q2 it put it to q1,
public: public:
int tic_number = 0;
TwoCache(size_t q1sz, size_t q2sz): cpct_q1(q1sz), cpct_q2(q2sz) {} //ctor TwoCache(size_t q1sz, size_t q2sz): cpct_q1(q1sz), cpct_q2(q2sz) {} //ctor
void put_page(int id) { void put_page(int id) {
if (umap1.find(id) != umap1.end()) { //page is in fast queue in the end, nothing to do (fast case) //std::cout << "id: " << id << std::endl;
return;
} else if (umap2.find(id) != umap2.end()) {
q2.erase(umap2[id]); //if page is in slow put it in fast
if (q1.size() < cpct_q1) {
q1.push_front(id);
umap1[id] = q1.begin();
} else {
int lru_page = q1.back(); //if fast is overflow - delete
q1.pop_back();
umap1.erase(lru_page);
q1.push_front(id);
umap1[id] = q1.begin();
}
return; if (umap1.find(id) != umap1.end()) { //page is found, nothing to do (fast case)
//std::cout << "id ="<<id<<" TIC!\n";
tic_number++;
//print_info();
return;
} else if (umap2.find(id) != umap2.end()) {
//std::cout << "id ="<<id<<" TIC!\n";
tic_number++;
q2.erase(umap2[id]);
umap2.erase(id);
if (q1.size() < cpct_q1) {
//print_info();
q1.push_front(id);
umap1[id] = q1.begin();
//print_info();
} else {
//print_info();
auto lru_page = q1.back(); //if fast is overflow - delete
umap1.erase(lru_page);
q1.pop_back();
q1.push_front(id);
umap1[id] = q1.begin();
//print_info();
return;
}
} else if (q2.size() < cpct_q2) { //if not in cache put it in slow } else if (q2.size() < cpct_q2) { //if not in cache put it in slow
//print_info();
q2.push_front(id); q2.push_front(id);
umap2[id] = q2.begin(); umap2[id] = q2.begin();
//print_info();
return; return;
} else { //if slow is overflow - pop } else { //if slow is overflow - pop
int lru_page = q2.back();
q2.pop_back(); //print_info();
umap2.erase(lru_page);
int lru_page = q2.back(); //if fast is overflow - delete
umap2.erase(lru_page);
q2.pop_back();
q2.push_front(id); q2.push_front(id);
umap2[id] = q2.begin(); umap2[id] = q2.begin();
return; return;
//print_info();
} }
} }
void print_info() { void print_info() {
std::cout <<__LINE__ << " " << __FILE__<< std::endl;
std::cout << "\nq1 (the most usable): "; std::cout << "\nq1 (the most usable): ";
for (auto i = q1.begin(); i != q1.end(); i++) { for (auto i = q1.begin(); i != q1.end(); i++) {
std::cout << *i << " "; std::cout << *i << " ";
@ -63,7 +103,24 @@ public:
for (auto i = q2.begin(); i != q2.end(); i++) { for (auto i = q2.begin(); i != q2.end(); i++) {
std::cout << *i << " "; std::cout << *i << " ";
} }
std::cout << std::endl; std::cout << std::endl;
std::cout << "Umap1:\n";
for (auto it = umap1.begin(); it != umap1.end(); ++it) {
std::cout << "Key: " << (it->first) << ", Val: " << *(it->second) << " ";
}
std::cout << std::endl;
std::cout << "Umap2:\n";
for (auto it = umap2.begin(); it != umap2.end(); ++it) {
std::cout << "Key: " << it->first << ", Val: " << *(it->second) << " ";
}
std::cout << std::endl<<std::endl;
} }
std::string string_info () { //to easy compare with tests; std::string string_info () { //to easy compare with tests;
@ -76,7 +133,7 @@ public:
answer += std::to_string(*i) + " "; answer += std::to_string(*i) + " ";
} }
answer.pop_back(); answer.pop_back(); //delete last space
return answer; return answer;
} }

69
Ideal_cache.h Normal file
View File

@ -0,0 +1,69 @@
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
class Ideal_Cache {
private:
int capacity;
std::vector<int> cache;
std::unordered_map<int, int> cacheMap; // Для быстрого доступа к индексам
public:
Ideal_Cache(size_t capacity) : capacity(capacity) {}
int tic_number = 0;
void get_request_vector(const std::vector<int>& requests) {
// смотрим есть ли айди в кэше
// Есть - тик
// Нет - смотрим размер, если размер не полный добавляем в кэш
// если размер полный, то уберем тот, который позже всех встретится
for (size_t i = 0; i < requests.size(); ++i) {
int item = requests[i];
if (cacheMap.find(item) != cacheMap.end()) {
tic_number++; //tic
continue;
}
if (cache.size() < capacity) {
cache.push_back(item);
cacheMap[item] = cache.size() - 1;
} else {
int farthest = -1;
int indexToRemove = -1;
for (size_t j = 0; j < cache.size(); ++j) {
auto nextIndex = std::find(requests.begin() + i, requests.end(), cache[j]); //находим следующее вхождение (начиная с i ого тк предыдущие обработались)
if (nextIndex == requests.end()) {
indexToRemove = j; //В конце удалим последний
break;
} else {
int nextIdx = std::distance(requests.begin(), nextIndex);
if (nextIdx > farthest) {
farthest = nextIdx;
indexToRemove = j;
}
}
}
cacheMap.erase(cache[indexToRemove]);
cache[indexToRemove] = item;
cacheMap[item] = indexToRemove;
}
}
}
void info() const {
std::cout << "ideal cache: ";
for (int item : cache) {
std::cout << item << " ";
}
std::cout << std::endl;
}
};

22
Readme.md Normal file
View File

@ -0,0 +1,22 @@
# Алгоритм кэширования 2Q
Алгоритм кэширования 2Q (Two Queues) представляет собой метод управления кэш-памятью. Этот алгоритм состоит из двух очередей: q1 и q2. q1 является буфером, в который добавляются данные только в начале и замещаются из конца, а q2 используется для хранения "недавно использованных" данных.
# Тестирование
Генератор тестов - test_gen.py спрашивает количество тестов и создает pytests.txt
# Запуск тестов
make test - собирает программу затем
./cache_test "test_file_name"
## Напиример:
./cache_test pytests.txt
# Принцип работы
Когда данные добавляются в кэш, они помещаются в начало q1.
Если данные извлекаются из кэша, они перемещаются из q1 в начало q2.
Если данные снова запрашиваются и они находятся в q2, они перемещаются в конец q1.
Если данные снова запрашиваются и их уже нет в кэше, они добавляются в начало q1, а если q1 заполнена, то данные из конца q1 удаляются и добавляются новые данные в начало q1.
# Преимущества
## Эффективность:
алгоритм 2Q обладает хорошей производительностью и способен эффективно управлять кэш-памятью.
## Адаптивность:
алгоритм автоматически регулируется в зависимости от обращаемости данных, приспосабливаясь к изменениям в запросах.
## Недостатки
Не подходит для всех типов данных: алгоритм 2Q неэффективен для случаев, когда данные необходимо хранить в определённом порядке.
Сложность реализации: реализация алгоритма кэширования 2Q может быть более сложной, чем у других методов управления кэш-памятью.

BIN
cache_test Executable file

Binary file not shown.

View File

@ -1,7 +1,12 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include "2Q_cache.h" #include "2Q_cache.h"
#include "Ideal_cache.h"
#include <sstream> #include <sstream>
#include <vector>
#include <algorithm>
const char* default_test_file = "tests.txt";
int cache_test(std::string file_name) { int cache_test(std::string file_name) {
@ -16,26 +21,35 @@ int cache_test(std::string file_name) {
std::string test_line, answer_line; std::string test_line, answer_line;
int fast_q_sz, slow_q_sz, num_of_calls, page_id, test_number = 0; int fast_q_sz, slow_q_sz, num_of_calls, page_id, test_number = 0, test_passed = 0, test_failed = 0;
while(std::getline(file, test_line)) { while (std::getline(file, test_line)) {
std::stringstream ss(test_line); std::stringstream ss(test_line);
ss >> fast_q_sz >> slow_q_sz>>num_of_calls; //read input ss >> fast_q_sz >> slow_q_sz>>num_of_calls; //read input
TwoCache cache(fast_q_sz, slow_q_sz); //initialising TwoCache cache(fast_q_sz, slow_q_sz); //initialising
Ideal_Cache ideal_cache(fast_q_sz + slow_q_sz);
for(int i = 0; i < num_of_calls; i++) { //executing std::vector <int> requests;
for (int i = 0; i < num_of_calls; i++) { //executing
ss >> page_id; ss >> page_id;
cache.put_page(page_id); cache.put_page(page_id);
requests.push_back(page_id);
} }
ideal_cache.get_request_vector(requests);
std::getline(file, answer_line); std::getline(file, answer_line);
if(cache.string_info() == answer_line) { //compare answers if (cache.string_info() == answer_line) { //compare answers
std::cout << "test - " << test_number << " passed\n"; std::cout << "test - " << test_number << " passed\n";
std::cout << "ideal tics/your tics " << ideal_cache.tic_number << "/" << cache.tic_number << std::endl;
test_passed++;
} else { } else {
std::cout << "test - " << test_number << " failed\n"; std::cout << "test - " << test_number << " failed\n";
std::cout << "right answer - " << answer_line << "your answer - " << cache.string_info() << std::endl; std::cout << "right answer - " << answer_line << "your answer - " << cache.string_info() << std::endl;
test_failed++;
} }
test_number++; test_number++;
@ -43,11 +57,15 @@ int cache_test(std::string file_name) {
file.close(); file.close();
std::cout << "\nNumber of tests = "<< test_number << "\nTests passed = " << test_passed << "\nTests failed = " << test_failed << "\n";
return 0; return 0;
} }
int main() { int main(int argc, char* argv[]) {
cache_test("tests.txt"); if (argc == 0) {
cache_test(default_test_file);
} } else {
cache_test(argv[1]);
}
}

View File

@ -1,10 +1,6 @@
TARGET = Q2 TARGET = Q2
CXX = g++ CXX = g++
CXXFLAGS = -Wall -Wextra -std=c++11
SRCS = main.cpp SRCS = main.cpp
TEST = cache_test.cpp TEST = cache_test.cpp
@ -16,16 +12,22 @@ $(TARGET): $(OBJS)
$(CXX) -o $@ $^ $(CXX) -o $@ $^
%.o: %.cpp %.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@ $(CXX) $(CPPFLAGS) -c $< -o $@
test: test:
$(CXX) $(CXXFLAGS) $(TEST) -o cache_test $(CXX) $(CPPFLAGS) $(TEST) -o cache_test
clean: clean:
rm -f $(OBJS) $(TARGET) $(TEST) rm -f $(OBJS) $(TARGET) cache_test
run: $(TARGET) run: $(TARGET)
./$(TARGET) ./$(TARGET)
.PHONY: all clean run ifeq ($(OS), Windows_NT)
clean:
del /Q $(OBJS) $(TARGET) cache_test
run: $(TARGET)
$(TARGET).exe
endif
.PHONY: all clean run test

67
test_gen.py Normal file
View File

@ -0,0 +1,67 @@
import random
import sys
class Cache:
def __init__(self, cpst1, cpst2):
self.cpst1 = cpst1
self.cpst2 = cpst2
self.list1 = []
self.list2 = []
def put_page(self, id:int):
if(id in self.list1):
return 0 #tic
elif (id in self.list2):
self.list2.remove(id) #tic
if(len(self.list1) < self.cpst1):
self.list1.insert(0, id)
else:
self.list1.pop(-1)
self.list1.insert(0, id)
elif (len(self.list2) < self.cpst2):
self.list2.insert(0, id)
return 0
else:
self.list2.pop(-1)
self.list2.insert(0, id)
def ret_cache(self):
return self.list1 + self.list2
if __name__ == "__main__":
number_of_test = int(input())
with open('pytests.txt', 'w+') as file:
for i in range(number_of_test):
vect = []
szq1 = random.randint(5, 10)
szq2 = random.randint(5, 10)
num_of_requests = (random.randint(1, 1000000))
cache = Cache(szq1, szq2)
vect.append(szq1)
vect.append(szq2)
vect.append(num_of_requests)
for j in range(num_of_requests):
id = random.randint(0, 100)
vect.append(id)
cache.put_page(id)
file.write(str(vect)[1:-1].replace(", ", " "))
file.write('\n')
file.write(str(cache.ret_cache())[1:-1].replace(", ", " "))
file.write('\n')

View File

@ -1,8 +0,0 @@
1 1 10 1 1 1 1 1 2 2 2 2 3
2 3
3 4 16 1 2 3 4 5 6 1 2 3 1 4 5 3 4 6 7
4 3 1 7 6 5 2
3 10 10 1 2 3 1 2 3 6 8 9 11
3 2 1 11 9 8 6
10 10 20 1 2 3 4 5 6 7 8 9 10 11 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 11