init: HW2
This commit is contained in:
8 files changed
+815
No files matched your search
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
|
||||||
|
IndentWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
|
||||||
|
# Maximum line length before wrapping, 80 ~ 120 is common.
|
||||||
|
ColumnLimit: 100
|
||||||
|
|
||||||
|
IndentCaseLabels: true
|
||||||
|
|
||||||
|
# Force pointers away from the type.
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
PointerAlignment: Right
|
||||||
|
|
||||||
|
AlignConsecutiveMacros: Consecutive
|
||||||
|
|
||||||
|
# Allow short blocks and function definitions on a single line only if they're empty.
|
||||||
|
# This improves readability since single-line blocks are easier to overlook.
|
||||||
|
AllowShortBlocksOnASingleLine: Empty
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
|
||||||
|
# Pack function call arguments into as few lines as possible.
|
||||||
|
BinPackArguments: true
|
||||||
|
# Do not pack function declaration parameters into a single line.
|
||||||
|
# This helps readability when function signatures are long.
|
||||||
|
BinPackParameters: false
|
||||||
|
|
||||||
|
# Place binary operators at the beginning of the line to make them more visible.
|
||||||
|
BreakBeforeBinaryOperators: All
|
||||||
|
# Always break before braces.
|
||||||
|
BreakBeforeBraces: Allman
|
||||||
|
|
||||||
|
# Separate definition blocks with a blank line so they are easier to be identified.
|
||||||
|
SeparateDefinitionBlocks: Always
|
||||||
|
...
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
# Here is an explanation for why some of the checks are disabled:
|
||||||
|
#
|
||||||
|
# -bugprone-casting-through-void
|
||||||
|
# <mpi.h> defines macro that generates casting through void.
|
||||||
|
#
|
||||||
|
# -bugprone-easily-swappable-parameters
|
||||||
|
# Required big refactoring in the codebase. It was not worth it.
|
||||||
|
#
|
||||||
|
# -bugprone-implicit-widening-of-multiplication-result
|
||||||
|
# This makes the code too verbose. It's unlikely we're going to overflow.
|
||||||
|
#
|
||||||
|
# -clang-analyzer-optin.taint.TaintedAlloc
|
||||||
|
# Tainted allocations are used on the nodes where we currently don't have a
|
||||||
|
# good limit on the number of nodes.
|
||||||
|
#
|
||||||
|
# -performance-enum-size
|
||||||
|
# Keeping the code simple is more important than performance in this case.
|
||||||
|
#
|
||||||
|
# -performance-inefficient-string-concatenation
|
||||||
|
# It's common that we concatenate strings with +. For cases other than
|
||||||
|
# loops, using `append` is not worth it and makes the code less readable.
|
||||||
|
#
|
||||||
|
# -modernize-loop-convert
|
||||||
|
# As range-based for loops are only available since OpenMP 5,
|
||||||
|
# this check should not be used on code with a compatibility requirement of OpenMP prior to version 5.
|
||||||
|
#
|
||||||
|
# -modernize-use-auto:
|
||||||
|
# The use of auto is not always beneficial.
|
||||||
|
# It can make the code less readable and harder to understand.
|
||||||
|
#
|
||||||
|
# -modernize-use-nodiscard:
|
||||||
|
# The leading [[nodiscard]] attribute adds noise to the code when reading it
|
||||||
|
# and may not provide considerable benefits.
|
||||||
|
#
|
||||||
|
# -modernize-use-trailing-return-type:
|
||||||
|
# Most of the times, trailing return types provide no benefit.
|
||||||
|
# The additional "auto" and "->" are just noise.
|
||||||
|
#
|
||||||
|
# NOTE:
|
||||||
|
# This codebase heavily interacts with standard C libraries, so most of the `cppcoreguidelines`
|
||||||
|
# checks are disabled. In particular,
|
||||||
|
# cppcoreguidelines-init-variables: Many C library functions use output parameters.
|
||||||
|
# We intentionally avoid initializing variables passed to them,
|
||||||
|
# so that we get warnings if those variables are used before being properly set.
|
||||||
|
# cppcoreguidelines-pro-type-vararg: Variadic functions are used, as pre-C++20 lacks sufficient
|
||||||
|
# formatting support.
|
||||||
|
|
||||||
|
Checks: >
|
||||||
|
-*,
|
||||||
|
bugprone-*,
|
||||||
|
-bugprone-casting-through-void,
|
||||||
|
-bugprone-easily-swappable-parameters,
|
||||||
|
-bugprone-implicit-widening-of-multiplication-result,
|
||||||
|
performance-*,
|
||||||
|
-performance-enum-size,
|
||||||
|
-performance-inefficient-string-concatenation,
|
||||||
|
cppcoreguidelines-macro-usage,
|
||||||
|
cppcoreguidelines-no-malloc,
|
||||||
|
cppcoreguidelines-pro-type-cstyle-cast,
|
||||||
|
clang-analyzer-*,
|
||||||
|
modernize-*,
|
||||||
|
-modernize-loop-convert,
|
||||||
|
-modernize-use-auto,
|
||||||
|
-modernize-use-nodiscard,
|
||||||
|
-modernize-use-trailing-return-type,
|
||||||
|
readability-avoid-const-params-in-decls,
|
||||||
|
readability-avoid-nested-conditional-operator,
|
||||||
|
readability-avoid-return-with-void-value,
|
||||||
|
readability-avoid-unconditional-preprocessor-if,
|
||||||
|
readability-container-size-empty,
|
||||||
|
readability-delete-null-pointer,
|
||||||
|
readability-duplicate-include,
|
||||||
|
readability-else-after-return,
|
||||||
|
readability-identifier-naming,
|
||||||
|
readability-inconsistent-declaration-parameter-name,
|
||||||
|
readability-magic-numbers
|
||||||
|
readability-make-member-function-const,
|
||||||
|
readability-math-missing-parentheses,
|
||||||
|
readability-misleading-indentation,
|
||||||
|
readability-non-const-parameter,
|
||||||
|
readability-redundant-casting,
|
||||||
|
readability-redundant-control-flow,
|
||||||
|
readability-redundant-declaration,
|
||||||
|
readability-redundant-function-ptr-dereference,
|
||||||
|
readability-redundant-inline-specifier,
|
||||||
|
readability-redundant-preprocessor,
|
||||||
|
readability-reference-to-constructed-temporary,
|
||||||
|
readability-simplify-boolean-expr,
|
||||||
|
readability-simplify-subscript-expr,
|
||||||
|
readability-static-accessed-through-instance,
|
||||||
|
readability-static-definition-in-anonymous-namespace,
|
||||||
|
readability-string-compare,
|
||||||
|
|
||||||
|
# For details on the checks, see:
|
||||||
|
# https://clang.llvm.org/extra/clang-tidy/checks/list.html
|
||||||
|
|
||||||
|
CheckOptions:
|
||||||
|
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
|
||||||
|
- { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||||
|
- { key: readability-identifier-naming.UnionCase, value: CamelCase }
|
||||||
|
- { key: readability-identifier-naming.EnumCase, value: CamelCase }
|
||||||
|
- { key: readability-identifier-naming.TypeAliasCase, value: CamelCase }
|
||||||
|
- { key: readability-identifier-naming.TypedefCase, value: CamelCase }
|
||||||
|
- { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase }
|
||||||
|
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
|
||||||
|
- { key: readability-identifier-naming.MethodCase, value: lower_case }
|
||||||
|
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||||
|
- { key: readability-identifier-naming.ParameterCase, value: lower_case }
|
||||||
|
- { key: readability-identifier-naming.ConstantCase, value: UPPER_CASE }
|
||||||
|
- { key: readability-identifier-naming.ConstantParameterCase, value: lower_case }
|
||||||
|
- { key: readability-identifier-naming.LocalConstantCase, value: lower_case }
|
||||||
|
- { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE }
|
||||||
|
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
APP_NAME = mandelbrot
|
||||||
|
OBJDIR = objs
|
||||||
|
COMMONDIR = ./common
|
||||||
|
|
||||||
|
CXX = g++ -m64
|
||||||
|
CXXFLAGS = -I$(COMMONDIR) -I$(OBJDIR) -O3 -std=c++17 -Wall
|
||||||
|
LDLIBS = -lm -lpthread
|
||||||
|
|
||||||
|
PPM_CXX = $(COMMONDIR)/ppm.cpp
|
||||||
|
PPM_OBJ = $(addprefix $(OBJDIR)/, $(subst $(COMMONDIR)/,, $(PPM_CXX:.cpp=.o)))
|
||||||
|
|
||||||
|
|
||||||
|
default: $(APP_NAME)
|
||||||
|
|
||||||
|
dirs:
|
||||||
|
mkdir -p $(OBJDIR)/
|
||||||
|
.PHONY: dirs
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -r $(OBJDIR) *.ppm *~ $(APP_NAME)
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
OBJS = $(OBJDIR)/main.o $(OBJDIR)/mandelbrot_serial.o $(OBJDIR)/mandelbrot_thread.o $(PPM_OBJ)
|
||||||
|
|
||||||
|
$(APP_NAME): dirs $(OBJS)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $@ $(OBJS) $(LDLIBS)
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: %.cpp
|
||||||
|
$(CXX) $< $(CXXFLAGS) -c -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/%.o: $(COMMONDIR)/%.cpp
|
||||||
|
$(CXX) $< $(CXXFLAGS) -c -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/main.o: $(COMMONDIR)/cycle_timer.h
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
#ifndef SYRAH_CYCLE_TIMER_H
|
||||||
|
#define SYRAH_CYCLE_TIMER_H
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#else
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <mach/mach_time.h>
|
||||||
|
#endif // __x86_64__ or not
|
||||||
|
|
||||||
|
#include <cstdio> // fprintf
|
||||||
|
#include <cstdlib> // exit
|
||||||
|
|
||||||
|
#elif _WIN32
|
||||||
|
#include <time.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This uses the cycle counter of the processor. Different
|
||||||
|
// processors in the system will have different values for this. If
|
||||||
|
// you process moves across processors, then the delta time you
|
||||||
|
// measure will likely be incorrect. This is mostly for fine
|
||||||
|
// grained measurements where the process is likely to be on the
|
||||||
|
// same processor. For more global things you should use the
|
||||||
|
// Time interface.
|
||||||
|
|
||||||
|
// Also note that if you processors' speeds change (i.e. processors
|
||||||
|
// scaling) or if you are in a heterogenous environment, you will
|
||||||
|
// likely get spurious results.
|
||||||
|
class CycleTimer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using SysClock = unsigned long long;
|
||||||
|
|
||||||
|
//////////
|
||||||
|
// Return the current CPU time, in terms of clock ticks.
|
||||||
|
// Time zero is at some arbitrary point in the past.
|
||||||
|
static SysClock current_ticks()
|
||||||
|
{
|
||||||
|
#if defined(__APPLE__) && !defined(__x86_64__)
|
||||||
|
return mach_absolute_time();
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
LARGE_INTEGER qwTime;
|
||||||
|
QueryPerformanceCounter(&qwTime);
|
||||||
|
return qwTime.QuadPart;
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
unsigned int a, d;
|
||||||
|
asm volatile("rdtsc" : "=a"(a), "=d"(d));
|
||||||
|
return static_cast<unsigned long long>(a) | (static_cast<unsigned long long>(d) << 32);
|
||||||
|
#elif defined(__ARM_NEON__) && 0 // mrc requires superuser.
|
||||||
|
unsigned int val;
|
||||||
|
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(val));
|
||||||
|
return val;
|
||||||
|
#else
|
||||||
|
timespec spec;
|
||||||
|
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &spec);
|
||||||
|
return CycleTimer::SysClock(static_cast<float>(spec.tv_sec) * 1e9
|
||||||
|
+ static_cast<float>(spec.tv_nsec));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////
|
||||||
|
// Return the current CPU time, in terms of seconds.
|
||||||
|
// This is slower than current_ticks(). Time zero is at
|
||||||
|
// some arbitrary point in the past.
|
||||||
|
static double current_seconds()
|
||||||
|
{
|
||||||
|
return static_cast<double>(current_ticks()) * seconds_per_tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////
|
||||||
|
// Return the conversion from seconds to ticks.
|
||||||
|
static double ticks_per_second()
|
||||||
|
{
|
||||||
|
return 1.0 / seconds_per_tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *tick_units()
|
||||||
|
{
|
||||||
|
#if defined(__APPLE__) && !defined(__x86_64__)
|
||||||
|
return "ns";
|
||||||
|
#elif defined(__WIN32__) || defined(__x86_64__)
|
||||||
|
return "cycles";
|
||||||
|
#else
|
||||||
|
return "ns"; // clock_gettime
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////
|
||||||
|
// Return the conversion from ticks to seconds.
|
||||||
|
static double seconds_per_tick()
|
||||||
|
{
|
||||||
|
static bool initialized = false;
|
||||||
|
static double seconds_per_tick_val;
|
||||||
|
if (initialized)
|
||||||
|
return seconds_per_tick_val;
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#ifdef __x86_64__
|
||||||
|
int args[] = {CTL_HW, HW_CPU_FREQ};
|
||||||
|
unsigned int Hz;
|
||||||
|
size_t len = sizeof(Hz);
|
||||||
|
if (sysctl(args, 2, &Hz, &len, NULL, 0) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to initialize seconds_per_tick_val!\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
seconds_per_tick_val = 1.0 / (double)Hz;
|
||||||
|
#else
|
||||||
|
mach_timebase_info_data_t time_info;
|
||||||
|
mach_timebase_info(&time_info);
|
||||||
|
|
||||||
|
// Scales to nanoseconds without 1e-9f
|
||||||
|
seconds_per_tick_val
|
||||||
|
= (1e-9 * static_cast<double>(time_info.numer)) / static_cast<double>(time_info.denom);
|
||||||
|
#endif // x86_64 or not
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
LARGE_INTEGER qwTicksPerSec;
|
||||||
|
QueryPerformanceFrequency(&qwTicksPerSec);
|
||||||
|
seconds_per_tick_val = 1.0 / static_cast<double>(qwTicksPerSec.QuadPart);
|
||||||
|
#else
|
||||||
|
FILE *fp = fopen("/proc/cpuinfo", "r");
|
||||||
|
char input[1024]; // NOLINT(modernize-avoid-c-arrays): Extensively working with C libraries.
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s failed: couldn't find /proc/cpuinfo.", __PRETTY_FUNCTION__);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
// In case we don't find it, e.g. on the N900
|
||||||
|
seconds_per_tick_val = 1e-9;
|
||||||
|
while (!feof(fp) && fgets(input, 1024, fp))
|
||||||
|
{
|
||||||
|
// NOTE(boulos): Because reading cpuinfo depends on dynamic
|
||||||
|
// frequency scaling it's better to read the @ sign first
|
||||||
|
float ghz, mhz;
|
||||||
|
if (strstr(input, "model name"))
|
||||||
|
{
|
||||||
|
char *at_sign = strstr(input, "@");
|
||||||
|
if (at_sign)
|
||||||
|
{
|
||||||
|
char *after_at = at_sign + 1;
|
||||||
|
char *ghz_str = strstr(after_at, "ghz");
|
||||||
|
char *mhz_str = strstr(after_at, "mhz");
|
||||||
|
if (ghz_str)
|
||||||
|
{
|
||||||
|
*ghz_str = '\0';
|
||||||
|
if (1 == sscanf(after_at, "%f", &ghz))
|
||||||
|
{
|
||||||
|
// printf("GHz = %f\n", ghz);
|
||||||
|
seconds_per_tick_val = 1e-9f / ghz;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mhz_str)
|
||||||
|
{
|
||||||
|
*mhz_str = '\0';
|
||||||
|
if (1 == sscanf(after_at, "%f", &mhz))
|
||||||
|
{
|
||||||
|
// printf("MHz = %f\n", mhz);
|
||||||
|
seconds_per_tick_val = 1e-6f / mhz;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (1 == sscanf(input, "cpu MHz : %f", &mhz))
|
||||||
|
{
|
||||||
|
// printf("MHz = %f\n", mhz);
|
||||||
|
seconds_per_tick_val = 1e-6f / mhz;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
return seconds_per_tick_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////
|
||||||
|
// Return the conversion from ticks to milliseconds.
|
||||||
|
static double ms_per_tick()
|
||||||
|
{
|
||||||
|
return seconds_per_tick() * 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CycleTimer() = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SYRAH_CYCLE_TIMER_H
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
void write_ppm_image(int *data, int width, int height, const char *filename, int max_iterations)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(filename, "wb");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
perror("fopen() failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write ppm header
|
||||||
|
fprintf(fp, "P6\n");
|
||||||
|
fprintf(fp, "%d %d\n", width, height);
|
||||||
|
fprintf(fp, "255\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < width * height; ++i)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Clamp iteration count for this pixel, then scale the value
|
||||||
|
// to 0-1 range. Raise resulting value to a power (<1) to
|
||||||
|
// increase brightness of low iteration count
|
||||||
|
// pixels. a.k.a. Make things look cooler.
|
||||||
|
|
||||||
|
float mapped = std::pow(
|
||||||
|
std::min(static_cast<float>(max_iterations), static_cast<float>(data[i])) / 256.f, .5f);
|
||||||
|
|
||||||
|
// convert back into 0-255 range, 8-bit channels
|
||||||
|
unsigned char result = static_cast<unsigned char>(255.f * mapped);
|
||||||
|
for (int j = 0; j < 3; ++j)
|
||||||
|
fputc(result, fp);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
printf("Wrote image file %s\n", filename);
|
||||||
|
}
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "cycle_timer.h"
|
||||||
|
|
||||||
|
extern void mandelbrot_serial(float x0,
|
||||||
|
float y0,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int start_row,
|
||||||
|
int num_rows,
|
||||||
|
int max_iterations,
|
||||||
|
int *output);
|
||||||
|
|
||||||
|
extern void mandelbrot_thread(int num_threads,
|
||||||
|
float x0,
|
||||||
|
float y0,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int max_iterations,
|
||||||
|
int *output);
|
||||||
|
|
||||||
|
extern void
|
||||||
|
write_ppm_image(int *data, int width, int height, const char *filename, int max_iterations);
|
||||||
|
|
||||||
|
void scale_and_shift(
|
||||||
|
float &x0, float &x1, float &y0, float &y1, float scale, float shift_x, float shift_y)
|
||||||
|
{
|
||||||
|
|
||||||
|
x0 *= scale;
|
||||||
|
x1 *= scale;
|
||||||
|
y0 *= scale;
|
||||||
|
y1 *= scale;
|
||||||
|
x0 += shift_x;
|
||||||
|
x1 += shift_x;
|
||||||
|
y0 += shift_y;
|
||||||
|
y1 += shift_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(const char *progname)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [options]\n", progname);
|
||||||
|
printf("Program Options:\n");
|
||||||
|
printf(" -t --threads <N> Use N threads (Default = 2)\n");
|
||||||
|
printf(" -v --view <INT> Use specified view settings (Default = 1)\n");
|
||||||
|
printf(" -? --help This message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verify_result(int *gold, int *result, int width, int height)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
for (j = 0; j < width; j++)
|
||||||
|
{
|
||||||
|
if (gold[(i * width) + j] != result[(i * width) + j])
|
||||||
|
{
|
||||||
|
printf("Mismatch : [%d][%d], Expected : %d, Actual : %d\n", i, j,
|
||||||
|
gold[(i * width) + j], result[(i * width) + j]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
|
||||||
|
const unsigned int width = 1600;
|
||||||
|
const unsigned int height = 1200;
|
||||||
|
const int max_iterations = 256;
|
||||||
|
int num_threads = 2;
|
||||||
|
|
||||||
|
float x0 = -2;
|
||||||
|
float x1 = 1;
|
||||||
|
float y0 = -1;
|
||||||
|
float y1 = 1;
|
||||||
|
|
||||||
|
// parse commandline options ////////////////////////////////////////////
|
||||||
|
int opt;
|
||||||
|
// NOLINTNEXTLINE(modernize-avoid-c-arrays): Required by C function.
|
||||||
|
static struct option long_options[] = {{"threads", 1, nullptr, 't'},
|
||||||
|
{"view", 1, nullptr, 'v'},
|
||||||
|
{"help", 0, nullptr, '?'},
|
||||||
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, "t:v:?", long_options, nullptr)) != EOF)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 't':
|
||||||
|
{
|
||||||
|
num_threads = atoi(optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'v':
|
||||||
|
{
|
||||||
|
int view_index = atoi(optarg);
|
||||||
|
// change view settings
|
||||||
|
if (view_index == 2)
|
||||||
|
{
|
||||||
|
float scale_value = .015f;
|
||||||
|
float shift_x = -.986f;
|
||||||
|
float shift_y = .30f;
|
||||||
|
scale_and_shift(x0, x1, y0, y1, scale_value, shift_x, shift_y);
|
||||||
|
}
|
||||||
|
else if (view_index > 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Invalid view index\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end parsing of commandline options
|
||||||
|
|
||||||
|
int *output_serial = new int[width * height];
|
||||||
|
int *output_thread = new int[width * height];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Run the serial implementation. Run the code three times and
|
||||||
|
// take the minimum to get a good estimate.
|
||||||
|
//
|
||||||
|
|
||||||
|
double min_serial = 1e30;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
memset(output_serial, 0, width * height * sizeof(int));
|
||||||
|
double start_time = CycleTimer::current_seconds();
|
||||||
|
mandelbrot_serial(x0, y0, x1, y1, width, height, 0, height, max_iterations, output_serial);
|
||||||
|
double end_time = CycleTimer::current_seconds();
|
||||||
|
min_serial = std::min(min_serial, end_time - start_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[mandelbrot serial]:\t\t[%.3f] ms\n", min_serial * 1000);
|
||||||
|
write_ppm_image(output_serial, width, height, "mandelbrot-serial.ppm", max_iterations);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Run the threaded version
|
||||||
|
//
|
||||||
|
|
||||||
|
double min_thread = 1e30;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
memset(output_thread, 0, width * height * sizeof(int));
|
||||||
|
double start_time = CycleTimer::current_seconds();
|
||||||
|
mandelbrot_thread(num_threads, x0, y0, x1, y1, width, height, max_iterations,
|
||||||
|
output_thread);
|
||||||
|
double end_time = CycleTimer::current_seconds();
|
||||||
|
min_thread = std::min(min_thread, end_time - start_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[mandelbrot thread]:\t\t[%.3f] ms\n", min_thread * 1000);
|
||||||
|
write_ppm_image(output_thread, width, height, "mandelbrot-thread.ppm", max_iterations);
|
||||||
|
|
||||||
|
if (!verify_result(output_serial, output_thread, width, height))
|
||||||
|
{
|
||||||
|
printf("Error : Output from threads does not match serial output\n");
|
||||||
|
|
||||||
|
delete[] output_serial;
|
||||||
|
delete[] output_thread;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute speedup
|
||||||
|
printf("\t\t\t\t(%.2fx speedup from %d threads)\n", min_serial / min_thread, num_threads);
|
||||||
|
|
||||||
|
delete[] output_serial;
|
||||||
|
delete[] output_thread;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Note: This code was modified from example code
|
||||||
|
originally provided by Intel. To comply with Intel's open source
|
||||||
|
licensing agreement, their copyright is retained below.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2010-2011, Intel Corporation
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Intel Corporation nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||||
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
int mandel(float c_re, float c_im, int count)
|
||||||
|
{
|
||||||
|
float z_re = c_re, z_im = c_im;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (z_re * z_re + z_im * z_im > 4.f)
|
||||||
|
break;
|
||||||
|
|
||||||
|
float new_re = (z_re * z_re) - (z_im * z_im);
|
||||||
|
float new_im = 2.f * z_re * z_im;
|
||||||
|
z_re = c_re + new_re;
|
||||||
|
z_im = c_im + new_im;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
//
|
||||||
|
// MandelbrotSerial --
|
||||||
|
//
|
||||||
|
// Compute an image visualizing the mandelbrot set. The resulting
|
||||||
|
// array contains the number of iterations required before the complex
|
||||||
|
// number corresponding to a pixel could be rejected from the set.
|
||||||
|
//
|
||||||
|
// * x0, y0, x1, y1 describe the complex coordinates mapping
|
||||||
|
// into the image viewport.
|
||||||
|
// * width, height describe the size of the output image
|
||||||
|
// * startRow, totalRows describe how much of the image to compute
|
||||||
|
void mandelbrot_serial(float x0,
|
||||||
|
float y0,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int start_row,
|
||||||
|
int total_rows,
|
||||||
|
int max_iterations,
|
||||||
|
int *output)
|
||||||
|
{
|
||||||
|
float dx = (x1 - x0) / (float)width;
|
||||||
|
float dy = (y1 - y0) / (float)height;
|
||||||
|
|
||||||
|
int end_row = start_row + total_rows;
|
||||||
|
|
||||||
|
for (int j = start_row; j < end_row; j++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < width; ++i)
|
||||||
|
{
|
||||||
|
float x = x0 + ((float)i * dx);
|
||||||
|
float y = y0 + ((float)j * dy);
|
||||||
|
|
||||||
|
int index = ((j * width) + i);
|
||||||
|
output[index] = mandel(x, y, max_iterations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
#include <array>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
struct WorkerArgs
|
||||||
|
{
|
||||||
|
float x0, x1;
|
||||||
|
float y0, y1;
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
int maxIterations;
|
||||||
|
int *output;
|
||||||
|
int threadId;
|
||||||
|
int numThreads;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void mandelbrot_serial(float x0,
|
||||||
|
float y0,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int start_row,
|
||||||
|
int num_rows,
|
||||||
|
int max_iterations,
|
||||||
|
int *output);
|
||||||
|
|
||||||
|
//
|
||||||
|
// worker_thread_start --
|
||||||
|
//
|
||||||
|
// Thread entrypoint.
|
||||||
|
void worker_thread_start(WorkerArgs *const args)
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO FOR PP STUDENTS: Implement the body of the worker
|
||||||
|
// thread here. Each thread could make a call to mandelbrot_serial()
|
||||||
|
// to compute a part of the output image. For example, in a
|
||||||
|
// program that uses two threads, thread 0 could compute the top
|
||||||
|
// half of the image and thread 1 could compute the bottom half.
|
||||||
|
// Of course, you can copy mandelbrot_serial() to this file and
|
||||||
|
// modify it to pursue a better performance.
|
||||||
|
|
||||||
|
printf("Hello world from thread %d\n", args->threadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// mandelbrot_thread --
|
||||||
|
//
|
||||||
|
// Multi-threaded implementation of mandelbrot set image generation.
|
||||||
|
// Threads of execution are created by spawning std::threads.
|
||||||
|
void mandelbrot_thread(int num_threads,
|
||||||
|
float x0,
|
||||||
|
float y0,
|
||||||
|
float x1,
|
||||||
|
float y1,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int max_iterations,
|
||||||
|
int *output)
|
||||||
|
{
|
||||||
|
static constexpr int max_threads = 32;
|
||||||
|
|
||||||
|
if (num_threads > max_threads)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: Max allowed threads is %d\n", max_threads);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates thread objects that do not yet represent a thread.
|
||||||
|
std::array<std::thread, max_threads> workers;
|
||||||
|
std::array<WorkerArgs, max_threads> args = {};
|
||||||
|
|
||||||
|
for (int i = 0; i < num_threads; i++)
|
||||||
|
{
|
||||||
|
// TODO FOR PP STUDENTS: You may or may not wish to modify
|
||||||
|
// the per-thread arguments here. The code below copies the
|
||||||
|
// same arguments for each thread
|
||||||
|
args[i].x0 = x0;
|
||||||
|
args[i].y0 = y0;
|
||||||
|
args[i].x1 = x1;
|
||||||
|
args[i].y1 = y1;
|
||||||
|
args[i].width = width;
|
||||||
|
args[i].height = height;
|
||||||
|
args[i].maxIterations = max_iterations;
|
||||||
|
args[i].numThreads = num_threads;
|
||||||
|
args[i].output = output;
|
||||||
|
|
||||||
|
args[i].threadId = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn the worker threads. Note that only numThreads-1 std::threads
|
||||||
|
// are created and the main application thread is used as a worker
|
||||||
|
// as well.
|
||||||
|
for (int i = 1; i < num_threads; i++)
|
||||||
|
{
|
||||||
|
workers[i] = std::thread(worker_thread_start, &args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
worker_thread_start(&args[0]);
|
||||||
|
|
||||||
|
// join worker threads
|
||||||
|
for (int i = 1; i < num_threads; i++)
|
||||||
|
{
|
||||||
|
workers[i].join();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user