#define _POSIX_C_SOURCE 199309L // for CLOCK_MONOTONIC #include #include #include #include #include // for getopt #include // for getopt_long #include // for strcpy #include // for benchmarking // All implementations #include "tricorn.h" // Final implmementation #include "tricorn_V1.h" #include "tricorn_V2.h" #include "tricorn_V3.h" #include "tricorn_V4.h" #include "tricorn_V5.h" void save_bmp(const char *filename, unsigned char *img, size_t width, ssize_t height,_Bool color); void benchmarking(size_t benchmarking_amount, int version_to_use, float complex complexA, size_t width, ssize_t height, float resolution, unsigned n, _Bool colored, unsigned char *tempArr) { struct timespec start; struct timespec end; void (*tricorn_implementation)(float complex, size_t, ssize_t, float, unsigned, _Bool, unsigned char*); switch (version_to_use) { case 0: // Uses highly optimized SIMD and threaded version / main implementation printf("Benchmarking V0 - highly optimized SIMD + Threading implementation\n"); tricorn_implementation = tricorn; break; case 1: // Uses unoptimised scalar comparison implementation printf("Benchmarking V1 - unoptimized comparison implementation\n"); tricorn_implementation = tricorn_V1; break; case 2: // Uses optimized SISD implementation printf("Benchmarking V2 - optimized SISD implementation\n"); tricorn_implementation = tricorn_V2; break; case 3: // Uses unoptimized SIMD implementation printf("Benchmarking V3 - unoptimized SIMD implementation\n"); tricorn_implementation = tricorn_V3; break; case 4: // Uses highly optimized SIMD printf("Benchmarking V4 - optimized SIMD implementation\n"); tricorn_implementation = tricorn_V4; break; case 5: // Experimental cleanups and inlines / for testing printf("Benchmarking V5 - experimental implementation\n"); tricorn_implementation = tricorn_V5; break; default: fprintf(stderr,"Invalid version to use: V%d. Available versions are : V0,V1,V2,V3,V4,V5\n",version_to_use); return; } printf("\n"); clock_gettime(CLOCK_MONOTONIC, &start); for (size_t i = 0; i < benchmarking_amount; i++) { tricorn_implementation(complexA,width,height,resolution,n,colored,tempArr); } clock_gettime(CLOCK_MONOTONIC, &end); double time = end.tv_sec- start.tv_sec + 1e-9 * (end.tv_nsec- start.tv_nsec); printf("The total amount of time spent for %zu function calls: %lf seconds\n", benchmarking_amount,time); double average = time / benchmarking_amount; printf("Average time per function: %lf seconds\n", average); } unsigned tests(void) { printf("Doing testing\n\n"); size_t width = 1920; size_t height = 1080; unsigned char *tempArrColored = malloc(width*height*3); unsigned char *tempArrNotColored = malloc(width*height*1); if (tempArrColored == NULL || tempArrNotColored == NULL) { fprintf(stderr,"Failed to allocate space for image\n"); return EXIT_FAILURE; } printf("\nTest 1: Default image, not coloured\n"); benchmarking(3,0,-2.0f - 0.5f*I, width, height, 0.001f, 200, false, tempArrNotColored); printf("\nTest 2: Default image, coloured\n"); benchmarking(3,0,-2.0f - 0.5f*I, width, height, 0.001f, 200, true, tempArrColored); printf("\nTest 3: High iteration count\n"); benchmarking(3,0,-2.0f - 0.5f*I, width, height, 0.001f, 5000, false, tempArrNotColored); printf("\nTest 4: Zoomed image\n"); benchmarking(3,0,-0.5f - 0.5f*I, 1024, 1024, 0.0005f, 500, false, tempArrNotColored); free(tempArrColored); free(tempArrNotColored); return 0; } void checkInputs(_Bool n,_Bool r,_Bool o,_Bool d,_Bool s) { if (!n) { fprintf(stderr, "ERROR: Input n not given.\n"); } if (!r) { fprintf(stderr, "ERROR: Input r not given.\n"); } if (!o) { fprintf(stderr, "ERROR: Input o not given.\n"); } if (!d) { fprintf(stderr, "ERROR: Input d not given.\n"); } if (!s) { fprintf(stderr, "ERROR: Input s not given.\n"); } } // REMOVE LATER: interesting test command ./tricorn -s -2.0,0.6 -r 0.0010 -n 200 -d 1920,1080 -o image.bmp -C // AND FOR NEGATIVE HEIGHT: ./tricorn -s -2,-0.5 -r 0.0010 -n 200 -d 1920,-1080 -o image.bmp -C const char* help_messsage = "Help Message\n\n" "Usage:\n" " ./tricorn [OPTIONS]\n\n" "Options:\n" "-V \n" " Selects the implementation to use.\n" " -V 0 uses the main implementation.\n" " If no argument is given, -V 0 is used.\n" " Example: -V 1\n\n" "-B \n" " Benchmarks the selected implementation.\n" " The argument chooses how many times the function is called.\n" " Example: -B 5\n\n" "-s ,\n" " Sets the starting point of the function.\n" " Example: -s -2.0,1.5\n\n" "-d ,\n" " Sets the width and the height of the generated image.\n" " Example: -d 800,600\n\n" "-n \n" " Sets the maximum number of iterations per pixel.\n" " Example: -n 255\n\n" "-r \n" " Sets the resolution per pixel.\n" " Example: -r 0.01\n\n" "-o \n" " Sets the output file name.\n" " The file name must end with .bmp\n" " Example: -o image.bmp\n\n" "-C, --color\n" " Enables colored output.\n\n" "-h, --help\n" " Displays the help message and exits.\n\n" "-t, --test\n" " Runs the tests, prints the results, and exists.\n"; int main(int argc, char** argv) { int opt_long; // will be the returned optional character, // example: ./main -c -> opt_long here will be the optional 'c' const char* optstring = "V:B:s:d:n:r:o:Cht" ; // valid options that can be given to the programme. options with ":" after them expect an argument, which we'll later store in // example: B: -> optional B expects an argument after it, and is valid -> ./main -B 67 (optarg is now "67") // example: h -> optional h doesn't expect an argument -> ./main -h // optind -> keeps track of the next index. initialised as 1 in the beginning. calling opt_long updates it every time // optarg -> is a charptr. stores the arguments given to the optionals static struct option long_options[] = { {"color",0,0,'C'}, {"help",0,0,'h'}, {"test",0,0,'t'}, {0,0,0,0} }; // these are the long optionals, which we can call with double "--". "--help" or "--test". they are converted to the optionals "h" and "t" respectively // first argument is "name" -> name of the long. "--" // second is "has_arg" -> 0 if no arg, 1 if arg, 2 if optional arg // third is "*flag" -> sets a flag so that "--help" simply returns "h" and does nothing else // fourth is "val" -> what to return when "--help" is given, returns "h" int version_to_use = 0; // actual initial value _Bool colored = 0; // actual initial value _Bool will_benchmark = false; // actual initial value _Bool will_test = false; _Bool nGiven = false; _Bool rGiven = false; _Bool oGiven = false; _Bool dGiven = false; _Bool sGiven = false; size_t amount_function_called = 0; // initial values, so that the compiler doesn't complain unsigned n = 0; float starting_point_real = -1; float starting_point_imaginary = -1; size_t width = 0; ssize_t height = -1; float resolution = -1; char* output_data = NULL; opt_long = getopt_long(argc,argv,optstring, long_options, NULL); // first four arguments are self-explanatory, fifth argument is the index of which long_option we return in the struct "long_options", but it's irrelevant so we set it to NULL while (opt_long != -1) { switch (opt_long) { case 'V': {// done, function needed char *end; version_to_use = strtol(optarg,&end,10); if (version_to_use < 0 || version_to_use > 5 || end == optarg || *end != '\0') { fprintf(stderr,"Invalid version to use: V%s. Available versions are : V0,V1,V2,V3,V4,V5\n", optarg); return EXIT_FAILURE; } printf("version is %d\n", version_to_use); // call different version functions // Die Implementierung, die verwendet werden soll. Hierbei soll mit // -V 0 Ihre Hauptimplementierung verwendet werden. Wenn diese Option nicht // gesetzt wird, soll ebenfalls die Hauptimplementierung ausgeführt werden break; } case 'B': { // done, function needed // wird die Laufzeit der angegebenen Implementierung gemessen und ausgegeben (do this later) char *end; long value = strtol(optarg,&end,10); if (value <= 0 || end == optarg || *end != '\0') { fprintf(stderr, "Invalid benchmarking input: %s\n", optarg); return EXIT_FAILURE; } amount_function_called = (size_t)value; will_benchmark = true; printf("amount function called is %zu\n", amount_function_called); // call benchmarking function // Das Argument dieser Option gibt die Anzahl an Wiederholungen des Funktionsaufrufs an, z. B.-B 5 . break; } case 's': { sGiven = true; char *imaginary_string; char *end; starting_point_real = strtof(optarg,&imaginary_string); // split into two if (imaginary_string == optarg || *imaginary_string != ',') { fprintf(stderr, "Invalid first number or missing comma: %s\n",optarg); return EXIT_FAILURE; } starting_point_imaginary = strtof(imaginary_string + 1, &end); // second part, the imaginary part if (end == imaginary_string + 1 || *end != '\0') { fprintf(stderr, "Invalid second number or extra characters: %s\n",optarg); return EXIT_FAILURE; } printf("real %f, img %f\n", starting_point_real,starting_point_imaginary); // s , — Startpunkt der Berechnung break; } case 'd': { dGiven = true; char* height_string; char *end; long widthValue = strtol(optarg,&height_string,10); if (height_string == optarg || *height_string != ',') { fprintf(stderr, "Invalid width or missing comma: %s\n",optarg); return EXIT_FAILURE; } long heightValue = strtol(height_string + 1,&end,10); if (end == height_string + 1 || *end != '\0') { fprintf(stderr, "Invalid height or extra characters: %s\n",optarg); return EXIT_FAILURE; } if (widthValue <= 0 || heightValue == 0) { fprintf(stderr, "Width must be > 0 and height must not be 0: %s\n", optarg); return EXIT_FAILURE; } width = (size_t)widthValue; height = (ssize_t)heightValue; printf("width %zu, height %zd\n", width,height); // -d , —Breite und Höhe des Bildausschnitts break; } case 'n': { nGiven = true; char* end; long nValue = strtol(optarg,&end,10); if (end == optarg || *end != '\0' || nValue <= 0) { fprintf(stderr, "Invalid n value: %s\n", optarg); return EXIT_FAILURE; } n = (unsigned) nValue; printf("n:%u\n",n); // -n —MaximaleAnzahlanIterationen pro Bildpixel break; } case 'r': { rGiven = true; char *end; resolution = strtof(optarg,&end); if (end == optarg || *end != '\0' || resolution <= 0.0f) { fprintf(stderr, "Invalid floating point number: %s\n",optarg); return EXIT_FAILURE; } printf("r:%f\n", resolution); // r —Schrittweite pro Bildpixel break; } case 'o': oGiven = true; if (optarg[0] == '-') { fprintf(stderr, "Invalid output filename: %s\n",optarg); return EXIT_FAILURE; } output_data = malloc(strlen(optarg) + 1); if (output_data == NULL) { fprintf(stderr,"Memory allocation failed.\n"); return EXIT_FAILURE; } strcpy(output_data,optarg); printf("output data: %s\n", output_data); // test // -o —Ausgabedatei break; case 'C': // done colored = true; printf("colored:%d\n",colored); // C|--color —Farbausgabe break; case 'h': // done printf("%s",help_messsage); // print help message // Eine Beschreibung aller Optionen des Programms und Verwendungsbeispiele werden ausgegeben und das Programm danach beendet. // Die Beschreibung soll mit "Help Message" anfangen. return EXIT_SUCCESS; case 't': // done, function needed will_test = true; // Führt eine vordefinierte Testsuite aus, welche sämtliche von Ihnen // verwendete Benchmarking-Szenarien gesammelt durchläuft. Das Programm gibt // die jeweiligen Parameter, eine kurze Beschreibungen sowie die Ergebnisse in einer // übersichtlichen und gut lesbaren Form aus und terminiert anschließend. break; default: // idk // case '?' -> when the argument is not valid fprintf(stderr,"ERROR: Invalid argument -%c:\n",optopt); // print error msg return EXIT_FAILURE; } opt_long = getopt_long(argc,argv,optstring, long_options, NULL); } float complex complexA = (starting_point_real + starting_point_imaginary*I); int bytesPerColour = colored ? 3 : 1; ssize_t absolute_height = height >= 0 ? height : height * - 1; unsigned char *tempArr = malloc(width*absolute_height*bytesPerColour);// allocate image array on the heap to support large images as the stack is usually only 8 MB big if(tempArr == NULL) { exit(EXIT_FAILURE); } if (will_test) { return tests() ? EXIT_FAILURE : EXIT_SUCCESS; } if (!(nGiven && rGiven && oGiven && dGiven && sGiven)) { checkInputs(nGiven,rGiven,oGiven,dGiven,sGiven); free(tempArr); free(output_data); return EXIT_FAILURE; } if (will_benchmark) { printf("\nDoing benchmarking. No images will be created\n"); benchmarking(amount_function_called,version_to_use,complexA,width,height,resolution,n,colored,tempArr); free(tempArr); free(output_data); return EXIT_SUCCESS; } switch (version_to_use) { case 0: // Uses highly optimized SIMD and threaded version / main implementation tricorn(complexA,width,height,resolution,n,colored,tempArr); break; case 1: // Uses unoptimised comparison implementation tricorn_V1(complexA,width,height,resolution,n,colored,tempArr); break; case 2: // Uses optimized SISD version tricorn_V2(complexA,width,height,resolution,n,colored,tempArr); break; case 3: // Uses unoptimized SIMD version tricorn_V3(complexA,width,height,resolution,n,colored,tempArr); break; case 4: // Uses highly optimized SIMD tricorn_V4(complexA,width,height,resolution,n,colored,tempArr); break; case 5: // Experimental cleanups and inlines / for testing tricorn_V5(complexA,width,height,resolution,n,colored,tempArr); break; default: break; } save_bmp(output_data,tempArr,width,height,colored); free(output_data); free(tempArr); return 0; } void save_bmp(const char *filename, unsigned char *img, size_t width, ssize_t height,_Bool color) { // Open a file at the filepath given as a stream in the program FILE *fptr = fopen(filename,"wb"); if(fptr == NULL) { // Chech for errors with opening the file stream fprintf(stderr,"ERROR: Could not open output file at %s\n.",filename); exit(EXIT_FAILURE); } size_t bytesPerColor = color ? 3 : 1; int actual_height = (height < 0) ? -height : height; // Calculate absolute height for iteration and calculation purposes size_t row_padding = (4 - (width * bytesPerColor) % 4) % 4; // Calculate padding for each row as .bmp is 4 bytes aligned size_t row_width = width * bytesPerColor + row_padding; // Calculate row width with padding size_t image_size = actual_height * row_width; size_t file_size = image_size + 54; // File size = Image Size + Headers size_t header_offset = color ? 54 : 1078; // If colored then offset = 54 if not we add offset = 54 + 1024; 1024 Bytes for color pallete // File header - contains information about the file unsigned char file_header[14] = { 'B', 'M', // BM - identifies file as a .bmp file 0, 0, 0, 0, // 4 bytes for file size - will be set later 0, 0, 0, 0, // Reserved 0, 0, 0, 0 // 4 bytes that specify the offset where the image array begins }; // Serialize the size into 4 bytes - (unsigned char) takes only bottom 4 bytes 1 2 3 4 file_header[2] = (unsigned char)(file_size); file_header[3] = (unsigned char)(file_size >> 8); file_header[4] = (unsigned char)(file_size >> 16); file_header[5] = (unsigned char)(file_size >> 24); file_header[10] = (unsigned char)(header_offset); file_header[11] = (unsigned char)(header_offset >> 8); // Info header - contains information about the image itself unsigned char info_header[40] = { 40, 0, 0, 0, // Size of this header 0, 0, 0, 0, // Width in pixels - set later 0, 0, 0, 0, // Height in pixels - set later 1, 0, // Number of color planes - always 1 bytesPerColor * 8, 0, // Number of bits per pixel or color depth 0, 0, 0, 0, // Compression method - 0 for no compression 0, 0, 0, 0, // Image size - set later 0x13, 0x0B, 0, 0, // Horizontal resolution in pixels per metre - Set to standart 72 pixels per inch 0x13, 0x0B, 0, 0, // Vertical resolution in pixels per metre - Set to standart 72 pixels per inch 0, 0, 0, 0, // Number of colors in the color pallete - 0 for default of 2^n where n is bits per pixel 0, 0, 0, 0 // Number of important colors - 0 for all colors important }; // Setting width info_header[4] = (unsigned char)(width); info_header[5] = (unsigned char)(width >> 8); info_header[6] = (unsigned char)(width >> 16); info_header[7] = (unsigned char)(width >> 24); // Setting height uint32_t h = (uint32_t)height; info_header[8] = (unsigned char)(h); info_header[9] = (unsigned char)(h >> 8); info_header[10] = (unsigned char)(h >> 16); info_header[11] = (unsigned char)(h >> 24); // Setting image size info_header[20] = (unsigned char)(image_size); info_header[21] = (unsigned char)(image_size >> 8); info_header[22] = (unsigned char)(image_size >> 16); info_header[23] = (unsigned char)(image_size >> 24); // Write headers into file //size_t fwrite(const void ptr[restrict .size * .n], size_t size, size_t n, FILE *restrict stream); fwrite(file_header,1,14,fptr); fwrite(info_header,1,40,fptr); //If image is grayscale write color dictionary to tell reader what color to visualise for each value // Row 0 - Value 0: [R=0;B=0;G=0] // ... //Row i - Value i: [R=i;B=i;G=i] if (!color) { unsigned char palette[1024]; for (int i = 0; i < 256; i++) { palette[i * 4 + 0] = i; // B palette[i * 4 + 1] = i; // G palette[i * 4 + 2] = i; // R palette[i * 4 + 3] = 0; // Reserved (Padding) } fwrite(palette, 1, 1024, fptr); } // Save padding bytes as array so we can write from it easily to the file later unsigned char padding_bytes[3] = {0, 0, 0}; for(int i = 0; i < actual_height; i++) { // Write whole row of pixels - img + (i * width * 3) is the position of the row in the image array // size = 3 as we are interpreting every pixel as an object fwrite(img + (i * width * bytesPerColor), bytesPerColor, width, fptr); if(row_padding > 0) { fwrite(padding_bytes,1,row_padding,fptr); // Write row_padding number of padding bytes to the file stream } } fclose(fptr); }