diff --git a/prak3/.vscode/launch.json b/prak3/.vscode/launch.json index 2ad16e4c7ad14d5b1d4ab77d0419db2186392707..e40495c38db351f015684020e8a638756617b4cc 100644 --- a/prak3/.vscode/launch.json +++ b/prak3/.vscode/launch.json @@ -37,11 +37,68 @@ "preLaunchTask": "build" }, { - "name": "Debug Client", + "name": "Debug Client (equal)", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/src/myclient", "args": [ + "127.0.0.1", + "2525", + "1024", + "20000", + "equal", + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "build" + }, + { + "name": "Debug Client (random)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/src/myclient", + "args": [ + "127.0.0.1", + "2525", + "1024", + "20000", + "random", + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "build" + }, + { + "name": "Debug Client (realistic)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/src/myclient", + "args": [ + "127.0.0.1", + "2525", + "1024", + "20000", + "realistic", ], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/prak3/readme.md b/prak3/readme.md index 17aace96aaf2fb10ed0bca98400f89237bbc1a4e..6457039d8074844552c7df7c007f73ed2bb7f0d2 100644 --- a/prak3/readme.md +++ b/prak3/readme.md @@ -21,35 +21,32 @@ Use the following command to execute the server: ``` Use the following command to execute the client: ```sh -./build/src/myclient store name saif -./build/src/myclient store name chris -./build/src/myclient get name -./build/src/myclient delete name -./build/src/myclient get name +./startClients.sh <SERVER_IP> <PORT> <BUFFER_SIZE> <NUMBER_OF_REQUESTS> <REQUEST_GENERATION_TYPE> ``` --- -## đ Performance Testing (EDIT THIT ) +## đ Performance Testing The following scripts measure execution time and generate profiling data for different scenarios: ### âł Execution Time Measurement -- **With socket output:** +- **Execute the client with random Requests:** ```sh - ./exec_time.sh socket + ./startClients.sh 141.100.42.131 2525 1024 2000 random ``` -- **With pipe output:** +- **Execute the client with equal Requests:** ```sh - ./exec_time.sh pipe + ./startClients.sh 141.100.42.131 2525 1024 2000 equal ``` -- **With shared_memory output:** +- **Execute the client with realistic Requests:** ```sh - ./exec_time.sh shared_memory + ./startClients.sh 141.100.42.131 2525 1024 2000 realistic ``` + ### đ„ Stack Trace & Profiling (Perf + Flamegraphs) - **With socket output:** diff --git a/prak3/src/client.cpp b/prak3/src/client.cpp index 7f648276bc1247e1eb161d09249bfbb61842dc35..8d82c4f8750fc1b154c3769f38f4efb12850bf85 100644 --- a/prak3/src/client.cpp +++ b/prak3/src/client.cpp @@ -6,15 +6,13 @@ #include <cstdlib> #include <ctime> -#define SERVER_IP "127.0.0.1" -#define PORT 2525 -#define BUFFER_SIZE 1024 -#define REQUEST_COUNT 70000 +pid_t pid = getpid(); std::string generateRandomKey() { - return "key" + std::to_string(rand() % 1000); + return "key" + std::to_string(rand() % 100); } + std::string generateRandomValue() { return "value" + std::to_string(rand() % 10000); } @@ -31,44 +29,102 @@ std::string createRandomMessage() { } } -int main() { +std::vector<std::string> generateRequests(int requestCount, std::string requestGenerationType) { + std::vector<std::string> requests; + requests.reserve(requestCount); + + if (requestGenerationType == "random") { + for (int i = 0; i < requestCount; i++) { + requests.push_back(createRandomMessage()); + } + } else if (requestGenerationType == "equal") { + int getCount = requestCount * 0.33; + int storeCount = requestCount * 0.33; + int deleteCount = requestCount * 0.33; + + for (int i = 0; i < getCount; i++) { + requests.push_back("s,key" + std::to_string(pid) + std::to_string(i) + ",value" + std::to_string(i) + ";"); + } + for (int i = 0; i < storeCount; i++) { + requests.push_back("g,key" + std::to_string(pid) + std::to_string(i) +';'); + } + for (int i = 0; i < deleteCount; i++) { + requests.push_back("d,key" + std::to_string(pid) + std::to_string(i) + ';'); + } + } else if (requestGenerationType == "realistic") { + int getCount = requestCount * 0.7; + int storeCount = requestCount * 0.25; + int deleteCount = requestCount - (getCount + storeCount); // Restliche 5% + + for (int i = 0; i < getCount; i++) { + requests.push_back("g," + generateRandomKey() + ";"); + } + for (int i = 0; i < storeCount; i++) { + requests.push_back("s," + generateRandomKey() + "," + generateRandomValue() + ";"); + } + for (int i = 0; i < deleteCount; i++) { + requests.push_back("d," + generateRandomKey() + ";"); + } + + // Um eine realistischere Last zu simulieren, die Reihenfolge zufĂ€llig mischen + //std::random_shuffle(requests.begin(), requests.end()); + } else { + std::cerr << "requestGenerationType is not expected. Please use [random, equal, realistic]" << std::endl; + } + return requests; +} + + +int main(int argc, char* argv[]) { + if (argc != 6) { + std::cerr << "Usage: " << argv[0] << " <server_ip> <port> <buffer_size> <request_count> <requestGenerationType [random, equal, realistic]>" << std::endl; + return EXIT_FAILURE; + } + + std::string serverIp = argv[1]; + int port = std::stoi(argv[2]); + int bufferSize = std::stoi(argv[3]); + int requestCount = std::stoi(argv[4]); + std::string requestGenerationType = argv[5]; + srand(time(nullptr)); + std::vector<std::string> requests = generateRequests(requestCount, requestGenerationType); + int sock; struct sockaddr_in server_address; - char buffer[BUFFER_SIZE] = {0}; + char* buffer[bufferSize] = {0}; // Socket erstellen sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("Socket failed"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } // Serveradresse konfigurieren server_address.sin_family = AF_INET; - server_address.sin_port = htons(PORT); - if (inet_pton(AF_INET, SERVER_IP, &server_address.sin_addr) <= 0) { + server_address.sin_port = htons(port); + if (inet_pton(AF_INET, serverIp.c_str(), &server_address.sin_addr) <= 0) { perror("Invalid address"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } // Verbindung zum Server herstellen if (connect(sock, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { perror("Connection failed"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } - for (int i = 0; i < REQUEST_COUNT; i++) { - std::string message = createRandomMessage(); + for (const auto& message : requests) { send(sock, message.c_str(), message.size(), 0); std::cout << "Sent: " << message << std::endl; - ssize_t bytes_received = read(sock, buffer, BUFFER_SIZE); + ssize_t bytes_received = read(sock, buffer, bufferSize); if (bytes_received > 0) { - std::cout << "Server: " << buffer << std::endl; + std::cout << "Server Response: " << buffer << std::endl; } - memset(buffer, 0, BUFFER_SIZE); + memset(buffer, 0, bufferSize); } std::cout << "Closing connection..." << std::endl; diff --git a/prak3/src/server.cpp b/prak3/src/server.cpp index b83f9c263041ef4c85deff4af950af6c1526b274..e2198f50c8e10df646b700a5636124e1602bcb70 100644 --- a/prak3/src/server.cpp +++ b/prak3/src/server.cpp @@ -6,6 +6,7 @@ #include <arpa/inet.h> #include <mutex> #include <map> +#include <atomic> #define PORT 2525 #define BUFFER_SIZE 1024 @@ -13,89 +14,18 @@ #define GET_OPERATION 'g' #define DELETE_OPERATION 'd' -//=========== SLOW PARSER ==================== -struct Request { - char operation; - std::string key; - std::string value; -}; - Request* parseRequest(const char* buffer) { - // Create a new Request struct - Request* req = new Request(); - - // Copy the input buffer to a mutable string for tokenization - char* input = strdup(buffer); // Duplicate the buffer - if (!input) { - return nullptr; // Handle memory allocation failure - } - - // Tokenize the input string using ',' and ';' as delimiters - char* token = strtok(input, ",;"); - if (token) { - // First token is the operation - req->operation = token[0]; // Extract the first character (s, g, or d) - - // Second token is the key - token = strtok(nullptr, ",;"); - if (token) { - req->key = token; - - // Third token is the value (only for store operation) - if (req->operation == 's') { - token = strtok(nullptr, ",;"); - if (token) { - req->value = token; - } - } - } - } - - // Free the duplicated input string - free(input); - - return req; -} -//============================================= - std::mutex mtx; std::map<std::string, std::string> DB; +std::atomic<int> request_count(0); -// we assume that this function is slow -std::string processRequest_slow(const std::string& request) { - - std::string response; - Request* parsedRequest = parseRequest(request.c_str()); - - std::lock_guard<std::mutex> lock(mtx); - - switch (parsedRequest->operation) { - case STORE_OPERATION: - DB[parsedRequest->key] = parsedRequest->value; - response = "Stored [" + parsedRequest->key + "] = " + parsedRequest->value; - break; - case GET_OPERATION: - if (DB.find(parsedRequest->key) != DB.end()) { - response = "Value of [" + parsedRequest->key + "] is " + DB[parsedRequest->key]; - } else { - response = "Key [" + parsedRequest->key + "] not found"; - } - break; - case DELETE_OPERATION: - if (DB.erase(parsedRequest->key)) { - response = "Deleted key [" + parsedRequest->key + "]"; - } else { - response = "Key [" + parsedRequest->key + "] not found for deletion"; - } - break; - default: - response = "Invalid operation!"; - break; +void throughput_monitor() { + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "Anfragen pro Sekunde: " << request_count.load() << std::endl; + request_count.store(0); } - - return response; } - -// there is parsing problem the parced key is always with ; at the end + std::string processRequest(const std::string& request) { std::string response; @@ -112,11 +42,9 @@ std::string processRequest(const std::string& request) { std::string value; if (second != std::string::npos) { - // Extract key without ';' key = request.substr(first + 1, second - first - 1); value = request.substr(second + 1, end - second - 1); } else { - // Extract key correctly without including ';' key = request.substr(first + 1, end - first - 1); } @@ -151,20 +79,21 @@ std::string processRequest(const std::string& request) { void handle_client(int client_socket) { char buffer[BUFFER_SIZE] = {0}; - - while (true) { // the Thread is allways active + while (true) { ssize_t bytes_received = read(client_socket, buffer, BUFFER_SIZE - 1); if (bytes_received <= 0) { std::cout << "Client disconnected." << std::endl; break; } - + buffer[bytes_received] = '\0'; std::string response = processRequest(buffer); + // AnfragezĂ€hler inkrementieren + request_count.fetch_add(1, std::memory_order_relaxed); + send(client_socket, response.c_str(), response.size(), 0); } - close(client_socket); } @@ -195,6 +124,10 @@ int main() { std::cout << "Server listening on port " << PORT << std::endl; + // Starte den Throughput-Monitor + std::thread monitor_thread(throughput_monitor); + monitor_thread.detach(); + while (true) { client_socket = accept(server_fd, (struct sockaddr*)&address, &addrlen); diff --git a/prak3/startClients.sh b/prak3/startClients.sh new file mode 100644 index 0000000000000000000000000000000000000000..b7ab5b8e6a1c6fcbbdd7862de56d0ee16b817b82 --- /dev/null +++ b/prak3/startClients.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Configuration +PROCESS="./build/src/myclient" # Path to the process/script you want to run +SERVER_IP=$1 # Server IP (first argument) +PORT=$2 # Port (second argument) +BUFFER_SIZE=$3 # Buffer size (third argument) +NUMBER_OF_REQUESTS=$4 # Number of requests (fourth argument) +REQUEST_GENERATION_TYPE=$5 # Request generation type (fifth argument) +NUMBER_OF_PROCESSES_TO_START = $6 + +# Validate input arguments +if [ $# -ne 6 ]; then + echo "Usage: $0 <SERVER_IP> <PORT> <BUFFER_SIZE> <NUMBER_OF_REQUESTS> <REQUEST_GENERATION_TYPE>" + exit 1 +fi + +# Function to run the process +run_process() { + local id=$1 + echo "Starting process $id with args: $SERVER_IP, $PORT, $BUFFER_SIZE, $REQUEST_GENERATION_TYPE" + $PROCESS "$SERVER_IP" "$PORT" "$BUFFER_SIZE" "$NUMBER_OF_REQUESTS" "$REQUEST_GENERATION_TYPE" > "output_$id.log" 2>&1 + echo "Finished process $id" +} + +# Parallel execution +echo "Running $NUMBER_OF_PROCESSES_TO_START processes in parallel..." +for ((i = 1; i <= NUMBER_OF_PROCESSES_TO_START; i++)); do + run_process $i & +done +wait # Wait for all background processes to finish + +echo "All processes completed." \ No newline at end of file