Skip to content

Commit

Permalink
tools: Add codec benchmark
Browse files Browse the repository at this point in the history
This code can be used to benchmark native vs .js implementations of
vertex codec (and, in the future, vertex filters).

Note: right now the SIMD variant of the JS benchmark doesn't run because
compiler generates some instructions that Chrome doesn't support...
  • Loading branch information
zeux committed Feb 12, 2020
1 parent c4314d1 commit e452b03
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ $(EXECUTABLE): $(DEMO_OBJECTS) $(LIBRARY)
vcachetuner: tools/vcachetuner.cpp $(BUILD)/tools/meshloader.cpp.o $(BUILD)/demo/miniz.cpp.o $(LIBRARY)
$(CXX) $^ -fopenmp $(CXXFLAGS) -std=c++11 $(LDFLAGS) -o $@

codecbench: tools/codecbench.cpp $(LIBRARY)
$(CXX) $^ $(CXXFLAGS) $(LDFLAGS) -o $@

codecbench.js: tools/codecbench.cpp ${LIBRARY_SOURCES}
emcc $^ -O3 -DNDEBUG -s ALLOW_MEMORY_GROWTH=1 -o $@

codecbench-simd.js: tools/codecbench.cpp ${LIBRARY_SOURCES}
emcc $^ -O3 -DNDEBUG -s ALLOW_MEMORY_GROWTH=1 -munimplemented-simd128 -o $@

$(LIBRARY): $(LIBRARY_OBJECTS)
ar rcs $@ $^

Expand Down
137 changes: 137 additions & 0 deletions tools/codecbench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include "../src/meshoptimizer.h"

#include <vector>

#include <time.h>
#include <stdint.h>
#include <stdio.h>

#ifdef __EMSCRIPTEN__
double timestamp()
{
return double(clock()) / double(CLOCKS_PER_SEC);
}
#else
double timestamp()
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return double(ts.tv_sec) + 1e-9 * double(ts.tv_nsec);
}
#endif

struct Vertex
{
uint16_t data[16];
};

uint32_t murmur3(uint32_t h)
{
h ^= h >> 16;
h *= 0x85ebca6bu;
h ^= h >> 13;
h *= 0xc2b2ae35u;
h ^= h >> 16;

return h;
}

void bench(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices)
{
std::vector<Vertex> vb(vertices.size());
std::vector<unsigned int> ib(indices.size());

std::vector<unsigned char> vc(meshopt_encodeVertexBufferBound(vertices.size(), sizeof(Vertex)));
std::vector<unsigned char> ic(meshopt_encodeIndexBufferBound(indices.size(), vertices.size()));

printf("source: vertex data %d bytes, index data %d bytes\n", int(vertices.size() * sizeof(Vertex)), int(indices.size() * 4));

for (int pass = 0; pass < 2; ++pass)
{
if (pass == 1)
meshopt_optimizeVertexCacheStrip(&ib[0], &indices[0], indices.size(), vertices.size());
else
meshopt_optimizeVertexCache(&ib[0], &indices[0], indices.size(), vertices.size());

meshopt_optimizeVertexFetch(&vb[0], &ib[0], indices.size(), &vertices[0], vertices.size(), sizeof(Vertex));

vc.resize(vc.capacity());
vc.resize(meshopt_encodeVertexBuffer(&vc[0], vc.size(), &vb[0], vertices.size(), sizeof(Vertex)));

ic.resize(ic.capacity());
ic.resize(meshopt_encodeIndexBuffer(&ic[0], ic.size(), &ib[0], indices.size()));

printf("pass %d: vertex data %d bytes, index data %d bytes\n", pass, int(vc.size()), int(ic.size()));

for (int attempt = 0; attempt < 10; ++attempt)
{
double t0 = timestamp();

int rv = meshopt_decodeVertexBuffer(&vb[0], vertices.size(), sizeof(Vertex), &vc[0], vc.size());
assert(rv == 0);
(void)rv;

double t1 = timestamp();

int ri = meshopt_decodeIndexBuffer(&ib[0], indices.size(), 4, &ic[0], ic.size());
assert(ri == 0);
(void)ri;

double t2 = timestamp();

double GB = 1024 * 1024 * 1024;

printf("decode: vertex %.2f ms (%.2f GB/sec), index %.2f ms (%.2f GB/sec)\n",
(t1 - t0) * 1000, double(vertices.size() * sizeof(Vertex)) / GB / (t1 - t0),
(t2 - t1) * 1000, double(indices.size() * 4) / GB / (t2 - t1));
}
}
}

int main()
{
meshopt_encodeIndexVersion(1);

const int N = 2000;

std::vector<Vertex> vertices;
vertices.reserve((N + 1) * (N + 1));

for (int x = 0; x <= N; ++x)
{
for (int y = 0; y <= N; ++y)
{
Vertex v;

for (int k = 0; k < 16; ++k)
{
uint32_t h = murmur3((x * (N + 1) + y) * 16 + k);

// use random k-bit sequence for each word to test all encoding types
// note: this doesn't stress the sentinel logic too much but it's all branchless so it's probably fine?
v.data[k] = h & ((1 << k) - 1);
}

vertices.push_back(v);
}
}

std::vector<unsigned int> indices;
indices.reserve(N * N * 6);

for (int x = 0; x < N; ++x)
{
for (int y = 0; y < N; ++y)
{
indices.push_back((x + 0) * N + (y + 0));
indices.push_back((x + 1) * N + (y + 0));
indices.push_back((x + 0) * N + (y + 1));

indices.push_back((x + 0) * N + (y + 1));
indices.push_back((x + 1) * N + (y + 0));
indices.push_back((x + 1) * N + (y + 1));
}
}

bench(vertices, indices);
}

0 comments on commit e452b03

Please sign in to comment.