Skip to content

Added perlin noise generator micro benchmark #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions perlin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "rust perlin"
version = "0.0.1"
[[bin]]
name = "bin_test_rs"
path = "test.rs"
[dependencies]
rand = "*"
9 changes: 9 additions & 0 deletions perlin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Perlin noise benchmark.

Runs perlin noise 256x256 image generation 1000 times.

Original benchmark https://github.com/nsf/pnoise.

To compile all: `sh build.sh`

To run all: `sh run.sh`
14 changes: 14 additions & 0 deletions perlin/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
gcc -std=c99 -march=native -msse3 -mfpmath=sse -O3 -o bin_test_c_gcc test.c -lm
clang -std=c99 -march=native -msse3 -mfpmath=sse -O3 -o bin_test_c_clang test.c -lm
dmd -ofbin_test_d_dmd -O -boundscheck=off -inline -release test.d
ldc2 -O5 -ofbin_test_d_ldc test.d -release -mcpu=native -inline -boundscheck=off
gdc -Ofast -o bin_test_d_gdc test.d -frelease -finline -march=native -fno-bounds-check
gccgo -O3 -g -o bin_test_go_gccgo test.go
go build -o bin_test_go_gc test.go
cargo build --release
mcs -out:bin_test_cs test.cs
nim c -d:release --cc:gcc -o:bin_test_nim_gcc test.nim
nim c -d:release --cc:clang -o:bin_test_nim_clang test.nim
crystal build -o bin_test_cr --release test.cr
javac test.java
10 changes: 10 additions & 0 deletions perlin/clean.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh
rm base64_*
rm *.class
rm *.o
rm *.exe
rm *.lock
rm -rf bin_*
rm -rf .crystal
rm -rf nimcache
rm -rf target
32 changes: 32 additions & 0 deletions perlin/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/sh
readonly NUM=1000
echo C
../xtime.rb ./bin_test_c_clang $NUM 2>&1 > /dev/null
echo Cpp
../xtime.rb ./bin_test_c_gcc $NUM 2>&1 > /dev/null
echo Mono
../xtime.rb mono ./bin_test_cs $NUM 2>&1 > /dev/null
echo D
../xtime.rb ./bin_test_d_dmd $NUM 2>&1 > /dev/null
echo D Ldc
../xtime.rb ./bin_test_d_ldc $NUM 2>&1 > /dev/null
echo D Gdc
../xtime.rb ./bin_test_d_gdc $NUM 2>&1 > /dev/null
echo Go
../xtime.rb ./bin_test_go_gc $NUM 2>&1 > /dev/null
echo Go gcc
../xtime.rb ./bin_test_go_gccgo $NUM 2>&1 > /dev/null
echo Rust
../xtime.rb target/release/bin_test_rs $NUM 2>&1 >/dev/null
echo Nim gcc
../xtime.rb ./bin_test_nim_gcc $NUM 2>&1 > /dev/null
echo Nim clang
../xtime.rb ./bin_test_nim_clang $NUM 2>&1 > /dev/null
echo Crystal
../xtime.rb ./bin_test_cr $NUM 2>&1 > /dev/null
echo Java
../xtime.rb java -cp . test $NUM 2>&1 > /dev/null
echo Python Pypy
../xtime.rb pypy test.py $NUM 2>&1 > /dev/null
echo Python
../xtime.rb python test.py $NUM 2>&1 > /dev/null
124 changes: 124 additions & 0 deletions perlin/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define M_PI 3.1415926535f

typedef struct {
float x, y;
} Vec2;

static inline float lerp(float a, float b, float v)
{
return a * (1 - v) + b * v;
}

static inline float smooth(float v) {
return v * v * (3 - 2 * v);
}

static inline Vec2 random_gradient()
{
const float v = (float)rand() / RAND_MAX * M_PI * 2.0f;
return (Vec2){cosf(v), sinf(v)};
}

static inline float gradient(Vec2 orig, Vec2 grad, Vec2 p)
{
Vec2 sp = {p.x - orig.x, p.y - orig.y};
return grad.x * sp.x + grad.y * sp.y;
}

typedef struct {
Vec2 rgradients[256];
int permutations[256];
Vec2 gradients[4];
Vec2 origins[4];
} Noise2DContext;

static inline Vec2 get_gradient(Noise2DContext *ctx, int x, int y) {
int idx = ctx->permutations[x & 255] + ctx->permutations[y & 255];
return ctx->rgradients[idx & 255];
}

static inline void get_gradients(Noise2DContext *ctx, float x, float y) {
float x0f = floorf(x);
float y0f = floorf(y);
int x0 = x0f;
int y0 = y0f;
int x1 = x0 + 1;
int y1 = y0 + 1;

ctx->gradients[0] = get_gradient(ctx, x0, y0);
ctx->gradients[1] = get_gradient(ctx, x1, y0);
ctx->gradients[2] = get_gradient(ctx, x0, y1);
ctx->gradients[3] = get_gradient(ctx, x1, y1);

ctx->origins[0] = (Vec2){x0f + 0.0f, y0f + 0.0f};
ctx->origins[1] = (Vec2){x0f + 1.0f, y0f + 0.0f};
ctx->origins[2] = (Vec2){x0f + 0.0f, y0f + 1.0f};
ctx->origins[3] = (Vec2){x0f + 1.0f, y0f + 1.0f};
}


