Mandelbrot图像(Mandelbrot Set)是由法国数学家Benoît B. Mandelbrot在20世纪70年代研究分形几何时引入的。Mandelbrot集合是一个复杂的、广义的图形,它展示了无限复杂的边界结构。这种图像是通过迭代复数公式生成的,通常是非常美丽且具有自相似性质的分形图案。
Mandelbrot集合定义
Mandelbrot集合是所有在复平面上满足特定条件的复数集合。具体来说,Mandelbrot集合的定义是基于迭代公式:
[ z_{n+1} = z_n^2 + c ]
其中,( z_0 = 0 ),( c ) 是一个复数。
判断复数是否属于Mandelbrot集合
一个复数 ( c ) 是否属于Mandelbrot集合取决于迭代过程是否发散。具体步骤如下:
- 对于给定的复数 ( c ),从 ( z_0 = 0 ) 开始,迭代计算 ( z_{n+1} = z_n^2 + c )。
- 如果在某个迭代步骤中 ( |z_n| ) 超过某个阈值(通常是2),则认为序列发散,复数 ( c ) 不属于Mandelbrot集合。
- 如果在一定的迭代次数内(例如1000次),序列没有发散,则认为 ( c ) 属于Mandelbrot集合。
Mandelbrot图像的生成
Mandelbrot图像通常通过以下步骤生成:
- 复平面格点化:将复平面上的某个区域(例如从-2到2的实部和虚部)划分为网格。
- 迭代计算:对每个网格点(对应一个复数 ( c )),进行迭代计算,判断是否发散。
- 颜色映射:对于发散的点,根据发散速度(即在多少次迭代后发散)分配不同的颜色;对于不发散的点,通常填充为黑色。
Mandelbrot图像的特点
- 自相似性:Mandelbrot图像在不同尺度上显示相似的结构,这种自相似性是分形的重要特征。
- 复杂边界:虽然Mandelbrot集合的边界是有限的,但其细节是无限复杂的,放大任意部分都会看到新的复杂结构。
- 美学价值:由于其独特的形状和丰富的色彩,Mandelbrot图像在艺术和计算机图形学中有重要的应用。
Mandelbrot图像展示了数学中的一种奇妙的分形结构,通过简单的复数迭代公式,可以生成出复杂且美丽的图案。这种图像不仅在数学研究中具有重要意义,还因为其美学特征而在艺术领域广受欢迎。
下面开始实现c语言画图来体验一下并且实现并行化
第一个,渐变色输出Mandelbrot图像
代码如下
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#define WIDTH 640
#define HEIGHT 480
#define MAX_ITER 1000
#define FILENAME "mandelbrot_color_beautiful.bmp"
#pragma pack(push, 1)
typedef struct {
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
uint32_t header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t image_size;
int32_t x_pixels_per_meter;
int32_t y_pixels_per_meter;
uint32_t colors_used;
uint32_t important_colors;
} BMPHeader;
#pragma pack(pop)
void save_image(uint8_t* data) {
BMPHeader header;
header.type = 0x4D42;
header.size = sizeof(BMPHeader) + WIDTH * HEIGHT * 3;
header.reserved1 = 0;
header.reserved2 = 0;
header.offset = sizeof(BMPHeader);
header.header_size = 40;
header.width = WIDTH;
header.height = HEIGHT;
header.planes = 1;
header.bits_per_pixel = 24;
header.compression = 0;
header.image_size = 0;
header.x_pixels_per_meter = 0;
header.y_pixels_per_meter = 0;
header.colors_used = 0;
header.important_colors = 0;
FILE* file = fopen(FILENAME, "wb");
if (!file) {
fprintf(stderr, "Error: Could not open file for writing.\n");
exit(1);
}
fwrite(&header, sizeof(BMPHeader), 1, file);
fwrite(data, 1, WIDTH * HEIGHT * 3, file);
fclose(file);
}
int mandelbrot(double x0, double y0) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < MAX_ITER) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
void hsv_to_rgb(double h, double s, double v, uint8_t* r, uint8_t* g, uint8_t* b) {
int i;
double f, p, q, t;
if (s == 0) {
*r = *g = *b = (uint8_t)(v * 255);
return;
}
h /= 60;
i = (int)floor(h);
f = h - i;
p = v * (1 - s);
q = v * (1 - s * f);
t = v * (1 - s * (1 - f));
switch (i) {
case 0: *r = (uint8_t)(v * 255); *g = (uint8_t)(t * 255); *b = (uint8_t)(p * 255); break;
case 1: *r = (uint8_t)(q * 255); *g = (uint8_t)(v * 255); *b = (uint8_t)(p * 255); break;
case 2: *r = (uint8_t)(p * 255); *g = (uint8_t)(v * 255); *b = (uint8_t)(t * 255); break;
case 3: *r = (uint8_t)(p * 255); *g = (uint8_t)(q * 255); *b = (uint8_t)(v * 255); break;
case 4: *r = (uint8_t)(t * 255); *g = (uint8_t)(p * 255); *b = (uint8_t)(v * 255); break;
default: *r = (uint8_t)(v * 255); *g = (uint8_t)(p * 255); *b = (uint8_t)(q * 255); break;
}
}
void beautiful_hsv_to_rgb(double h, double s, double v, uint8_t* r, uint8_t* g, uint8_t* b) {
double nh = fmod(h + 60, 360); // shift hue
hsv_to_rgb(nh, s, v, r, g, b);
}
int main() {
uint8_t* image = (uint8_t*)malloc(WIDTH * HEIGHT * 3);
if (!image) {
fprintf(stderr, "Error: Memory allocation failed.\n");
return 1;
}
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / WIDTH;
double dy = (ymax - ymin) / HEIGHT;
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag);
double hue = (double)iteration / MAX_ITER * 360.0;
double saturation = 1.0;
double value = (iteration < MAX_ITER) ? 1.0 : 0.0;
uint8_t r, g, b;
beautiful_hsv_to_rgb(hue, saturation, value, &r, &g, &b);
image[(y * WIDTH + x) * 3] = r; // R
image[(y * WIDTH + x) * 3 + 1] = g; // G
image[(y * WIDTH + x) * 3 + 2] = b; // B
}
}
save_image(image);
free(image);
printf("Beautiful Image generated successfully: %s\n", FILENAME);
return 0;
}
普通版本黑白输出Mandelbrot图像
代码如下
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#define WIDTH 640
#define HEIGHT 480
#define MAX_ITER 1000
#define FILENAME "mandelbrot.bmp"
#pragma pack(push, 1)
typedef struct {
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
uint32_t header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t image_size;
int32_t x_pixels_per_meter;
int32_t y_pixels_per_meter;
uint32_t colors_used;
uint32_t important_colors;
} BMPHeader;
#pragma pack(pop)
void save_image(uint8_t* data) {
BMPHeader header;
header.type = 0x4D42;
header.size = sizeof(BMPHeader) + WIDTH * HEIGHT * 3;
header.reserved1 = 0;
header.reserved2 = 0;
header.offset = sizeof(BMPHeader);
header.header_size = 40;
header.width = WIDTH;
header.height = HEIGHT;
header.planes = 1;
header.bits_per_pixel = 24;
header.compression = 0;
header.image_size = 0;
header.x_pixels_per_meter = 0;
header.y_pixels_per_meter = 0;
header.colors_used = 0;
header.important_colors = 0;
FILE* file = fopen(FILENAME, "wb");
if (!file) {
fprintf(stderr, "Error: Could not open file for writing.\n");
exit(1);
}
fwrite(&header, sizeof(BMPHeader), 1, file);
fwrite(data, 1, WIDTH * HEIGHT * 3, file);
fclose(file);
}
int mandelbrot(double x0, double y0) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < MAX_ITER) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
int main() {
uint8_t* image = (uint8_t*)malloc(WIDTH * HEIGHT * 3);
if (!image) {
fprintf(stderr, "Error: Memory allocation failed.\n");
return 1;
}
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / WIDTH;
double dy = (ymax - ymin) / HEIGHT;
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag);
int color = iteration % 256;
image[(y * WIDTH + x) * 3] = color; // R
image[(y * WIDTH + x) * 3 + 1] = color; // G
image[(y * WIDTH + x) * 3 + 2] = color; // B
}
}
save_image(image);
free(image);
printf("Image generated successfully: %s\n", FILENAME);
return 0;
}
基础使用MPI库来实现并行计算,通过在不同的进程中并行计算不同部分的图像数据,输出运行时间,效率,加速比(后面是指定长宽)
运行计算加速比
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <mpi.h>
#define WIDTH 640
#define HEIGHT 480
#define MAX_ITER 1000
int mandelbrot(double x0, double y0) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < MAX_ITER) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
int main(int argc, char** argv) {
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int thread_counts[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
int num_threads = sizeof(thread_counts) / sizeof(thread_counts[0]);
double times[128];
double elapsed_time;
double baseline_time;
for (int i = 0; i < num_threads; i++) {
int thread_count = thread_counts[i];
MPI_Barrier(MPI_COMM_WORLD);
elapsed_time = -MPI_Wtime();
MPI_Comm new_comm;
MPI_Comm_split(MPI_COMM_WORLD, rank < thread_count, rank, &new_comm);
if (rank < thread_count) {
int rows_per_process = HEIGHT / thread_count;
uint8_t* local_image = (uint8_t*)malloc(rows_per_process * WIDTH * 3);
if (!local_image) {
fprintf(stderr, "Error: Memory allocation failed.\n");
MPI_Finalize();
return 1;
}
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / WIDTH;
double dy = (ymax - ymin) / HEIGHT;
double start_time = MPI_Wtime();
int start_row = rank * rows_per_process;
int end_row = (rank + 1) * rows_per_process;
for (int y = start_row; y < end_row; y++) {
for (int x = 0; x < WIDTH; x++) {
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag);
int color = iteration % 256;
local_image[((y - start_row) * WIDTH + x) * 3] = color; // R
local_image[((y - start_row) * WIDTH + x) * 3 + 1] = color; // G
local_image[((y - start_row) * WIDTH + x) * 3 + 2] = color; // B
}
}
double end_time = MPI_Wtime();
elapsed_time += end_time - start_time;
free(local_image);
}
MPI_Barrier(MPI_COMM_WORLD);
elapsed_time += MPI_Wtime();
MPI_Reduce(&elapsed_time, &baseline_time, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
if (rank == 0) {
times[i] = baseline_time;
printf("Thread %d: Time taken: %f seconds\n", thread_count, times[i]);
}
}
if (rank == 0) {
printf("\nThread\t Time\t\t Acceleration\t Efficiency\n");
for (int i = 0; i < num_threads; i++) {
double acceleration = times[0] / times[i];
double efficiency = acceleration / thread_counts[i];
printf("%d\t %.6f\t %.6f\t %.6f\n", thread_counts[i], times[i], acceleration, efficiency);
}
}
MPI_Finalize();
return 0;
}
基础使用openmp库来实现并行计算,通过在不同的进程中并行计算不同部分的图像数据,输出运行时间,效率,加速比(后面是指定长宽)
代码如下
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <omp.h>
#define WIDTH 640
#define HEIGHT 480
#define MAX_ITER 1000
int mandelbrot(double x0, double y0) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < MAX_ITER) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
int main() {
int thread_counts[] = { 1, 2, 4, 8, 16, 32 };
int num_threads = sizeof(thread_counts) / sizeof(thread_counts[0]);
double times[128];
for (int i = 0; i < num_threads; i++) {
int thread_count = thread_counts[i];
double start_time = omp_get_wtime();
#pragma omp parallel num_threads(thread_count)
{
int tid = omp_get_thread_num();
int num_threads = omp_get_num_threads();
int rows_per_thread = HEIGHT / num_threads;
int start_row = tid * rows_per_thread;
int end_row = (tid + 1) * rows_per_thread;
for (int y = start_row; y < end_row; y++) {
for (int x = 0; x < WIDTH; x++) {
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / WIDTH;
double dy = (ymax - ymin) / HEIGHT;
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag);
int color = iteration % 256;
}
}
}
double end_time = omp_get_wtime();
double elapsed_time = end_time - start_time;
times[i] = elapsed_time;
printf("Thread %d: Time taken: %f seconds\n", thread_count, times[i]);
}
printf("\nThread\t Time\t\t Acceleration\t Efficiency\n");
for (int i = 0; i < num_threads; i++) {
double acceleration = times[0] / times[i];
double efficiency = acceleration / thread_counts[i];
printf("%d\t %.6f\t %.6f\t %.6f\n", thread_counts[i], times[i], acceleration, efficiency);
}
return 0;
}
openmp基础指定图长宽
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <omp.h>
#define MAX_ITER 1000
int mandelbrot(double x0, double y0, int max_iter) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < max_iter) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
int main() {
int width, height;
printf("Enter the width of the Mandelbrot set image: ");
scanf("%d", &width);
printf("Enter the height of the Mandelbrot set image: ");
scanf("%d", &height);
int thread_counts[] = { 1, 2, 4, 8, 16, 32 };
int num_threads = sizeof(thread_counts) / sizeof(thread_counts[0]);
double times[128];
for (int i = 0; i < num_threads; i++) {
int thread_count = thread_counts[i];
double start_time = omp_get_wtime();
#pragma omp parallel num_threads(thread_count)
{
int tid = omp_get_thread_num();
int num_threads = omp_get_num_threads();
int rows_per_thread = height / num_threads;
int start_row = tid * rows_per_thread;
int end_row = (tid + 1) * rows_per_thread;
for (int y = start_row; y < end_row; y++) {
for (int x = 0; x < width; x++) {
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / width;
double dy = (ymax - ymin) / height;
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag, MAX_ITER);
int color = iteration % 256;
}
}
}
double end_time = omp_get_wtime();
double elapsed_time = end_time - start_time;
times[i] = elapsed_time;
printf("Thread %d: Time taken: %f seconds\n", thread_count, times[i]);
}
printf("\nThread\t Time\t\t Acceleration\t Efficiency\n");
for (int i = 0; i < num_threads; i++) {
double acceleration = times[0] / times[i];
double efficiency = acceleration / thread_counts[i];
printf("%d\t %.6f\t %.6f\t %.6f\n", thread_counts[i], times[i], acceleration, efficiency);
}
return 0;
}
mpi的基础指定图长宽
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <mpi.h>
#define MAX_ITER 1000
int mandelbrot(double x0, double y0, int max_iter) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < max_iter) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
int main(int argc, char** argv) {
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int thread_counts[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
int num_threads = sizeof(thread_counts) / sizeof(thread_counts[0]);
double times[128];
double elapsed_time;
double baseline_time;
int width, height;
if (rank == 0) {
printf("Enter the width of the Mandelbrot set image: ");
scanf("%d", &width);
printf("Enter the height of the Mandelbrot set image: ");
scanf("%d", &height);
}
MPI_Bcast(&width, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(&height, 1, MPI_INT, 0, MPI_COMM_WORLD);
for (int i = 0; i < num_threads; i++) {
int thread_count = thread_counts[i];
MPI_Barrier(MPI_COMM_WORLD);
elapsed_time = -MPI_Wtime();
MPI_Comm new_comm;
MPI_Comm_split(MPI_COMM_WORLD, rank < thread_count, rank, &new_comm);
if (rank < thread_count) {
int rows_per_process = height / thread_count;
uint8_t* local_image = (uint8_t*)malloc(rows_per_process * width * 3);
if (!local_image) {
fprintf(stderr, "Error: Memory allocation failed.\n");
MPI_Finalize();
return 1;
}
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / width;
double dy = (ymax - ymin) / height;
double start_time = MPI_Wtime();
int start_row = rank * rows_per_process;
int end_row = (rank + 1) * rows_per_process;
for (int y = start_row; y < end_row; y++) {
for (int x = 0; x < width; x++) {
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag, MAX_ITER);
int color = iteration % 256;
local_image[((y - start_row) * width + x) * 3] = color; // R
local_image[((y - start_row) * width + x) * 3 + 1] = color; // G
local_image[((y - start_row) * width + x) * 3 + 2] = color; // B
}
}
double end_time = MPI_Wtime();
elapsed_time += end_time - start_time;
free(local_image);
}
MPI_Barrier(MPI_COMM_WORLD);
elapsed_time += MPI_Wtime();
MPI_Reduce(&elapsed_time, &baseline_time, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
if (rank == 0) {
times[i] = baseline_time;
printf("Thread %d: Time taken: %f seconds\n", thread_count, times[i]);
}
}
if (rank == 0) {
printf("\nThread\t Time\t\t Acceleration\t Efficiency\n");
for (int i = 0; i < num_threads; i++) {
double acceleration = times[0] / times[i];
double efficiency = acceleration / thread_counts[i];
printf("%d\t %.6f\t %.6f\t %.6f\n", thread_counts[i], times[i], acceleration, efficiency);
}
}
MPI_Finalize();
return 0;
}
openmp指定,然后打印图片
解释为什么,他输出的全是完整的图片,
在OpenMP程序中,每个线程都会参与计算完整的Mandelbrot图像。通过使用OpenMP中的并行化指令,比如#pragma omp parallel, #pragma omp for,和 #pragma omp sections,程序中的计算部分会被分配到不同的线程上,每个线程负责计算图像的一部分。但是,所有的线程都操作同一个共享的图像数据结构,这意味着所有线程计算出的像素会被正确地写入到同一个图像缓冲区中,最终生成的图像是完整的。
与MPI不同,OpenMP程序中的线程可以直接访问共享内存,因此它们能够在同一个内存空间中工作。这意味着它们能够共享同一个图像数据结构,并且在计算过程中不需要显式地进行通信。因此,每个线程计算出的结果会直接写入到共享的图像缓冲区中,最终生成的图像是完整的,并且不同线程之间的图像数据是一致的。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <omp.h>
#define MAX_ITER 1000
#pragma pack(push, 1)
typedef struct {
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
uint32_t dib_header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t image_size;
int32_t x_pixels_per_meter;
int32_t y_pixels_per_meter;
uint32_t total_colors;
uint32_t important_colors;
} BMPHeader;
#pragma pack(pop)
int mandelbrot(double x0, double y0, int max_iter) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < max_iter) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
int main() {
int width, height;
printf("Enter the width of the Mandelbrot set image: ");
scanf("%d", &width);
printf("Enter the height of the Mandelbrot set image: ");
scanf("%d", &height);
int thread_counts[] = { 1, 2, 4, 8, 16, 32 };
int num_threads = sizeof(thread_counts) / sizeof(thread_counts[0]);
double times[128];
for (int i = 0; i < num_threads; i++) {
int thread_count = thread_counts[i];
double start_time = omp_get_wtime();
// Allocate memory for image buffer
uint8_t *image = (uint8_t *)malloc(width * height * 3 * sizeof(uint8_t));
#pragma omp parallel num_threads(thread_count)
{
int tid = omp_get_thread_num();
int num_threads = omp_get_num_threads();
int rows_per_thread = height / num_threads;
int start_row = tid * rows_per_thread;
int end_row = (tid + 1) * rows_per_thread;
for (int y = start_row; y < end_row; y++) {
for (int x = 0; x < width; x++) {
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / width;
double dy = (ymax - ymin) / height;
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag, MAX_ITER);
int color = iteration % 256;
// Assign color to the pixel
int index = ((height - 1 - y) * width + x) * 3;
image[index] = color; // Red
image[index + 1] = color; // Green
image[index + 2] = color; // Blue
}
}
}
double end_time = omp_get_wtime();
double elapsed_time = end_time - start_time;
times[i] = elapsed_time;
printf("Thread %d: Time taken: %f seconds\n", thread_count, times[i]);
// Write image to BMP file
char filename[50];
sprintf(filename, "mandelbrot_%d.bmp", thread_count);
FILE *file = fopen(filename, "wb");
if (file == NULL) {
fprintf(stderr, "Error: Unable to create file %s\n", filename);
return 1;
}
BMPHeader header = {0};
header.type = 0x4D42;
header.size = sizeof(BMPHeader) + width * height * 3;
header.offset = sizeof(BMPHeader);
header.dib_header_size = 40;
header.width = width;
header.height = height;
header.planes = 1;
header.bits_per_pixel = 24;
header.compression = 0;
header.image_size = width * height * 3;
header.x_pixels_per_meter = 0;
header.y_pixels_per_meter = 0;
header.total_colors = 0;
header.important_colors = 0;
fwrite(&header, sizeof(BMPHeader), 1, file);
fwrite(image, sizeof(uint8_t), width * height * 3, file);
fclose(file);
free(image);
}
printf("\nThread\t Time\t\t Acceleration\t Efficiency\n");
for (int i = 0; i < num_threads; i++) {
double acceleration = times[0] / times[i];
double efficiency = acceleration / thread_counts[i];
printf("%d\t %.6f\t %.6f\t %.6f\n", thread_counts[i], times[i], acceleration, efficiency);
}
printf("\nImage generation complete. Saved as BMP files.\n");
return 0;
}
mpi指定,然后输出图片
解释:为什么图片输出是一部分一部分
MPI程序在生成Mandelbrot图像时,每个进程负责计算一部分图像的像素。具体来说,每个进程根据给定的宽度和高度划分为多个行,并计算这些行对应的像素。然后,它们将计算得到的像素数据收集到根进程,根进程负责将所有收集到的像素数据写入到BMP文件中。
因为每个进程计算的部分是不同的,所以每个进程生成的图像也会不同。这是因为Mandelbrot集的图像是通过迭代计算每个像素的值而生成的,而不同的计算范围和起始点可能导致不同的迭代路径,从而产生不同的图像。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <mpi.h>
#define MAX_ITER 1000
#pragma pack(push, 1)
typedef struct {
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
uint32_t dib_header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t image_size;
int32_t x_pixels_per_meter;
int32_t y_pixels_per_meter;
uint32_t total_colors;
uint32_t important_colors;
} BMPHeader;
#pragma pack(pop)
int mandelbrot(double x0, double y0, int max_iter) {
double x = 0.0;
double y = 0.0;
int iteration = 0;
while (x * x + y * y <= 4.0 && iteration < max_iter) {
double xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
iteration++;
}
return iteration;
}
int main(int argc, char** argv) {
MPI_Init(&argc, &argv);
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int width, height;
if (rank == 0) {
printf("Enter the width of the Mandelbrot set image: ");
scanf("%d", &width);
printf("Enter the height of the Mandelbrot set image: ");
scanf("%d", &height);
}
MPI_Bcast(&width, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(&height, 1, MPI_INT, 0, MPI_COMM_WORLD);
int thread_counts[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
int num_threads = sizeof(thread_counts) / sizeof(thread_counts[0]);
double times[128];
double elapsed_time;
double baseline_time;
for (int i = 0; i < num_threads; i++) {
int thread_count = thread_counts[i];
MPI_Barrier(MPI_COMM_WORLD);
elapsed_time = -MPI_Wtime();
MPI_Comm new_comm;
MPI_Comm_split(MPI_COMM_WORLD, rank < thread_count, rank, &new_comm);
if (rank < thread_count) {
int rows_per_process = height / thread_count;
uint8_t* local_image = (uint8_t*)malloc(rows_per_process * width * 3);
if (!local_image) {
fprintf(stderr, "Error: Memory allocation failed.\n");
MPI_Finalize();
return 1;
}
double xmin = -2.0;
double xmax = 2.0;
double ymin = -1.5;
double ymax = 1.5;
double dx = (xmax - xmin) / width;
double dy = (ymax - ymin) / height;
double start_time = MPI_Wtime();
int start_row = rank * rows_per_process;
int end_row = (rank + 1) * rows_per_process;
for (int y = start_row; y < end_row; y++) {
for (int x = 0; x < width; x++) {
double real = xmin + x * dx;
double imag = ymax - y * dy;
int iteration = mandelbrot(real, imag, MAX_ITER);
int color = iteration % 256;
local_image[((y - start_row) * width + x) * 3] = color; // R
local_image[((y - start_row) * width + x) * 3 + 1] = color; // G
local_image[((y - start_row) * width + x) * 3 + 2] = color; // B
}
}
double end_time = MPI_Wtime();
elapsed_time += end_time - start_time;
// Gather all local images
uint8_t* global_image = NULL;
if (rank == 0) {
global_image = (uint8_t*)malloc(height * width * 3);
if (!global_image) {
fprintf(stderr, "Error: Memory allocation failed.\n");
MPI_Finalize();
return 1;
}
}
MPI_Gather(local_image, rows_per_process * width * 3, MPI_UNSIGNED_CHAR, global_image, rows_per_process * width * 3, MPI_UNSIGNED_CHAR, 0, MPI_COMM_WORLD);
free(local_image);
// Write image to BMP file
if (rank == 0) {
char filename[50];
sprintf(filename, "mandelbrot_%d.bmp", thread_count);
FILE* file = fopen(filename, "wb");
if (file == NULL) {
fprintf(stderr, "Error: Unable to create file %s\n", filename);
MPI_Finalize();
return 1;
}
BMPHeader header = { 0 };
header.type = 0x4D42;
header.size = sizeof(BMPHeader) + width * height * 3;
header.offset = sizeof(BMPHeader);
header.dib_header_size = 40;
header.width = width;
header.height = height;
header.planes = 1;
header.bits_per_pixel = 24;
header.compression = 0;
header.image_size = width * height * 3;
header.x_pixels_per_meter = 0;
header.y_pixels_per_meter = 0;
header.total_colors = 0;
header.important_colors = 0;
fwrite(&header, sizeof(BMPHeader), 1, file);
// Write pixel data
fwrite(global_image, sizeof(uint8_t), width * height * 3, file);
fclose(file);
printf("Image generated successfully and saved as %s\n", filename);
free(global_image);
}
}
MPI_Barrier(MPI_COMM_WORLD);
elapsed_time += MPI_Wtime();
MPI_Reduce(&elapsed_time, &baseline_time, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
if (rank == 0) {
times[i] = baseline_time;
printf("Thread %d: Time taken: %f seconds\n", thread_count, times[i]);
}
}
if (rank == 0) {
printf("\nThread\t Time\t\t Acceleration\t Efficiency\n");
for (int i = 0; i < num_threads; i++) {
double acceleration = times[0] / times[i];
double efficiency = acceleration / thread_counts[i];
printf("%d\t %.6f\t %.6f\t %.6f\n", thread_counts[i], times[i], acceleration, efficiency);
}
}
MPI_Finalize();
return 0;
}