commit 1a050d28e22d579a655adebdbd4ab5132fa47311 Author: Abdelrahman Date: Sat Feb 25 22:39:57 2023 +0000 INITIAL COMMIT diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5ae425 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +python/__pycache__/**/* +cpp/haverscan +cpp/haverstrtok +data_10000000_flex.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..59a89d1 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +mk_fscanf: + clang++ -g cpp/fscanf.cpp cpp/haversine.cpp -o cpp/haverscan + +run_fscanf: + ./cpp/haverscan + +mk_strtok: + clang++ -g cpp/strtok.cpp cpp/haversine.cpp -o cpp/haverstrtok + +run_strtok: + ./cpp/haverstrtok + +run_python_json: + python ./python/haversine_json.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..b488f94 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Casey Muratori's Performance Aware Programming Course + +Following along Casey's course [Performance Aware Programming](https://www.computerenhance.com/s/programming-courses/archive?sort=new) diff --git a/cpp/fscanf.cpp b/cpp/fscanf.cpp new file mode 100644 index 0000000..e787c4e --- /dev/null +++ b/cpp/fscanf.cpp @@ -0,0 +1,104 @@ +#include "haversine.h" +#include +#include +#include +#include +#include + +#define DEBUG 0 + +#define EARTH_RADIUS_KM 6371.0 + +#if DEBUG +#define ITEM_COUNT 2 +#else +#define ITEM_COUNT 10000000 +#endif + +struct Item { + double x0; + double y0; + double x1; + double y1; +}; + +int main(int argc, char *argv[]) { +#if DEBUG + FILE *fp = fopen("/home/abdelrahman/Sources/dev/" + "performance_aware_programming/test_data.json", + "rb"); +#else + FILE *fp = fopen("/home/abdelrahman/Sources/dev/" + "performance_aware_programming/data_10000000_flex.json", + "rb"); +#endif + + Item *items = (Item *)malloc(ITEM_COUNT * sizeof(Item)); + int index = 0; + + clock_t start_time = clock(); + + { + char buf[256]; + int read = fread(buf, 1, 256, fp); + + for (int i = 1; i < read; ++i) { + if (buf[i] == '{') { + fseek(fp, i, SEEK_SET); + break; + } + } + } + + while (true) { + fscanf(fp, "{\n"); + + int scanned = fscanf(fp, + " \"x0\": %lf,\n \"y0\": %lf,\n " + "\"x1\": %lf,\n \"y1\": %lf\n },\n ", + &(items[index].x0), &(items[index].y0), + &(items[index].x1), &(items[index].y1)); + + if (scanned == 0) { + break; + } + + ++index; + } + + clock_t mid_time = clock(); + + double sum = 0.0; + + for (int i = 0; i < ITEM_COUNT; ++i) { + sum += haversine_of_degrees(items[i].x0, items[i].y0, items[i].x1, + items[i].y1, EARTH_RADIUS_KM); + } + + double average = sum / ITEM_COUNT; + + clock_t end_time = clock(); + + printf("Result: %.16f\n", average); + printf("Input = %.16f seconds\n", + (double)(mid_time - start_time) / CLOCKS_PER_SEC); + printf("Math = %.16f seconds\n", + (double)(end_time - mid_time) / CLOCKS_PER_SEC); + printf("Total = %.16f seconds\n", + (double)(end_time - start_time) / CLOCKS_PER_SEC); + printf("Throughput = %.16f haversines/second\n", + ITEM_COUNT / ((double)(end_time - start_time) / CLOCKS_PER_SEC)); + +#if DEBUG + for (int i = 0; i < ITEM_COUNT; ++i) { + printf("%.16f\n%.16f\n%.16f\n%.16f\n\n", items[i].x0, items[i].y0, + items[i].x1, items[i].y1); + } +#endif + + free(items); + + fclose(fp); + + return 0; +} diff --git a/cpp/haversine.cpp b/cpp/haversine.cpp new file mode 100644 index 0000000..3887257 --- /dev/null +++ b/cpp/haversine.cpp @@ -0,0 +1,24 @@ +#include "haversine.h" +#include + +#define PI 3.14159265358979323845 +#define SQUARE(X) ((X) * (X)) + +double radians(double degrees); + +double haversine_of_degrees(double x0, double y0, double x1, double y1, + double radius) { + double dy = radians(y1 - y0); + double dx = radians(x1 - x0); + y0 = radians(y0); + y1 = radians(y1); + + double root_term = + SQUARE(sin(dy / 2.0)) + cos(y0) * cos(y1) * SQUARE(sin(dx / 2.0)); + + double result = 2.0 * radius * asin(sqrt(root_term)); + + return result; +} + +double radians(double degrees) { return (degrees * PI) / 180.0; } diff --git a/cpp/haversine.h b/cpp/haversine.h new file mode 100644 index 0000000..b4faf6f --- /dev/null +++ b/cpp/haversine.h @@ -0,0 +1,7 @@ +#ifndef HAVERSINE_H +#define HAVERSINE_H + +double haversine_of_degrees(double x0, double y0, double x1, double y1, + double radius); + +#endif // !HAVERSINE_H diff --git a/cpp/strtok.cpp b/cpp/strtok.cpp new file mode 100644 index 0000000..831d6cf --- /dev/null +++ b/cpp/strtok.cpp @@ -0,0 +1,116 @@ +#include "haversine.h" +#include +#include +#include +#include +#include + +#define DEBUG 0 + +#define EARTH_RADIUS_KM 6371.0 + +#if DEBUG +#define ITEM_COUNT 2 +#else +#define ITEM_COUNT 10000000 +#endif + +struct Item { + double x0; + double y0; + double x1; + double y1; +}; + +int main(int argc, char *argv[]) { +#if DEBUG + FILE *fp = fopen("/home/abdelrahman/Sources/dev/" + "performance_aware_programming/test_data.json", + "rb"); +#else + FILE *fp = fopen("/home/abdelrahman/Sources/dev/" + "performance_aware_programming/data_10000000_flex.json", + "rb"); +#endif + + Item *items = (Item *)malloc(ITEM_COUNT * sizeof(Item)); + int index = 0; + double *elem = nullptr; + + fseek(fp, 0, SEEK_END); + + long length = (long)ftell(fp); + + fseek(fp, 0, SEEK_SET); + + char *text = (char *)malloc(length + 1); + memset(text, 0, length + 1); + + fread(text, 1, length, fp); + + clock_t start_time = clock(); + + const char *delim = "{}[]:,\"\n "; + + char *token = strtok(text, delim); + + while (token) { + if (strcmp(token, "x0") == 0) { + elem = &(items[index].x0); + } else if (strcmp(token, "y0") == 0) { + elem = &(items[index].y0); + } else if (strcmp(token, "x1") == 0) { + elem = &(items[index].x1); + } else if (strcmp(token, "y1") == 0) { + elem = &(items[index].y1); + ++index; + } else { + if (elem) { + char *end = &(token[strlen(token)]); + *elem = strtod(token, &end); + elem = nullptr; + } + } + + token = strtok(NULL, delim); + } + + clock_t mid_time = clock(); + + double sum = 0.0; + + for (int i = 0; i < ITEM_COUNT; ++i) { + sum += haversine_of_degrees(items[i].x0, items[i].y0, items[i].x1, + items[i].y1, EARTH_RADIUS_KM); + } + + double average = sum / ITEM_COUNT; + + clock_t end_time = clock(); + + printf("Result: %.16f\n", average); + printf("Input = %.16f seconds\n", + (double)(mid_time - start_time) / CLOCKS_PER_SEC); + printf("Math = %.16f seconds\n", + (double)(end_time - mid_time) / CLOCKS_PER_SEC); + printf("Total = %.16f seconds\n", + (double)(end_time - start_time) / CLOCKS_PER_SEC); + printf("Throughput = %.16f haversines/second\n", + ITEM_COUNT / ((double)(end_time - start_time) / CLOCKS_PER_SEC)); + +#if DEBUG + int precision = 16; + for (int i = 0; i < ITEM_COUNT; ++i) { + printf("%.*f\n%.*f\n%.*f\n%.*f\n\n", precision, items[i].x0, precision, + items[i].y0, precision, items[i].x1, precision, items[i].y1); + } +#endif + + free(text); + + free(items); + + fclose(fp); + + return 0; +} diff --git a/python/haversine_algorithm.py b/python/haversine_algorithm.py new file mode 100644 index 0000000..377a401 --- /dev/null +++ b/python/haversine_algorithm.py @@ -0,0 +1,14 @@ +from math import radians, sin, cos, sqrt, asin + + +def haversine_of_degrees(x0, y0, x1, y1, radius): + dy = radians(y1 - y0) + dx = radians(x1 - x0) + y0 = radians(y0) + y1 = radians(y1) + + root_term = (sin(dy / 2) ** 2) + cos(y0) * cos(y1) * (sin(dx / 2) ** 2) + + result = 2 * radius * asin(sqrt(root_term)) + + return result diff --git a/python/haversine_json.py b/python/haversine_json.py new file mode 100644 index 0000000..c190a6a --- /dev/null +++ b/python/haversine_json.py @@ -0,0 +1,40 @@ +# Naive Python version of the code + + +import os +from haversine_algorithm import haversine_of_degrees +import time +import json + + +filepath = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "data_10000000_flex.json", +) + +with open(filepath, "r") as json_file: + start_time = time.time() + json_input = json.load(json_file) + mid_time = time.time() + + +earth_radius_km = 6371 +sum = 0 +count = 0 + +for pair in json_input["pairs"]: + sum += haversine_of_degrees( + pair["x0"], pair["y0"], pair["x1"], pair["y1"], earth_radius_km + ) + + count += 1 + +average = sum / count + +end_time = time.time() + +print(f"Result: {average}") +print(f"Input = {mid_time - start_time} seconds") +print(f"Math = {end_time - mid_time} seconds") +print(f"Total = {end_time - start_time} seconds") +print(f"Throughput = {count / (end_time - start_time)} haversines/second") diff --git a/test_data.json b/test_data.json new file mode 100644 index 0000000..d29dfc3 --- /dev/null +++ b/test_data.json @@ -0,0 +1,16 @@ +{ + "pairs": [ + { + "x0": -4.618412003630709, + "y0": 135.8153583196389, + "x1": -12.383220322958223, + "y1": 85.20071945064029 + }, + { + "x0": 47.817641963713584, + "y0": 111.40590001495877, + "x1": -34.52607922832944, + "y1": -127.52991299863771 + } + ] +}