static float noise2d_get(Noise2DContext *ctx, float x, float y)
{
Vec2 p = {x, y};
get_gradients(ctx, x, y);
float v0 = gradient(ctx->origins[0], ctx->gradients[0], p);
float v1 = gradient(ctx->origins[1], ctx->gradients[1], p);
float v2 = gradient(ctx->origins[2], ctx->gradients[2], p);
float v3 = gradient(ctx->origins[3], ctx->gradients[3], p);

float fx = smooth(x - ctx->origins[0].x);
float vx0 = lerp(v0, v1, fx);
float vx1 = lerp(v2, v3, fx);
float fy = smooth(y - ctx->origins[0].y);
return lerp(vx0, vx1, fy);
}

static void init_noise2d(Noise2DContext *ctx)
{
for (int i = 0; i < 256; i++)
ctx->rgradients[i] = random_gradient();

for (int i = 0; i < 256; i++) {
int j = rand() % (i+1);
ctx->permutations[i] = ctx->permutations[j];
ctx->permutations[j] = i;
}
}

int main(int argc, char **argv)
{
srand(time(NULL));

const char *symbols[] = {" ", "░", "▒", "▓", "█", "█"};
float *pixels = malloc(sizeof(float) * 256 * 256);
int n = 1;
if (argc > 1) n = atoi(argv[1]);

Noise2DContext n2d;
init_noise2d(&n2d);

for (int i = 0; i < n; i++) {
for (int y = 0; y < 256; y++) {
for (int x = 0; x < 256; x++) {
float v = noise2d_get(&n2d, x * 0.1f, y * 0.1f)
* 0.5f + 0.5f;
pixels[y*256+x] = v;
}
}
}

for (int y = 0; y < 256; y++) {
for (int x = 0; x < 256; x++) {
int idx = pixels[y*256+x] / 0.2f;
printf("%s", symbols[idx]);
}
printf("\n");
}

return 0;
}
95 changes: 95 additions & 0 deletions perlin/test.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
record Vec2, x : Float64, y : Float64

def lerp(a, b, v)
a * (1.0 - v) + b * v
end

def smooth(v)
v * v * (3.0 - 2.0 * v)
end

def random_gradient
v = rand * Math::PI * 2.0
Vec2.new Math.cos(v), Math.sin(v)
end

def gradient(orig, grad, p)
sp = Vec2.new(p.x - orig.x, p.y - orig.y)
grad.x * sp.x + grad.y * sp.y
end

struct Noise2DContext
@permutations : StaticArray(Int32, 256)

def initialize
@rgradients = StaticArray(Vec2, 256).new { random_gradient }
@permutations = typeof(@permutations).new { |i| i }.shuffle!
end

def get_gradient(x, y)
idx = @permutations[x & 255] + @permutations[y & 255]
@rgradients[idx & 255]
end

def get_gradients(x, y)
x0f = x.floor
y0f = y.floor
x0 = x0f.to_i
y0 = y0f.to_i
x1 = x0 + 1
y1 = y0 + 1

{
{
get_gradient(x0, y0),
get_gradient(x1, y0),
get_gradient(x0, y1),
get_gradient(x1, y1),
},
{
Vec2.new(x0f + 0.0, y0f + 0.0),
Vec2.new(x0f + 1.0, y0f + 0.0),
Vec2.new(x0f + 0.0, y0f + 1.0),
Vec2.new(x0f + 1.0, y0f + 1.0),
},
}
end

def get(x, y)
p = Vec2.new(x, y)
gradients, origins = get_gradients(x, y)
v0 = gradient(origins[0], gradients[0], p)
v1 = gradient(origins[1], gradients[1], p)
v2 = gradient(origins[2], gradients[2], p)
v3 = gradient(origins[3], gradients[3], p)
fx = smooth(x - origins[0].x)
vx0 = lerp(v0, v1, fx)
vx1 = lerp(v2, v3, fx)
fy = smooth(y - origins[0].y)
lerp(vx0, vx1, fy)
end
end

symbols = [' ', '░', '▒', '▓', '█', '█']
pixels = Array.new(256) { Array.new(256, 0.0) }

n2d = Noise2DContext.new

n = (ARGV[0]? || 1).to_i

n.times do |i|
256.times do |y|
256.times do |x|
v = n2d.get(x * 0.1, (y + (i * 128)) * 0.1) * 0.5 + 0.5
pixels[y][x] = v
end
end
end

256.times do |y|
256.times do |x|
v = pixels[y][x]
print(symbols[(v / 0.2).to_i])
end
puts
end
Loading