<commit_before>#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <direct.h>
#else
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#endif
#include <cstring>
#include <string>
#include <set>
#include <algorithm>
#include <stdio.h>
#include <sys/stat.h>
#include <ctype.h>

#include "base/logging.h"
#include "base/basictypes.h"
#include "file/file_util.h"

#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__SYMBIAN32__)
#define stat64 stat
#endif

#if !defined(readdir_r)
static inline int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
    struct dirent *readdir_entry;

    readdir_entry = readdir(dirp);
    if (readdir_entry == NULL) {
        *result = NULL;
        return errno;
    }

    *entry = *readdir_entry;
    *result = entry;
    return 0;
}
#endif

bool writeStringToFile(bool text_file, const std::string &str, const char *filename)
{
	FILE *f = fopen(filename, text_file ? "w" : "wb");
	if (!f)
		return false;
	size_t len = str.size();
	if (len != fwrite(str.data(), 1, str.size(), f))
	{
		fclose(f);
		return false;
	}
	fclose(f);
	return true;
}

bool writeDataToFile(bool text_file, const void* data, const unsigned int size, const char *filename)
{
	FILE *f = fopen(filename, text_file ? "w" : "wb");
	if (!f)
		return false;
	size_t len = size;
	if (len != fwrite(data, 1, len, f))
	{
		fclose(f);
		return false;
	}
	fclose(f);
	return true;
}

uint64_t GetSize(FILE *f)
{
	// can't use off_t here because it can be 32-bit
	uint64_t pos = ftell(f);
	if (fseek(f, 0, SEEK_END) != 0) {
		return 0;
	}
	uint64_t size = ftell(f);
	// Reset the seek position to where it was when we started.
	if ((size != pos) && (fseek(f, pos, SEEK_SET) != 0)) {
		// Should error here
		return 0;
	}
	return size;
}

bool ReadFileToString(bool text_file, const char *filename, std::string &str)
{
	FILE *f = fopen(filename, text_file ? "r" : "rb");
	if (!f)
		return false;
	size_t len = (size_t)GetSize(f);
	char *buf = new char[len + 1];
	buf[fread(buf, 1, len, f)] = 0;
	str = std::string(buf, len);
	fclose(f);
	delete [] buf;
	return true;
}

bool readDataFromFile(bool text_file, unsigned char* &data, const unsigned int size, const char *filename)
{
	FILE *f = fopen(filename, text_file ? "r" : "rb");
	if (!f)
		return false;
	size_t len = (size_t)GetSize(f);
	if(len < size)
	{
		fclose(f);
		return false;
	}
	data[fread(data, 1, size, f)] = 0;
	fclose(f);
	return true;
}

#define DIR_SEP "/"
#define DIR_SEP_CHR '\\'

#ifndef METRO

// Remove any ending forward slashes from directory paths
// Modifies argument.
static void stripTailDirSlashes(std::string &fname)
{
	if (fname.length() > 1)
	{
		size_t i = fname.length() - 1;
		while (fname[i] == DIR_SEP_CHR)
			fname[i--] = '\0';
	}
	return;
}

// Returns true if file filename exists
bool exists(const std::string &filename)
{
#ifdef _WIN32
	return GetFileAttributes(filename.c_str()) != 0xFFFFFFFF;
#else
	struct stat64 file_info;

	std::string copy(filename);
	stripTailDirSlashes(copy);

	int result = stat64(copy.c_str(), &file_info);

	return (result == 0);
#endif
}

// Returns true if filename is a directory
bool isDirectory(const std::string &filename)
{
	FileInfo info;
	getFileInfo(filename.c_str(), &info);
	return info.isDirectory;
}

bool getFileInfo(const char *path, FileInfo *fileInfo)
{
	// TODO: Expand relative paths?
	fileInfo->fullName = path;

#ifdef _WIN32
	DWORD attributes = GetFileAttributes(path);
	fileInfo->isDirectory = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
	fileInfo->isWritable = (attributes & FILE_ATTRIBUTE_READONLY) == 0;
#else
	struct stat64 file_info;

	std::string copy(path);
	stripTailDirSlashes(copy);

	int result = stat64(copy.c_str(), &file_info);

	if (result < 0) {
		WLOG("IsDirectory: stat failed on %s", path);
		return false;
	}

	fileInfo->isDirectory = S_ISDIR(file_info.st_mode);
	fileInfo->isWritable = false;
	// HACK: approximation
	if (file_info.st_mode & 0200)
		fileInfo->isWritable = true;
#endif
	return true;
}

std::string getFileExtension(const std::string &fn) {
	int pos = fn.rfind(".");
	if (pos < 0) return "";
	std::string ext = fn.substr(pos+1);
	for (size_t i = 0; i < ext.size(); i++) {
		ext[i] = tolower(ext[i]);
	}
	return ext;
}

size_t getFilesInDir(const char *directory, std::vector<FileInfo> *files, const char *filter) {
	size_t foundEntries = 0;
	std::set<std::string> filters;
	std::string tmp;
	if (filter) {
		while (*filter) {
			if (*filter == ':') {
				filters.insert(tmp);
				tmp = "";
			} else {
				tmp.push_back(*filter);
			}
			filter++;
		}
	}
#ifdef _WIN32
	// Find the first file in the directory.
	WIN32_FIND_DATA ffd;
#ifdef UNICODE
	HANDLE hFind = FindFirstFile((std::wstring(directory) + "\\*").c_str(), &ffd);
#else
	HANDLE hFind = FindFirstFile((std::string(directory) + "\\*").c_str(), &ffd);
#endif
	if (hFind == INVALID_HANDLE_VALUE) {
		FindClose(hFind);
		return 0;
	}
	// windows loop
	do
	{
		const std::string virtualName(ffd.cFileName);
#else
	struct dirent_large { struct dirent entry; char padding[FILENAME_MAX+1]; };
	struct dirent_large diren;
	struct dirent *result = NULL;

	DIR *dirp = opendir(directory);
	if (!dirp)
		return 0;
	// non windows loop
	while (!readdir_r(dirp, (dirent*) &diren, &result) && result)
	{
		const std::string virtualName(result->d_name);
#endif
		// check for "." and ".."
		if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
			((virtualName[0] == '.') && (virtualName[1] == '.') &&
			(virtualName[2] == '\0')))
			continue;

		// Remove dotfiles (should be made optional?)
		if (virtualName[0] == '.')
			continue;

		FileInfo info;
		info.name = virtualName;
		info.fullName = std::string(directory) + "/" + virtualName;
		info.isDirectory = isDirectory(info.fullName);
		info.exists = true;
		if (!info.isDirectory) {
			std::string ext = getFileExtension(info.fullName);
			if (filter) {
				if (filters.find(ext) == filters.end())
					continue;
			}
		}

		files->push_back(info);
#ifdef _WIN32
	} while (FindNextFile(hFind, &ffd) != 0);
	FindClose(hFind);
#else
	}
	closedir(dirp);
#endif
	std::sort(files->begin(), files->end());
	return foundEntries;
}

void deleteFile(const char *file)
{
#ifdef _WIN32
	if (!DeleteFile(file)) {
		ELOG("Error deleting %s: %i", file, GetLastError());
	}
#else
	int err = unlink(file);
	if (err) {
		ELOG("Error unlinking %s: %i", file, err);
	}
#endif
}
#endif

std::string getDir(const std::string &path)
{
	if (path == "/")
		return path;
	int n = path.size() - 1;
	while (n >= 0 && path[n] != '\\' && path[n] != '/')
		n--;
	std::string cutpath = path.substr(0, n);
	for (size_t i = 0; i < cutpath.size(); i++)
	{
		if (cutpath[i] == '\\') cutpath[i] = '/';
	}
#ifndef _WIN32
	if (!cutpath.size()) {
		return "/";
	}
#endif
	return cutpath;
}

void mkDir(const std::string &path)
{
#ifdef _WIN32
	mkdir(path.c_str());
#else
	mkdir(path.c_str(), 0777);
#endif
}
<commit_msg>Build fix<commit_after>#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <direct.h>
#else
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#endif
#include <cstring>
#include <string>
#include <set>
#include <algorithm>
#include <stdio.h>
#include <sys/stat.h>
#include <ctype.h>

#include "base/logging.h"
#include "base/basictypes.h"
#include "file/file_util.h"

#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__SYMBIAN32__)
#define stat64 stat
#endif

// Hack
#ifdef __SYMBIAN32__
static inline int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
    struct dirent *readdir_entry;

    readdir_entry = readdir(dirp);
    if (readdir_entry == NULL) {
        *result = NULL;
        return errno;
    }

    *entry = *readdir_entry;
    *result = entry;
    return 0;
}
#endif

bool writeStringToFile(bool text_file, const std::string &str, const char *filename)
{
	FILE *f = fopen(filename, text_file ? "w" : "wb");
	if (!f)
		return false;
	size_t len = str.size();
	if (len != fwrite(str.data(), 1, str.size(), f))
	{
		fclose(f);
		return false;
	}
	fclose(f);
	return true;
}

bool writeDataToFile(bool text_file, const void* data, const unsigned int size, const char *filename)
{
	FILE *f = fopen(filename, text_file ? "w" : "wb");
	if (!f)
		return false;
	size_t len = size;
	if (len != fwrite(data, 1, len, f))
	{
		fclose(f);
		return false;
	}
	fclose(f);
	return true;
}

uint64_t GetSize(FILE *f)
{
	// can't use off_t here because it can be 32-bit
	uint64_t pos = ftell(f);
	if (fseek(f, 0, SEEK_END) != 0) {
		return 0;
	}
	uint64_t size = ftell(f);
	// Reset the seek position to where it was when we started.
	if ((size != pos) && (fseek(f, pos, SEEK_SET) != 0)) {
		// Should error here
		return 0;
	}
	return size;
}

bool ReadFileToString(bool text_file, const char *filename, std::string &str)
{
	FILE *f = fopen(filename, text_file ? "r" : "rb");
	if (!f)
		return false;
	size_t len = (size_t)GetSize(f);
	char *buf = new char[len + 1];
	buf[fread(buf, 1, len, f)] = 0;
	str = std::string(buf, len);
	fclose(f);
	delete [] buf;
	return true;
}

bool readDataFromFile(bool text_file, unsigned char* &data, const unsigned int size, const char *filename)
{
	FILE *f = fopen(filename, text_file ? "r" : "rb");
	if (!f)
		return false;
	size_t len = (size_t)GetSize(f);
	if(len < size)
	{
		fclose(f);
		return false;
	}
	data[fread(data, 1, size, f)] = 0;
	fclose(f);
	return true;
}

#define DIR_SEP "/"
#define DIR_SEP_CHR '\\'

#ifndef METRO

// Remove any ending forward slashes from directory paths
// Modifies argument.
static void stripTailDirSlashes(std::string &fname)
{
	if (fname.length() > 1)
	{
		size_t i = fname.length() - 1;
		while (fname[i] == DIR_SEP_CHR)
			fname[i--] = '\0';
	}
	return;
}

// Returns true if file filename exists
bool exists(const std::string &filename)
{
#ifdef _WIN32
	return GetFileAttributes(filename.c_str()) != 0xFFFFFFFF;
#else
	struct stat64 file_info;

	std::string copy(filename);
	stripTailDirSlashes(copy);

	int result = stat64(copy.c_str(), &file_info);

	return (result == 0);
#endif
}

// Returns true if filename is a directory
bool isDirectory(const std::string &filename)
{
	FileInfo info;
	getFileInfo(filename.c_str(), &info);
	return info.isDirectory;
}

bool getFileInfo(const char *path, FileInfo *fileInfo)
{
	// TODO: Expand relative paths?
	fileInfo->fullName = path;

#ifdef _WIN32
	DWORD attributes = GetFileAttributes(path);
	fileInfo->isDirectory = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
	fileInfo->isWritable = (attributes & FILE_ATTRIBUTE_READONLY) == 0;
#else
	struct stat64 file_info;

	std::string copy(path);
	stripTailDirSlashes(copy);

	int result = stat64(copy.c_str(), &file_info);

	if (result < 0) {
		WLOG("IsDirectory: stat failed on %s", path);
		return false;
	}

	fileInfo->isDirectory = S_ISDIR(file_info.st_mode);
	fileInfo->isWritable = false;
	// HACK: approximation
	if (file_info.st_mode & 0200)
		fileInfo->isWritable = true;
#endif
	return true;
}

std::string getFileExtension(const std::string &fn) {
	int pos = fn.rfind(".");
	if (pos < 0) return "";
	std::string ext = fn.substr(pos+1);
	for (size_t i = 0; i < ext.size(); i++) {
		ext[i] = tolower(ext[i]);
	}
	return ext;
}

size_t getFilesInDir(const char *directory, std::vector<FileInfo> *files, const char *filter) {
	size_t foundEntries = 0;
	std::set<std::string> filters;
	std::string tmp;
	if (filter) {
		while (*filter) {
			if (*filter == ':') {
				filters.insert(tmp);
				tmp = "";
			} else {
				tmp.push_back(*filter);
			}
			filter++;
		}
	}
#ifdef _WIN32
	// Find the first file in the directory.
	WIN32_FIND_DATA ffd;
#ifdef UNICODE
	HANDLE hFind = FindFirstFile((std::wstring(directory) + "\\*").c_str(), &ffd);
#else
	HANDLE hFind = FindFirstFile((std::string(directory) + "\\*").c_str(), &ffd);
#endif
	if (hFind == INVALID_HANDLE_VALUE) {
		FindClose(hFind);
		return 0;
	}
	// windows loop
	do
	{
		const std::string virtualName(ffd.cFileName);
#else
	struct dirent_large { struct dirent entry; char padding[FILENAME_MAX+1]; };
	struct dirent_large diren;
	struct dirent *result = NULL;

	DIR *dirp = opendir(directory);
	if (!dirp)
		return 0;
	// non windows loop
	while (!readdir_r(dirp, (dirent*) &diren, &result) && result)
	{
		const std::string virtualName(result->d_name);
#endif
		// check for "." and ".."
		if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
			((virtualName[0] == '.') && (virtualName[1] == '.') &&
			(virtualName[2] == '\0')))
			continue;

		// Remove dotfiles (should be made optional?)
		if (virtualName[0] == '.')
			continue;

		FileInfo info;
		info.name = virtualName;
		info.fullName = std::string(directory) + "/" + virtualName;
		info.isDirectory = isDirectory(info.fullName);
		info.exists = true;
		if (!info.isDirectory) {
			std::string ext = getFileExtension(info.fullName);
			if (filter) {
				if (filters.find(ext) == filters.end())
					continue;
			}
		}

		files->push_back(info);
#ifdef _WIN32
	} while (FindNextFile(hFind, &ffd) != 0);
	FindClose(hFind);
#else
	}
	closedir(dirp);
#endif
	std::sort(files->begin(), files->end());
	return foundEntries;
}

void deleteFile(const char *file)
{
#ifdef _WIN32
	if (!DeleteFile(file)) {
		ELOG("Error deleting %s: %i", file, GetLastError());
	}
#else
	int err = unlink(file);
	if (err) {
		ELOG("Error unlinking %s: %i", file, err);
	}
#endif
}
#endif

std::string getDir(const std::string &path)
{
	if (path == "/")
		return path;
	int n = path.size() - 1;
	while (n >= 0 && path[n] != '\\' && path[n] != '/')
		n--;
	std::string cutpath = path.substr(0, n);
	for (size_t i = 0; i < cutpath.size(); i++)
	{
		if (cutpath[i] == '\\') cutpath[i] = '/';
	}
#ifndef _WIN32
	if (!cutpath.size()) {
		return "/";
	}
#endif
	return cutpath;
}

void mkDir(const std::string &path)
{
#ifdef _WIN32
	mkdir(path.c_str());
#else
	mkdir(path.c_str(), 0777);
#endif
}
<|endoftext|>


<commit_before>#include <emmintrin.h>
#include <cmath>
#include <cstdint>
#include <cstdio>

#include "benchmark.h"
#include "HalideBuffer.h"

using namespace Halide;

//#define cimg_display 0
//#include "CImg.h"
//using namespace cimg_library;

// typedef CImg<uint16_t> Image;

double t;

Buffer<uint16_t> blur(Buffer<uint16_t> in) {
    Buffer<uint16_t> tmp(in.width()-8, in.height());
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    t = benchmark(10, 1, [&]() {
        for (int y = 0; y < tmp.height(); y++)
            for (int x = 0; x < tmp.width(); x++)
                tmp(x, y) = (in(x, y) + in(x+1, y) + in(x+2, y))/3;

        for (int y = 0; y < out.height(); y++)
            for (int x = 0; x < out.width(); x++)
                out(x, y) = (tmp(x, y) + tmp(x, y+1) + tmp(x, y+2))/3;
    });

    return out;
}

Buffer<uint16_t> blur_fast(Buffer<uint16_t> in) {
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    t = benchmark(10, 1, [&]() {
        __m128i one_third = _mm_set1_epi16(21846);
#pragma omp parallel for
        for (int yTile = 0; yTile < out.height(); yTile += 32) {
            __m128i a, b, c, sum, avg;
            __m128i tmp[(128/8) * (32 + 2)];
            for (int xTile = 0; xTile < out.width(); xTile += 128) {
                __m128i *tmpPtr = tmp;
                for (int y = 0; y < 32+2; y++) {
                    const uint16_t *inPtr = &(in(xTile, yTile+y));
                    for (int x = 0; x < 128; x += 8) {
                        a = _mm_load_si128((const __m128i*)(inPtr));
                        b = _mm_loadu_si128((const __m128i*)(inPtr+1));
                        c = _mm_loadu_si128((const __m128i*)(inPtr+2));
                        sum = _mm_add_epi16(_mm_add_epi16(a, b), c);
                        avg = _mm_mulhi_epi16(sum, one_third);
                        _mm_store_si128(tmpPtr++, avg);
                        inPtr+=8;
                    }
                }
                tmpPtr = tmp;
                for (int y = 0; y < 32; y++) {
                    __m128i *outPtr = (__m128i *)(&(out(xTile, yTile+y)));
                    for (int x = 0; x < 128; x += 8) {
                        a = _mm_load_si128(tmpPtr+(2*128)/8);
                        b = _mm_load_si128(tmpPtr+128/8);
                        c = _mm_load_si128(tmpPtr++);
                        sum = _mm_add_epi16(_mm_add_epi16(a, b), c);
                        avg = _mm_mulhi_epi16(sum, one_third);
                        _mm_store_si128(outPtr++, avg);
                    }
                }
            }
        }
    });

    return out;
}

/*
  Image blur_fast(const Image &in) {
  Image out(in.width(), in.height());

  __m128i one_third = _mm_set1_epi16(21846);
  #pragma omp parallel for
  for (int yTile = 0; yTile < in.height(); yTile += 64) {
  __m128i tmp[(64/8)*(64+2)];
  for (int xTile = 0; xTile < in.width(); xTile += 64) {
  __m128i *tmpPtr = tmp;
  for (int y = -1; y < 64+1; y++) {
  const uint16_t *inPtr = &(in(xTile, yTile+y));
  for (int x = 0; x < 64; x += 8) {
  __m128i val = _mm_loadu_si128((__m128i *)(inPtr-1));
  val = _mm_add_epi16(val, _mm_load_si128((__m128i *)inPtr));
  val = _mm_add_epi16(val, _mm_loadu_si128((__m128i *)(inPtr+1)));
  val = _mm_mulhi_epi16(val, one_third);
  _mm_store_si128(tmpPtr++, val);
  inPtr += 8;
  }
  }
  tmpPtr = tmp;
  for (int y = 0; y < 64; y++) {
  __m128i *outPtr = (__m128i *)(&(out(xTile, yTile+y)));
  for (int x = 0; x < 64; x += 8) {
  __m128i val = _mm_load_si128(tmpPtr);
  val = _mm_add_epi16(val, _mm_load_si128(tmpPtr+64/8));
  val = _mm_add_epi16(val, _mm_load_si128(tmpPtr+(2*64)/8));
  val = _mm_mulhi_epi16(val, one_third);
  _mm_store_si128(outPtr++, val);
  tmpPtr++;
  }
  }
  }
  }

  return out;
  }
*/

Buffer<uint16_t> blur_fast2(const Buffer<uint16_t> &in) {
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    int vw = in.width()/8;
    if (vw > 1024) {
        printf("Image too large for constant-sized stack allocation\n");
        return out;
    }

    t = benchmark(10, 1, [&]() {
        // multiplying by 21846 then taking the top 16 bits is equivalent to
        // dividing by three
        __m128i one_third = _mm_set1_epi16(21846);

#pragma omp parallel for
        for (int yTile = 0; yTile < in.height(); yTile += 128) {
            __m128i tmp[1024*4]; // four scanlines
            for (int y = -2; y < 128; y++) {
                // to produce this scanline of the output
                __m128i *outPtr = (__m128i *)(&(out(0, yTile + y)));
                // we use this scanline of the input
                const uint16_t *inPtr = &(in(0, yTile + y + 2));
                // and these scanlines of the intermediate result
                // We start y at negative 2 to fill the tmp buffer
                __m128i *tmpPtr0 = tmp + ((y+4) & 3) * vw;
                __m128i *tmpPtr1 = tmp + ((y+3) & 3) * vw;
                __m128i *tmpPtr2 = tmp + ((y+2) & 3) * vw;
                for (int x = 0; x < vw; x++) {
                    // blur horizontally to produce next scanline of tmp
                    __m128i val = _mm_load_si128((const __m128i *)(inPtr));
                    val = _mm_add_epi16(val, _mm_loadu_si128((const __m128i *)(inPtr+1)));
                    val = _mm_add_epi16(val, _mm_loadu_si128((const __m128i *)(inPtr+2)));
                    val = _mm_mulhi_epi16(val, one_third);
                    _mm_store_si128(tmpPtr0++, val);

                    // blur vertically using previous scanlines of tmp to produce output
                    if (y >= 0) {
                        val = _mm_add_epi16(val, _mm_load_si128(tmpPtr1++));
                        val = _mm_add_epi16(val, _mm_load_si128(tmpPtr2++));
                        val = _mm_mulhi_epi16(val, one_third);
                        _mm_store_si128(outPtr++, val);
                    }

                    inPtr += 8;
                }
            }
        }

    });

    return out;
}

extern "C" {
#include "halide_blur.h"
}

Buffer<uint16_t> blur_halide(Buffer<uint16_t> in) {
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    // Call it once to initialize the halide runtime stuff
    halide_blur(in, out);

    t = benchmark(10, 1, [&]() {
        // Compute the same region of the output as blur_fast (i.e., we're
        // still being sloppy with boundary conditions)
        halide_blur(in, out);
    });

    return out;
}

int main(int argc, char **argv) {

    Buffer<uint16_t> input(6408, 4802);

    for (int y = 0; y < input.height(); y++) {
        for (int x = 0; x < input.width(); x++) {
            input(x, y) = rand() & 0xfff;
        }
    }

    Buffer<uint16_t> blurry = blur(input);
    double slow_time = t;

    Buffer<uint16_t> speedy = blur_fast(input);
    double fast_time = t;

    //Buffer<uint16_t> speedy2 = blur_fast2(input);
    //float fast_time2 = t;

    Buffer<uint16_t> halide = blur_halide(input);
    double halide_time = t;

    // fast_time2 is always slower than fast_time, so skip printing it
    printf("times: %f %f %f\n", slow_time, fast_time, halide_time);

    for (int y = 64; y < input.height() - 64; y++) {
        for (int x = 64; x < input.width() - 64; x++) {
            if (blurry(x, y) != speedy(x, y) || blurry(x, y) != halide(x, y))
                printf("difference at (%d,%d): %d %d %d\n", x, y, blurry(x, y), speedy(x, y), halide(x, y));
        }
    }

    return 0;
}
<commit_msg>Remove pointless extern C<commit_after>#include <emmintrin.h>
#include <cmath>
#include <cstdint>
#include <cstdio>

#include "benchmark.h"
#include "HalideBuffer.h"

using namespace Halide;

//#define cimg_display 0
//#include "CImg.h"
//using namespace cimg_library;

// typedef CImg<uint16_t> Image;

double t;

Buffer<uint16_t> blur(Buffer<uint16_t> in) {
    Buffer<uint16_t> tmp(in.width()-8, in.height());
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    t = benchmark(10, 1, [&]() {
        for (int y = 0; y < tmp.height(); y++)
            for (int x = 0; x < tmp.width(); x++)
                tmp(x, y) = (in(x, y) + in(x+1, y) + in(x+2, y))/3;

        for (int y = 0; y < out.height(); y++)
            for (int x = 0; x < out.width(); x++)
                out(x, y) = (tmp(x, y) + tmp(x, y+1) + tmp(x, y+2))/3;
    });

    return out;
}

Buffer<uint16_t> blur_fast(Buffer<uint16_t> in) {
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    t = benchmark(10, 1, [&]() {
        __m128i one_third = _mm_set1_epi16(21846);
#pragma omp parallel for
        for (int yTile = 0; yTile < out.height(); yTile += 32) {
            __m128i a, b, c, sum, avg;
            __m128i tmp[(128/8) * (32 + 2)];
            for (int xTile = 0; xTile < out.width(); xTile += 128) {
                __m128i *tmpPtr = tmp;
                for (int y = 0; y < 32+2; y++) {
                    const uint16_t *inPtr = &(in(xTile, yTile+y));
                    for (int x = 0; x < 128; x += 8) {
                        a = _mm_load_si128((const __m128i*)(inPtr));
                        b = _mm_loadu_si128((const __m128i*)(inPtr+1));
                        c = _mm_loadu_si128((const __m128i*)(inPtr+2));
                        sum = _mm_add_epi16(_mm_add_epi16(a, b), c);
                        avg = _mm_mulhi_epi16(sum, one_third);
                        _mm_store_si128(tmpPtr++, avg);
                        inPtr+=8;
                    }
                }
                tmpPtr = tmp;
                for (int y = 0; y < 32; y++) {
                    __m128i *outPtr = (__m128i *)(&(out(xTile, yTile+y)));
                    for (int x = 0; x < 128; x += 8) {
                        a = _mm_load_si128(tmpPtr+(2*128)/8);
                        b = _mm_load_si128(tmpPtr+128/8);
                        c = _mm_load_si128(tmpPtr++);
                        sum = _mm_add_epi16(_mm_add_epi16(a, b), c);
                        avg = _mm_mulhi_epi16(sum, one_third);
                        _mm_store_si128(outPtr++, avg);
                    }
                }
            }
        }
    });

    return out;
}

/*
  Image blur_fast(const Image &in) {
  Image out(in.width(), in.height());

  __m128i one_third = _mm_set1_epi16(21846);
  #pragma omp parallel for
  for (int yTile = 0; yTile < in.height(); yTile += 64) {
  __m128i tmp[(64/8)*(64+2)];
  for (int xTile = 0; xTile < in.width(); xTile += 64) {
  __m128i *tmpPtr = tmp;
  for (int y = -1; y < 64+1; y++) {
  const uint16_t *inPtr = &(in(xTile, yTile+y));
  for (int x = 0; x < 64; x += 8) {
  __m128i val = _mm_loadu_si128((__m128i *)(inPtr-1));
  val = _mm_add_epi16(val, _mm_load_si128((__m128i *)inPtr));
  val = _mm_add_epi16(val, _mm_loadu_si128((__m128i *)(inPtr+1)));
  val = _mm_mulhi_epi16(val, one_third);
  _mm_store_si128(tmpPtr++, val);
  inPtr += 8;
  }
  }
  tmpPtr = tmp;
  for (int y = 0; y < 64; y++) {
  __m128i *outPtr = (__m128i *)(&(out(xTile, yTile+y)));
  for (int x = 0; x < 64; x += 8) {
  __m128i val = _mm_load_si128(tmpPtr);
  val = _mm_add_epi16(val, _mm_load_si128(tmpPtr+64/8));
  val = _mm_add_epi16(val, _mm_load_si128(tmpPtr+(2*64)/8));
  val = _mm_mulhi_epi16(val, one_third);
  _mm_store_si128(outPtr++, val);
  tmpPtr++;
  }
  }
  }
  }

  return out;
  }
*/

Buffer<uint16_t> blur_fast2(const Buffer<uint16_t> &in) {
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    int vw = in.width()/8;
    if (vw > 1024) {
        printf("Image too large for constant-sized stack allocation\n");
        return out;
    }

    t = benchmark(10, 1, [&]() {
        // multiplying by 21846 then taking the top 16 bits is equivalent to
        // dividing by three
        __m128i one_third = _mm_set1_epi16(21846);

#pragma omp parallel for
        for (int yTile = 0; yTile < in.height(); yTile += 128) {
            __m128i tmp[1024*4]; // four scanlines
            for (int y = -2; y < 128; y++) {
                // to produce this scanline of the output
                __m128i *outPtr = (__m128i *)(&(out(0, yTile + y)));
                // we use this scanline of the input
                const uint16_t *inPtr = &(in(0, yTile + y + 2));
                // and these scanlines of the intermediate result
                // We start y at negative 2 to fill the tmp buffer
                __m128i *tmpPtr0 = tmp + ((y+4) & 3) * vw;
                __m128i *tmpPtr1 = tmp + ((y+3) & 3) * vw;
                __m128i *tmpPtr2 = tmp + ((y+2) & 3) * vw;
                for (int x = 0; x < vw; x++) {
                    // blur horizontally to produce next scanline of tmp
                    __m128i val = _mm_load_si128((const __m128i *)(inPtr));
                    val = _mm_add_epi16(val, _mm_loadu_si128((const __m128i *)(inPtr+1)));
                    val = _mm_add_epi16(val, _mm_loadu_si128((const __m128i *)(inPtr+2)));
                    val = _mm_mulhi_epi16(val, one_third);
                    _mm_store_si128(tmpPtr0++, val);

                    // blur vertically using previous scanlines of tmp to produce output
                    if (y >= 0) {
                        val = _mm_add_epi16(val, _mm_load_si128(tmpPtr1++));
                        val = _mm_add_epi16(val, _mm_load_si128(tmpPtr2++));
                        val = _mm_mulhi_epi16(val, one_third);
                        _mm_store_si128(outPtr++, val);
                    }

                    inPtr += 8;
                }
            }
        }

    });

    return out;
}

#include "halide_blur.h"

Buffer<uint16_t> blur_halide(Buffer<uint16_t> in) {
    Buffer<uint16_t> out(in.width()-8, in.height()-2);

    // Call it once to initialize the halide runtime stuff
    halide_blur(in, out);

    t = benchmark(10, 1, [&]() {
        // Compute the same region of the output as blur_fast (i.e., we're
        // still being sloppy with boundary conditions)
        halide_blur(in, out);
    });

    return out;
}

int main(int argc, char **argv) {

    Buffer<uint16_t> input(6408, 4802);

    for (int y = 0; y < input.height(); y++) {
        for (int x = 0; x < input.width(); x++) {
            input(x, y) = rand() & 0xfff;
        }
    }

    Buffer<uint16_t> blurry = blur(input);
    double slow_time = t;

    Buffer<uint16_t> speedy = blur_fast(input);
    double fast_time = t;

    //Buffer<uint16_t> speedy2 = blur_fast2(input);
    //float fast_time2 = t;

    Buffer<uint16_t> halide = blur_halide(input);
    double halide_time = t;

    // fast_time2 is always slower than fast_time, so skip printing it
    printf("times: %f %f %f\n", slow_time, fast_time, halide_time);

    for (int y = 64; y < input.height() - 64; y++) {
        for (int x = 64; x < input.width() - 64; x++) {
            if (blurry(x, y) != speedy(x, y) || blurry(x, y) != halide(x, y))
                printf("difference at (%d,%d): %d %d %d\n", x, y, blurry(x, y), speedy(x, y), halide(x, y));
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>// BodyBodyConstraint.cpp
//
// Breannan Smith
// Last updated: 01/07/2016

#include "BodyBodyConstraint.h"

#include "scisim/Math/MathUtilities.h"

BodyBodyConstraint::BodyBodyConstraint( const unsigned idx0, const unsigned idx1, const Vector2s& p, const Vector2s& n, const VectorXs& q )
: m_idx0( idx0 )
, m_idx1( idx1 )
, m_n( n )
, m_r0( p - q.segment<2>( 3 * m_idx0 ) )
, m_r1( p - q.segment<2>( 3 * m_idx1 ) )
{
  assert( m_idx0 != m_idx1 );
  assert( m_idx0 < m_idx1 );
  assert( fabs( m_n.norm() - 1.0 ) <= 1.0e-6 );
}

scalar BodyBodyConstraint::evalNdotV( const VectorXs& q, const VectorXs& v ) const
{
  return m_n.dot( computeRelativeVelocity( q, v ) );
}

int BodyBodyConstraint::impactStencilSize() const
{
  return 3;
}

void BodyBodyConstraint::getSimulatedBodyIndices( std::pair<int,int>& bodies ) const
{
  bodies.first = m_idx0;
  bodies.second = m_idx1;
}

void BodyBodyConstraint::getBodyIndices( std::pair<int,int>& bodies ) const
{
  bodies.first = m_idx0;
  bodies.second = m_idx1;
}

void BodyBodyConstraint::evalH( const VectorXs& q, const MatrixXXsc& basis, MatrixXXsc& H0, MatrixXXsc& H1 ) const
{
  assert( H0.rows() == 2 ); assert( H0.cols() == 3 );
  assert( H1.rows() == 2 ); assert( H1.cols() == 3 );
  assert( ( basis * basis.transpose() - MatrixXXsc::Identity( 2, 2 ) ).lpNorm<Eigen::Infinity>() <= 1.0e-6 );
  assert( fabs( basis.determinant() - 1.0 ) <= 1.0e-6 );

  // Grab the contact normal
  const Vector2s n{ basis.col( 0 ) };
  // Grab the tangent basis
  const Vector2s t{ basis.col( 1 ) };

  // Format for H:
  //   n^T  r x n
  //   t^T  r x t

  H0.block<1,2>(0,0) = n;
  H0(0,2) = MathUtilities::cross( m_r0, n );

  H0.block<1,2>(1,0) = t;
  H0(1,2) = MathUtilities::cross( m_r0, t );

  H1.block<1,2>(0,0) = n;
  H1(0,2) = MathUtilities::cross( m_r1, n );

  H1.block<1,2>(1,0) = t;
  H1(1,2) = MathUtilities::cross( m_r1, t );
}

void BodyBodyConstraint::computeContactBasis( const VectorXs& q, const VectorXs& v, MatrixXXsc& basis ) const
{
  assert( fabs( m_n.norm() - 1.0 ) <= 1.0e-6 );
  const Vector2s t{ -m_n.y(), m_n.x() };
  assert( fabs( t.norm() - 1.0 ) <= 1.0e-6 ); assert( fabs( m_n.dot( t ) ) <= 1.0e-6 );
  basis.resize( 2, 2 );
  basis.col( 0 ) = m_n;
  basis.col( 1 ) = t;
}

bool BodyBodyConstraint::conservesTranslationalMomentum() const
{
  return true;
}

bool BodyBodyConstraint::conservesAngularMomentumUnderImpact() const
{
  return true;
}

bool BodyBodyConstraint::conservesAngularMomentumUnderImpactAndFriction() const
{
  return true;
}

std::string BodyBodyConstraint::name() const
{
  return "body_body";
}

VectorXs BodyBodyConstraint::computeRelativeVelocity( const VectorXs& q, const VectorXs& v ) const
{
  assert( v.size() % 3 == 0 );
  assert( 3 * m_idx0 + 2 < v.size() );
  assert( 3 * m_idx1 + 2 < v.size() );

  const Vector2s t0{ -m_r0.y(), m_r0.x() };
  const Vector2s t1{ -m_r1.y(), m_r1.x() };

  // v_0 + omega_0 x r_0 - ( v_1 + omega_1 x r_1 )
  return v.segment<2>( 3 * m_idx0 ) + v( 3 * m_idx0 + 2 ) * t0 - v.segment<2>( 3 * m_idx1 ) - v( 3 * m_idx1 + 2 ) * t1;
}

void BodyBodyConstraint::setBodyIndex0( const unsigned idx )
{
  m_idx0 = idx;
}

void BodyBodyConstraint::setBodyIndex1( const unsigned idx )
{
  m_idx1 = idx;
}

VectorXs BodyBodyConstraint::computeKinematicRelativeVelocity( const VectorXs& q, const VectorXs& v ) const
{
  // No kinematic contribution
  return VectorXs::Zero( 2 );
}

void BodyBodyConstraint::getWorldSpaceContactPoint( const VectorXs& q, VectorXs& contact_point ) const
{
  contact_point = m_r0 + q.segment<2>( 3 * m_idx0 );
  #ifndef NDEBUG
  const VectorXs p{ m_r1 + q.segment<2>( 3 * m_idx1 ) };
  assert( ( p - contact_point ).lpNorm<Eigen::Infinity>() <= 1.0e-6 );
  #endif
}

void BodyBodyConstraint::getWorldSpaceContactNormal( const VectorXs& q, VectorXs& contact_normal ) const
{
  contact_normal = m_n;
}
<commit_msg>The impact stencil size was off by 2x for the 2D rigid body body-body constraint.<commit_after>// BodyBodyConstraint.cpp
//
// Breannan Smith
// Last updated: 01/07/2016

#include "BodyBodyConstraint.h"

#include "scisim/Math/MathUtilities.h"

BodyBodyConstraint::BodyBodyConstraint( const unsigned idx0, const unsigned idx1, const Vector2s& p, const Vector2s& n, const VectorXs& q )
: m_idx0( idx0 )
, m_idx1( idx1 )
, m_n( n )
, m_r0( p - q.segment<2>( 3 * m_idx0 ) )
, m_r1( p - q.segment<2>( 3 * m_idx1 ) )
{
  assert( m_idx0 != m_idx1 );
  assert( m_idx0 < m_idx1 );
  assert( fabs( m_n.norm() - 1.0 ) <= 1.0e-6 );
}

scalar BodyBodyConstraint::evalNdotV( const VectorXs& q, const VectorXs& v ) const
{
  return m_n.dot( computeRelativeVelocity( q, v ) );
}

int BodyBodyConstraint::impactStencilSize() const
{
  return 6;
}

void BodyBodyConstraint::getSimulatedBodyIndices( std::pair<int,int>& bodies ) const
{
  bodies.first = m_idx0;
  bodies.second = m_idx1;
}

void BodyBodyConstraint::getBodyIndices( std::pair<int,int>& bodies ) const
{
  bodies.first = m_idx0;
  bodies.second = m_idx1;
}

void BodyBodyConstraint::evalH( const VectorXs& q, const MatrixXXsc& basis, MatrixXXsc& H0, MatrixXXsc& H1 ) const
{
  assert( H0.rows() == 2 ); assert( H0.cols() == 3 );
  assert( H1.rows() == 2 ); assert( H1.cols() == 3 );
  assert( ( basis * basis.transpose() - MatrixXXsc::Identity( 2, 2 ) ).lpNorm<Eigen::Infinity>() <= 1.0e-6 );
  assert( fabs( basis.determinant() - 1.0 ) <= 1.0e-6 );

  // Grab the contact normal
  const Vector2s n{ basis.col( 0 ) };
  // Grab the tangent basis
  const Vector2s t{ basis.col( 1 ) };

  // Format for H:
  //   n^T  r x n
  //   t^T  r x t

  H0.block<1,2>(0,0) = n;
  H0(0,2) = MathUtilities::cross( m_r0, n );

  H0.block<1,2>(1,0) = t;
  H0(1,2) = MathUtilities::cross( m_r0, t );

  H1.block<1,2>(0,0) = n;
  H1(0,2) = MathUtilities::cross( m_r1, n );

  H1.block<1,2>(1,0) = t;
  H1(1,2) = MathUtilities::cross( m_r1, t );
}

void BodyBodyConstraint::computeContactBasis( const VectorXs& q, const VectorXs& v, MatrixXXsc& basis ) const
{
  assert( fabs( m_n.norm() - 1.0 ) <= 1.0e-6 );
  const Vector2s t{ -m_n.y(), m_n.x() };
  assert( fabs( t.norm() - 1.0 ) <= 1.0e-6 ); assert( fabs( m_n.dot( t ) ) <= 1.0e-6 );
  basis.resize( 2, 2 );
  basis.col( 0 ) = m_n;
  basis.col( 1 ) = t;
}

bool BodyBodyConstraint::conservesTranslationalMomentum() const
{
  return true;
}

bool BodyBodyConstraint::conservesAngularMomentumUnderImpact() const
{
  return true;
}

bool BodyBodyConstraint::conservesAngularMomentumUnderImpactAndFriction() const
{
  return true;
}

std::string BodyBodyConstraint::name() const
{
  return "body_body";
}

VectorXs BodyBodyConstraint::computeRelativeVelocity( const VectorXs& q, const VectorXs& v ) const
{
  assert( v.size() % 3 == 0 );
  assert( 3 * m_idx0 + 2 < v.size() );
  assert( 3 * m_idx1 + 2 < v.size() );

  const Vector2s t0{ -m_r0.y(), m_r0.x() };
  const Vector2s t1{ -m_r1.y(), m_r1.x() };

  // v_0 + omega_0 x r_0 - ( v_1 + omega_1 x r_1 )
  return v.segment<2>( 3 * m_idx0 ) + v( 3 * m_idx0 + 2 ) * t0 - v.segment<2>( 3 * m_idx1 ) - v( 3 * m_idx1 + 2 ) * t1;
}

void BodyBodyConstraint::setBodyIndex0( const unsigned idx )
{
  m_idx0 = idx;
}

void BodyBodyConstraint::setBodyIndex1( const unsigned idx )
{
  m_idx1 = idx;
}

VectorXs BodyBodyConstraint::computeKinematicRelativeVelocity( const VectorXs& q, const VectorXs& v ) const
{
  // No kinematic contribution
  return VectorXs::Zero( 2 );
}

void BodyBodyConstraint::getWorldSpaceContactPoint( const VectorXs& q, VectorXs& contact_point ) const
{
  contact_point = m_r0 + q.segment<2>( 3 * m_idx0 );
  #ifndef NDEBUG
  const VectorXs p{ m_r1 + q.segment<2>( 3 * m_idx1 ) };
  assert( ( p - contact_point ).lpNorm<Eigen::Infinity>() <= 1.0e-6 );
  #endif
}

void BodyBodyConstraint::getWorldSpaceContactNormal( const VectorXs& q, VectorXs& contact_normal ) const
{
  contact_normal = m_n;
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_ARR_ERR_CHECK_OPENCL_HPP_
#define STAN_MATH_PRIM_ARR_ERR_CHECK_OPENCL_HPP_

#define __CL_ENABLE_EXCEPTIONS
#include <CL/cl.hpp>
#include <iostream>
#include <stdexcept>
#include <string>

/** @file stan/math/prim/arr/err/check_opencl.hpp
 *    @brief checking OpenCL error numbers
 */

namespace stan {
namespace math {

/**
 * Function that throws the OpenCL exception with the
 * given explanation
 *
 * @param function the name of the function where the error occured
 * @param msg information on the OpenCL error
 *
 * @throw std::domain_error Always.
 */
inline void throw_openCL(const char *function, const char *msg) {
  std::string error_msg
      = std::string() + function
        + ": The OpenCL application ended with the error: " + msg;
  throw std::domain_error(error_msg);
}
/**
 * Throws the domain error with specifying the OpenCL error that
 * occured. It outputs the OpenCL errors that are specified
 * in OpenCL 2.0. If no matching error number is found,
 * it throws the error with the number.
 *
 * @param function the name of the function where the error occured
 * @param e The error number
 *
 * @throw std::domain_error Always.
 */
inline void check_ocl_error(const char *function, const cl::Error &e) {
  switch (e.err()) {
    case 0:
      // CL_SUCCESS - no need to throw
      return;
    case -1:
      throw_openCL(function, "CL_DEVICE_NOT_FOUND");
    case -2:
      throw_openCL(function, "CL_DEVICE_NOT_AVAILABLE");
    case -3:
      throw_openCL(function, "CL_COMPILER_NOT_AVAILABLE");
    case -4:
      throw_openCL(function, "CL_MEM_OBJECT_ALLOCATION_FAILURE");
    case -5:
      throw_openCL(function, "CL_OUT_OF_RESOURCES");
    case -6:
      throw_openCL(function, "CL_OUT_OF_HOST_MEMORY");
    case -7:
      throw_openCL(function, "CL_PROFILING_INFO_NOT_AVAILABLE");
    case -8:
      throw_openCL(function, "CL_MEM_COPY_OVERLAP");
    case -9:
      throw_openCL(function, "CL_IMAGE_FORMAT_MISMATCH");
    case -10:
      throw_openCL(function, "CL_IMAGE_FORMAT_NOT_SUPPORTED");
    case -11:
      throw_openCL(function, "CL_BUILD_PROGRAM_FAILURE");
    case -12:
      throw_openCL(function, "CL_MAP_FAILURE");
    case -13:
      throw_openCL(function, "CL_MISALIGNED_SUB_BUFFER_OFFSET");
    case -14:
      throw_openCL(function, "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST");
    case -15:
      throw_openCL(function, "CL_COMPILE_PROGRAM_FAILURE");
    case -16:
      throw_openCL(function, "CL_LINKER_NOT_AVAILABLE");
    case -17:
      throw_openCL(function, "CL_LINK_PROGRAM_FAILURE");
    case -18:
      throw_openCL(function, "CL_DEVICE_PARTITION_FAILED");
    case -19:
      throw_openCL(function, "CL_KERNEL_ARG_INFO_NOT_AVAILABLE");
    case -30:
      throw_openCL(function, "CL_INVALID_VALUE");
    case -31:
      throw_openCL(function, "CL_INVALID_DEVICE_TYPE");
    case -32:
      throw_openCL(function, "CL_INVALID_PLATFORM");
    case -33:
      throw_openCL(function, "CL_INVALID_DEVICE");
    case -34:
      throw_openCL(function, "CL_INVALID_CONTEXT");
    case -35:
      throw_openCL(function, "CL_INVALID_QUEUE_PROPERTIES");
    case -36:
      throw_openCL(function, "CL_INVALID_COMMAND_QUEUE");
    case -37:
      throw_openCL(function, "CL_INVALID_HOST_PTR");
    case -38:
      throw_openCL(function, "CL_INVALID_MEM_OBJECT");
    case -39:
      throw_openCL(function, "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR");
    case -40:
      throw_openCL(function, "CL_INVALID_IMAGE_SIZE");
    case -41:
      throw_openCL(function, "CL_INVALID_SAMPLER");
    case -42:
      throw_openCL(function, "CL_INVALID_BINARY");
    case -43:
      throw_openCL(function, "CL_INVALID_BUILD_OPTIONS");
    case -44:
      throw_openCL(function, "CL_INVALID_PROGRAM");
    case -45:
      throw_openCL(function, "CL_INVALID_PROGRAM_EXECUTABLE");
    case -46:
      throw_openCL(function, "CL_INVALID_KERNEL_NAME");
    case -47:
      throw_openCL(function, "CL_INVALID_KERNEL_DEFINITION");
    case -48:
      throw_openCL(function, "CL_INVALID_KERNEL");
    case -49:
      throw_openCL(function, "CL_INVALID_ARG_INDEX");
    case -50:
      throw_openCL(function, "CL_INVALID_ARG_VALUE");
    case -51:
      throw_openCL(function, "CL_INVALID_ARG_SIZE");
    case -52:
      throw_openCL(function, "CL_INVALID_KERNEL_ARGS");
    case -53:
      throw_openCL(function, "CL_INVALID_WORK_DIMENSION");
    case -54:
      throw_openCL(function, "CL_INVALID_WORK_GROUP_SIZE");
    case -55:
      throw_openCL(function, "CL_INVALID_WORK_ITEM_SIZE");
    case -56:
      throw_openCL(function, "CL_INVALID_GLOBAL_OFFSET");
    case -57:
      throw_openCL(function, "CL_INVALID_EVENT_WAIT_LIST");
    case -58:
      throw_openCL(function, "CL_INVALID_EVENT");
    case -59:
      throw_openCL(function, "CL_INVALID_OPERATION");
    case -60:
      throw_openCL(function, "CL_INVALID_GL_OBJECT");
    case -61:
      throw_openCL(function, "CL_INVALID_BUFFER_SIZE");
    case -62:
      throw_openCL(function, "CL_INVALID_MIP_LEVEL");
    case -63:
      throw_openCL(function, "CL_INVALID_GLOBAL_WORK_SIZE");
    case -64:
      throw_openCL(function, "CL_INVALID_PROPERTY");
    case -65:
      throw_openCL(function, "CL_INVALID_IMAGE_DESCRIPTOR");
    case -66:
      throw_openCL(function, "CL_INVALID_COMPILER_OPTIONS");
    case -67:
      throw_openCL(function, "CL_INVALID_LINKER_OPTIONS");
    case -68:
      throw_openCL(function, "CL_INVALID_DEVICE_PARTITION_COUNT");
    case -69:
      throw_openCL(function, "CL_INVALID_PIPE_SIZE");
    case -70:
      throw_openCL(function, "CL_INVALID_DEVICE_QUEUE");
    case -1000:
      throw_openCL(function, "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR");
    case -1001:
      throw_openCL(function, "CL_PLATFORM_NOT_FOUND_KHR");
    case -1002:
      throw_openCL(function, "CL_INVALID_D3D10_DEVICE_KHR");
    case -1003:
      throw_openCL(function, "CL_INVALID_D3D10_RESOURCE_KHR");
    case -1004:
      throw_openCL(function, "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR");
    case -1005:
      throw_openCL(function, "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR");
    case -1006:
      throw_openCL(function, "CL_INVALID_D3D11_DEVICE_KHR");
    case -1007:
      throw_openCL(function, "CL_INVALID_D3D11_RESOURCE_KHR");
    case -1008:
      throw_openCL(function, "CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR");
    case -1009:
      throw_openCL(function, "CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR");
    case -101:
      throw_openCL(function, "CL_INVALID_D3D9_DEVICE_NV ");
    case -1011:
      throw_openCL(function, "CL_INVALID_D3D9_RESOURCE_NV ");
    case -1012:
      throw_openCL(function,
                   "CL_D3D9_RESOURCE_ALREADY_ACQUIRED_NV "
                   "CL_DX9_RESOURCE_ALREADY_ACQUIRED_INTEL");
    case -1013:
      throw_openCL(function,
                   "CL_D3D9_RESOURCE_NOT_ACQUIRED_NV "
                   "CL_DX9_RESOURCE_NOT_ACQUIRED_INTEL");
    case -1092:
      throw_openCL(function, "CL_EGL_RESOURCE_NOT_ACQUIRED_KHR");
    case -1093:
      throw_openCL(function, "CL_INVALID_EGL_OBJECT_KHR");
    case -1094:
      throw_openCL(function, "CL_INVALID_ACCELERATOR_INTEL");
    case -1095:
      throw_openCL(function, "CL_INVALID_ACCELERATOR_TYPE_INTEL");
    case -1096:
      throw_openCL(function, "CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL");
    case -1097:
      throw_openCL(function, "CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL");
    case -1098:
      throw_openCL(function, "CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL");
    case -1099:
      throw_openCL(function, "CL_INVALID_VA_API_MEDIA_SURFACE_INTEL");
    case -1100:
      throw_openCL(function, "CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL");
    case -1101:
      throw_openCL(function, "CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL");
    case -9999:
      throw_openCL(function, "ILLEGAL_READ_OR_WRITE_NVIDIA");
    default:
      throw_openCL(function, std::to_string(e.err()).c_str());
  }
}
}  // namespace math
}  // namespace stan
#endif
<commit_msg>updating header guard<commit_after>#ifndef STAN_MATH_PRIM_ARR_ERR_CHECK_OPENCL_HPP
#define STAN_MATH_PRIM_ARR_ERR_CHECK_OPENCL_HPP

#define __CL_ENABLE_EXCEPTIONS
#include <CL/cl.hpp>
#include <iostream>
#include <stdexcept>
#include <string>

/** @file stan/math/prim/arr/err/check_opencl.hpp
 *    @brief checking OpenCL error numbers
 */

namespace stan {
namespace math {

/**
 * Function that throws the OpenCL exception with the
 * given explanation
 *
 * @param function the name of the function where the error occured
 * @param msg information on the OpenCL error
 *
 * @throw std::domain_error Always.
 */
inline void throw_openCL(const char *function, const char *msg) {
  std::string error_msg
      = std::string() + function
        + ": The OpenCL application ended with the error: " + msg;
  throw std::domain_error(error_msg);
}
/**
 * Throws the domain error with specifying the OpenCL error that
 * occured. It outputs the OpenCL errors that are specified
 * in OpenCL 2.0. If no matching error number is found,
 * it throws the error with the number.
 *
 * @param function the name of the function where the error occured
 * @param e The error number
 *
 * @throw std::domain_error Always.
 */
inline void check_ocl_error(const char *function, const cl::Error &e) {
  switch (e.err()) {
    case 0:
      // CL_SUCCESS - no need to throw
      return;
    case -1:
      throw_openCL(function, "CL_DEVICE_NOT_FOUND");
    case -2:
      throw_openCL(function, "CL_DEVICE_NOT_AVAILABLE");
    case -3:
      throw_openCL(function, "CL_COMPILER_NOT_AVAILABLE");
    case -4:
      throw_openCL(function, "CL_MEM_OBJECT_ALLOCATION_FAILURE");
    case -5:
      throw_openCL(function, "CL_OUT_OF_RESOURCES");
    case -6:
      throw_openCL(function, "CL_OUT_OF_HOST_MEMORY");
    case -7:
      throw_openCL(function, "CL_PROFILING_INFO_NOT_AVAILABLE");
    case -8:
      throw_openCL(function, "CL_MEM_COPY_OVERLAP");
    case -9:
      throw_openCL(function, "CL_IMAGE_FORMAT_MISMATCH");
    case -10:
      throw_openCL(function, "CL_IMAGE_FORMAT_NOT_SUPPORTED");
    case -11:
      throw_openCL(function, "CL_BUILD_PROGRAM_FAILURE");
    case -12:
      throw_openCL(function, "CL_MAP_FAILURE");
    case -13:
      throw_openCL(function, "CL_MISALIGNED_SUB_BUFFER_OFFSET");
    case -14:
      throw_openCL(function, "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST");
    case -15:
      throw_openCL(function, "CL_COMPILE_PROGRAM_FAILURE");
    case -16:
      throw_openCL(function, "CL_LINKER_NOT_AVAILABLE");
    case -17:
      throw_openCL(function, "CL_LINK_PROGRAM_FAILURE");
    case -18:
      throw_openCL(function, "CL_DEVICE_PARTITION_FAILED");
    case -19:
      throw_openCL(function, "CL_KERNEL_ARG_INFO_NOT_AVAILABLE");
    case -30:
      throw_openCL(function, "CL_INVALID_VALUE");
    case -31:
      throw_openCL(function, "CL_INVALID_DEVICE_TYPE");
    case -32:
      throw_openCL(function, "CL_INVALID_PLATFORM");
    case -33:
      throw_openCL(function, "CL_INVALID_DEVICE");
    case -34:
      throw_openCL(function, "CL_INVALID_CONTEXT");
    case -35:
      throw_openCL(function, "CL_INVALID_QUEUE_PROPERTIES");
    case -36:
      throw_openCL(function, "CL_INVALID_COMMAND_QUEUE");
    case -37:
      throw_openCL(function, "CL_INVALID_HOST_PTR");
    case -38:
      throw_openCL(function, "CL_INVALID_MEM_OBJECT");
    case -39:
      throw_openCL(function, "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR");
    case -40:
      throw_openCL(function, "CL_INVALID_IMAGE_SIZE");
    case -41:
      throw_openCL(function, "CL_INVALID_SAMPLER");
    case -42:
      throw_openCL(function, "CL_INVALID_BINARY");
    case -43:
      throw_openCL(function, "CL_INVALID_BUILD_OPTIONS");
    case -44:
      throw_openCL(function, "CL_INVALID_PROGRAM");
    case -45:
      throw_openCL(function, "CL_INVALID_PROGRAM_EXECUTABLE");
    case -46:
      throw_openCL(function, "CL_INVALID_KERNEL_NAME");
    case -47:
      throw_openCL(function, "CL_INVALID_KERNEL_DEFINITION");
    case -48:
      throw_openCL(function, "CL_INVALID_KERNEL");
    case -49:
      throw_openCL(function, "CL_INVALID_ARG_INDEX");
    case -50:
      throw_openCL(function, "CL_INVALID_ARG_VALUE");
    case -51:
      throw_openCL(function, "CL_INVALID_ARG_SIZE");
    case -52:
      throw_openCL(function, "CL_INVALID_KERNEL_ARGS");
    case -53:
      throw_openCL(function, "CL_INVALID_WORK_DIMENSION");
    case -54:
      throw_openCL(function, "CL_INVALID_WORK_GROUP_SIZE");
    case -55:
      throw_openCL(function, "CL_INVALID_WORK_ITEM_SIZE");
    case -56:
      throw_openCL(function, "CL_INVALID_GLOBAL_OFFSET");
    case -57:
      throw_openCL(function, "CL_INVALID_EVENT_WAIT_LIST");
    case -58:
      throw_openCL(function, "CL_INVALID_EVENT");
    case -59:
      throw_openCL(function, "CL_INVALID_OPERATION");
    case -60:
      throw_openCL(function, "CL_INVALID_GL_OBJECT");
    case -61:
      throw_openCL(function, "CL_INVALID_BUFFER_SIZE");
    case -62:
      throw_openCL(function, "CL_INVALID_MIP_LEVEL");
    case -63:
      throw_openCL(function, "CL_INVALID_GLOBAL_WORK_SIZE");
    case -64:
      throw_openCL(function, "CL_INVALID_PROPERTY");
    case -65:
      throw_openCL(function, "CL_INVALID_IMAGE_DESCRIPTOR");
    case -66:
      throw_openCL(function, "CL_INVALID_COMPILER_OPTIONS");
    case -67:
      throw_openCL(function, "CL_INVALID_LINKER_OPTIONS");
    case -68:
      throw_openCL(function, "CL_INVALID_DEVICE_PARTITION_COUNT");
    case -69:
      throw_openCL(function, "CL_INVALID_PIPE_SIZE");
    case -70:
      throw_openCL(function, "CL_INVALID_DEVICE_QUEUE");
    case -1000:
      throw_openCL(function, "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR");
    case -1001:
      throw_openCL(function, "CL_PLATFORM_NOT_FOUND_KHR");
    case -1002:
      throw_openCL(function, "CL_INVALID_D3D10_DEVICE_KHR");
    case -1003:
      throw_openCL(function, "CL_INVALID_D3D10_RESOURCE_KHR");
    case -1004:
      throw_openCL(function, "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR");
    case -1005:
      throw_openCL(function, "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR");
    case -1006:
      throw_openCL(function, "CL_INVALID_D3D11_DEVICE_KHR");
    case -1007:
      throw_openCL(function, "CL_INVALID_D3D11_RESOURCE_KHR");
    case -1008:
      throw_openCL(function, "CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR");
    case -1009:
      throw_openCL(function, "CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR");
    case -101:
      throw_openCL(function, "CL_INVALID_D3D9_DEVICE_NV ");
    case -1011:
      throw_openCL(function, "CL_INVALID_D3D9_RESOURCE_NV ");
    case -1012:
      throw_openCL(function,
                   "CL_D3D9_RESOURCE_ALREADY_ACQUIRED_NV "
                   "CL_DX9_RESOURCE_ALREADY_ACQUIRED_INTEL");
    case -1013:
      throw_openCL(function,
                   "CL_D3D9_RESOURCE_NOT_ACQUIRED_NV "
                   "CL_DX9_RESOURCE_NOT_ACQUIRED_INTEL");
    case -1092:
      throw_openCL(function, "CL_EGL_RESOURCE_NOT_ACQUIRED_KHR");
    case -1093:
      throw_openCL(function, "CL_INVALID_EGL_OBJECT_KHR");
    case -1094:
      throw_openCL(function, "CL_INVALID_ACCELERATOR_INTEL");
    case -1095:
      throw_openCL(function, "CL_INVALID_ACCELERATOR_TYPE_INTEL");
    case -1096:
      throw_openCL(function, "CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL");
    case -1097:
      throw_openCL(function, "CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL");
    case -1098:
      throw_openCL(function, "CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL");
    case -1099:
      throw_openCL(function, "CL_INVALID_VA_API_MEDIA_SURFACE_INTEL");
    case -1100:
      throw_openCL(function, "CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL");
    case -1101:
      throw_openCL(function, "CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL");
    case -9999:
      throw_openCL(function, "ILLEGAL_READ_OR_WRITE_NVIDIA");
    default:
      throw_openCL(function, std::to_string(e.err()).c_str());
  }
}
}  // namespace math
}  // namespace stan
#endif
<|endoftext|>


<commit_before>#ifndef STAN_MATH_REV_FUN_MDIVIDE_LEFT_LDLT_HPP
#define STAN_MATH_REV_FUN_MDIVIDE_LEFT_LDLT_HPP

#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/LDLT_alloc.hpp>
#include <stan/math/rev/fun/LDLT_factor.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/fun/typedefs.hpp>

namespace stan {
namespace math {
namespace internal {

template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_alloc : public chainable_alloc {
 public:
  virtual ~mdivide_left_ldlt_alloc() {}

  /**
   * This share_ptr is used to prevent copying the LDLT factorizations
   * for mdivide_left_ldlt(ldltA, b) when ldltA is a LDLT_factor<double>.
   * The pointer is shared with the LDLT_factor<double> class.
   **/
  boost::shared_ptr<Eigen::LDLT<Eigen::Matrix<double, R1, C1> > > ldltP_;
  Eigen::Matrix<double, R2, C2> C_;
};

/**
 * The vari for mdivide_left_ldlt(A, b) which handles the chain() call
 * for all elements of the result.  This vari follows the pattern
 * used in the other matrix operations where there is one "master"
 * vari whose value is never used and a large number of "slave" varis
 * whose chain() functions are never called because their adjoints are
 * set by the "master" vari.
 *
 * This class handles the var/var case.
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 */
template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_vv_vari : public vari {
 public:
  int M_;  // A.rows() = A.cols() = B.rows()
  int N_;  // B.cols()
  vari **variRefB_;
  vari **variRefC_;
  mdivide_left_ldlt_alloc<R1, C1, R2, C2> *alloc_;
  const LDLT_alloc<R1, C1> *alloc_ldlt_;

  mdivide_left_ldlt_vv_vari(const LDLT_factor<var, R1, C1> &A,
                            const Eigen::Matrix<var, R2, C2> &B)
      : vari(0.0),
        M_(A.rows()),
        N_(B.cols()),
        variRefB_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        variRefC_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        alloc_(new mdivide_left_ldlt_alloc<R1, C1, R2, C2>()),
        alloc_ldlt_(A.alloc_) {
    Eigen::Map<matrix_vi>(variRefB_, M_, N_) = B.vi();
    alloc_->C_ = B.val();
    alloc_ldlt_->ldlt_.solveInPlace(alloc_->C_);
    Eigen::Map<matrix_vi>(variRefC_, M_, N_)
        = alloc_->C_.unaryExpr([](double x) { return new vari(x, false); });
  }

  virtual void chain() {
    matrix_d adjB = Eigen::Map<matrix_vi>(variRefC_, M_, N_).adj();

    alloc_ldlt_->ldlt_.solveInPlace(adjB);

    const_cast<matrix_vi &>(alloc_ldlt_->variA_).adj()
        -= adjB * alloc_->C_.transpose();
    Eigen::Map<matrix_vi>(variRefB_, M_, N_).adj() += adjB;
  }
};

/**
 * The vari for mdivide_left_ldlt(A, b) which handles the chain() call
 * for all elements of the result.  This vari follows the pattern
 * used in the other matrix operations where there is one "master"
 * vari whose value is never used and a large number of "slave" varis
 * whose chain() functions are never called because their adjoints are
 * set by the "master" vari.
 *
 * This class handles the double/var case.
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 */
template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_dv_vari : public vari {
 public:
  int M_;  // A.rows() = A.cols() = B.rows()
  int N_;  // B.cols()
  vari **variRefB_;
  vari **variRefC_;
  mdivide_left_ldlt_alloc<R1, C1, R2, C2> *alloc_;

  mdivide_left_ldlt_dv_vari(const LDLT_factor<double, R1, C1> &A,
                            const Eigen::Matrix<var, R2, C2> &B)
      : vari(0.0),
        M_(A.rows()),
        N_(B.cols()),
        variRefB_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        variRefC_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        alloc_(new mdivide_left_ldlt_alloc<R1, C1, R2, C2>()) {
    Eigen::Map<matrix_vi>(variRefB_, M_, N_) = B.vi();
    alloc_->C_ = B.val();
    alloc_->ldltP_ = A.ldltP_;
    alloc_->ldltP_->solveInPlace(alloc_->C_);
    Eigen::Map<matrix_vi>(variRefC_, M_, N_)
        = alloc_->C_.unaryExpr([](double x) { return new vari(x, false); });
  }

  virtual void chain() {
    matrix_d adjB = Eigen::Map<matrix_vi>(variRefC_, M_, N_).adj();
    alloc_->ldltP_->solveInPlace(adjB);
    Eigen::Map<matrix_vi>(variRefB_, M_, N_).adj() += adjB;
  }
};

/**
 * The vari for mdivide_left_ldlt(A, b) which handles the chain() call
 * for all elements of the result.  This vari follows the pattern
 * used in the other matrix operations where there is one "master"
 * vari whose value is never used and a large number of "slave" varis
 * whose chain() functions are never called because their adjoints are
 * set by the "master" vari.
 *
 * This class handles the var/double case.
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 */
template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_vd_vari : public vari {
 public:
  int M_;  // A.rows() = A.cols() = B.rows()
  int N_;  // B.cols()
  vari **variRefC_;
  mdivide_left_ldlt_alloc<R1, C1, R2, C2> *alloc_;
  const LDLT_alloc<R1, C1> *alloc_ldlt_;

  mdivide_left_ldlt_vd_vari(const LDLT_factor<var, R1, C1> &A,
                            const Eigen::Matrix<double, R2, C2> &B)
      : vari(0.0),
        M_(A.rows()),
        N_(B.cols()),
        variRefC_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        alloc_(new mdivide_left_ldlt_alloc<R1, C1, R2, C2>()),
        alloc_ldlt_(A.alloc_) {
    alloc_->C_ = B;
    alloc_ldlt_->ldlt_.solveInPlace(alloc_->C_);
    Eigen::Map<matrix_vi>(variRefC_, M_, N_)
        = alloc_->C_.unaryExpr([](double x) { return new vari(x, false); });
  }

  virtual void chain() {
    matrix_d adjC = Eigen::Map<matrix_vi>(variRefC_, M_, N_).adj();

    const_cast<matrix_vi &>(alloc_ldlt_->variA_).adj()
        -= alloc_ldlt_->ldlt_.solve(adjC * alloc_->C_.transpose());
  }
};
}  // namespace internal

/**
 * Returns the solution of the system Ax=b given an LDLT_factor of A
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 *
 * @param A LDLT_factor
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if rows of b don't match the size of A.
 */
template <int R1, int C1, int R2, int C2>
inline Eigen::Matrix<var, R1, C2> mdivide_left_ldlt(
    const LDLT_factor<var, R1, C1> &A, const Eigen::Matrix<var, R2, C2> &b) {
  check_multiplicable("mdivide_left_ldlt", "A", A, "b", b);
  if (A.cols() == 0) {
    return {0, b.cols()};
  }

  internal::mdivide_left_ldlt_vv_vari<R1, C1, R2, C2> *baseVari
      = new internal::mdivide_left_ldlt_vv_vari<R1, C1, R2, C2>(A, b);

  Eigen::Matrix<var, R1, C2> res(b.rows(), b.cols());
  res.vi() = Eigen::Map<matrix_vi>(baseVari->variRefC_, res.rows(), res.cols());

  return res;
}

/**
 * Returns the solution of the system Ax=b given an LDLT_factor of A
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 *
 * @param A LDLT_factor
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if rows of b don't match the size of A.
 */
template <int R1, int C1, int R2, int C2>
inline Eigen::Matrix<var, R1, C2> mdivide_left_ldlt(
    const LDLT_factor<var, R1, C1> &A, const Eigen::Matrix<double, R2, C2> &b) {
  check_multiplicable("mdivide_left_ldlt", "A", A, "b", b);
  if (A.cols() == 0) {
    return {0, b.cols()};
  }

  internal::mdivide_left_ldlt_vd_vari<R1, C1, R2, C2> *baseVari
      = new internal::mdivide_left_ldlt_vd_vari<R1, C1, R2, C2>(A, b);

  Eigen::Matrix<var, R1, C2> res(b.rows(), b.cols());
  res.vi() = Eigen::Map<matrix_vi>(baseVari->variRefC_, res.rows(), res.cols());

  return res;
}

/**
 * Returns the solution of the system Ax=b given an LDLT_factor of A
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 *
 * @param A LDLT_factor
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if rows of b don't match the size of A.
 */
template <int R1, int C1, int R2, int C2>
inline Eigen::Matrix<var, R1, C2> mdivide_left_ldlt(
    const LDLT_factor<double, R1, C1> &A, const Eigen::Matrix<var, R2, C2> &b) {
  check_multiplicable("mdivide_left_ldlt", "A", A, "b", b);
  if (A.cols() == 0) {
    return {0, b.cols()};
  }

  internal::mdivide_left_ldlt_dv_vari<R1, C1, R2, C2> *baseVari
      = new internal::mdivide_left_ldlt_dv_vari<R1, C1, R2, C2>(A, b);

  Eigen::Matrix<var, R1, C2> res(b.rows(), b.cols());
  res.vi() = Eigen::Map<matrix_vi>(baseVari->variRefC_, res.rows(), res.cols());

  return res;
}

}  // namespace math
}  // namespace stan
#endif
<commit_msg>one more use of boost::shared_ptr<commit_after>#ifndef STAN_MATH_REV_FUN_MDIVIDE_LEFT_LDLT_HPP
#define STAN_MATH_REV_FUN_MDIVIDE_LEFT_LDLT_HPP

#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/LDLT_alloc.hpp>
#include <stan/math/rev/fun/LDLT_factor.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/prim/fun/typedefs.hpp>
#include <memory>

namespace stan {
namespace math {
namespace internal {

template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_alloc : public chainable_alloc {
 public:
  virtual ~mdivide_left_ldlt_alloc() {}

  /**
   * This share_ptr is used to prevent copying the LDLT factorizations
   * for mdivide_left_ldlt(ldltA, b) when ldltA is a LDLT_factor<double>.
   * The pointer is shared with the LDLT_factor<double> class.
   **/
  std::shared_ptr<Eigen::LDLT<Eigen::Matrix<double, R1, C1> > > ldltP_;
  Eigen::Matrix<double, R2, C2> C_;
};

/**
 * The vari for mdivide_left_ldlt(A, b) which handles the chain() call
 * for all elements of the result.  This vari follows the pattern
 * used in the other matrix operations where there is one "master"
 * vari whose value is never used and a large number of "slave" varis
 * whose chain() functions are never called because their adjoints are
 * set by the "master" vari.
 *
 * This class handles the var/var case.
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 */
template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_vv_vari : public vari {
 public:
  int M_;  // A.rows() = A.cols() = B.rows()
  int N_;  // B.cols()
  vari **variRefB_;
  vari **variRefC_;
  mdivide_left_ldlt_alloc<R1, C1, R2, C2> *alloc_;
  const LDLT_alloc<R1, C1> *alloc_ldlt_;

  mdivide_left_ldlt_vv_vari(const LDLT_factor<var, R1, C1> &A,
                            const Eigen::Matrix<var, R2, C2> &B)
      : vari(0.0),
        M_(A.rows()),
        N_(B.cols()),
        variRefB_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        variRefC_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        alloc_(new mdivide_left_ldlt_alloc<R1, C1, R2, C2>()),
        alloc_ldlt_(A.alloc_) {
    Eigen::Map<matrix_vi>(variRefB_, M_, N_) = B.vi();
    alloc_->C_ = B.val();
    alloc_ldlt_->ldlt_.solveInPlace(alloc_->C_);
    Eigen::Map<matrix_vi>(variRefC_, M_, N_)
        = alloc_->C_.unaryExpr([](double x) { return new vari(x, false); });
  }

  virtual void chain() {
    matrix_d adjB = Eigen::Map<matrix_vi>(variRefC_, M_, N_).adj();

    alloc_ldlt_->ldlt_.solveInPlace(adjB);

    const_cast<matrix_vi &>(alloc_ldlt_->variA_).adj()
        -= adjB * alloc_->C_.transpose();
    Eigen::Map<matrix_vi>(variRefB_, M_, N_).adj() += adjB;
  }
};

/**
 * The vari for mdivide_left_ldlt(A, b) which handles the chain() call
 * for all elements of the result.  This vari follows the pattern
 * used in the other matrix operations where there is one "master"
 * vari whose value is never used and a large number of "slave" varis
 * whose chain() functions are never called because their adjoints are
 * set by the "master" vari.
 *
 * This class handles the double/var case.
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 */
template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_dv_vari : public vari {
 public:
  int M_;  // A.rows() = A.cols() = B.rows()
  int N_;  // B.cols()
  vari **variRefB_;
  vari **variRefC_;
  mdivide_left_ldlt_alloc<R1, C1, R2, C2> *alloc_;

  mdivide_left_ldlt_dv_vari(const LDLT_factor<double, R1, C1> &A,
                            const Eigen::Matrix<var, R2, C2> &B)
      : vari(0.0),
        M_(A.rows()),
        N_(B.cols()),
        variRefB_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        variRefC_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        alloc_(new mdivide_left_ldlt_alloc<R1, C1, R2, C2>()) {
    Eigen::Map<matrix_vi>(variRefB_, M_, N_) = B.vi();
    alloc_->C_ = B.val();
    alloc_->ldltP_ = A.ldltP_;
    alloc_->ldltP_->solveInPlace(alloc_->C_);
    Eigen::Map<matrix_vi>(variRefC_, M_, N_)
        = alloc_->C_.unaryExpr([](double x) { return new vari(x, false); });
  }

  virtual void chain() {
    matrix_d adjB = Eigen::Map<matrix_vi>(variRefC_, M_, N_).adj();
    alloc_->ldltP_->solveInPlace(adjB);
    Eigen::Map<matrix_vi>(variRefB_, M_, N_).adj() += adjB;
  }
};

/**
 * The vari for mdivide_left_ldlt(A, b) which handles the chain() call
 * for all elements of the result.  This vari follows the pattern
 * used in the other matrix operations where there is one "master"
 * vari whose value is never used and a large number of "slave" varis
 * whose chain() functions are never called because their adjoints are
 * set by the "master" vari.
 *
 * This class handles the var/double case.
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 */
template <int R1, int C1, int R2, int C2>
class mdivide_left_ldlt_vd_vari : public vari {
 public:
  int M_;  // A.rows() = A.cols() = B.rows()
  int N_;  // B.cols()
  vari **variRefC_;
  mdivide_left_ldlt_alloc<R1, C1, R2, C2> *alloc_;
  const LDLT_alloc<R1, C1> *alloc_ldlt_;

  mdivide_left_ldlt_vd_vari(const LDLT_factor<var, R1, C1> &A,
                            const Eigen::Matrix<double, R2, C2> &B)
      : vari(0.0),
        M_(A.rows()),
        N_(B.cols()),
        variRefC_(reinterpret_cast<vari **>(
            ChainableStack::instance_->memalloc_.alloc(sizeof(vari *) * B.rows()
                                                       * B.cols()))),
        alloc_(new mdivide_left_ldlt_alloc<R1, C1, R2, C2>()),
        alloc_ldlt_(A.alloc_) {
    alloc_->C_ = B;
    alloc_ldlt_->ldlt_.solveInPlace(alloc_->C_);
    Eigen::Map<matrix_vi>(variRefC_, M_, N_)
        = alloc_->C_.unaryExpr([](double x) { return new vari(x, false); });
  }

  virtual void chain() {
    matrix_d adjC = Eigen::Map<matrix_vi>(variRefC_, M_, N_).adj();

    const_cast<matrix_vi &>(alloc_ldlt_->variA_).adj()
        -= alloc_ldlt_->ldlt_.solve(adjC * alloc_->C_.transpose());
  }
};
}  // namespace internal

/**
 * Returns the solution of the system Ax=b given an LDLT_factor of A
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 *
 * @param A LDLT_factor
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if rows of b don't match the size of A.
 */
template <int R1, int C1, int R2, int C2>
inline Eigen::Matrix<var, R1, C2> mdivide_left_ldlt(
    const LDLT_factor<var, R1, C1> &A, const Eigen::Matrix<var, R2, C2> &b) {
  check_multiplicable("mdivide_left_ldlt", "A", A, "b", b);
  if (A.cols() == 0) {
    return {0, b.cols()};
  }

  internal::mdivide_left_ldlt_vv_vari<R1, C1, R2, C2> *baseVari
      = new internal::mdivide_left_ldlt_vv_vari<R1, C1, R2, C2>(A, b);

  Eigen::Matrix<var, R1, C2> res(b.rows(), b.cols());
  res.vi() = Eigen::Map<matrix_vi>(baseVari->variRefC_, res.rows(), res.cols());

  return res;
}

/**
 * Returns the solution of the system Ax=b given an LDLT_factor of A
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 *
 * @param A LDLT_factor
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if rows of b don't match the size of A.
 */
template <int R1, int C1, int R2, int C2>
inline Eigen::Matrix<var, R1, C2> mdivide_left_ldlt(
    const LDLT_factor<var, R1, C1> &A, const Eigen::Matrix<double, R2, C2> &b) {
  check_multiplicable("mdivide_left_ldlt", "A", A, "b", b);
  if (A.cols() == 0) {
    return {0, b.cols()};
  }

  internal::mdivide_left_ldlt_vd_vari<R1, C1, R2, C2> *baseVari
      = new internal::mdivide_left_ldlt_vd_vari<R1, C1, R2, C2>(A, b);

  Eigen::Matrix<var, R1, C2> res(b.rows(), b.cols());
  res.vi() = Eigen::Map<matrix_vi>(baseVari->variRefC_, res.rows(), res.cols());

  return res;
}

/**
 * Returns the solution of the system Ax=b given an LDLT_factor of A
 *
 * @tparam R1 number of rows in the LDLT_factor, can be Eigen::Dynamic
 * @tparam C1 number of columns in the LDLT_factor, can be Eigen::Dynamic
 * @tparam R2 number of rows in the right-hand side matrix, can be
 *         Eigen::Dynamic
 * @tparam C2 number of columns in the right-hand side matrix, can be
 *         Eigen::Dynamic
 *
 * @param A LDLT_factor
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if rows of b don't match the size of A.
 */
template <int R1, int C1, int R2, int C2>
inline Eigen::Matrix<var, R1, C2> mdivide_left_ldlt(
    const LDLT_factor<double, R1, C1> &A, const Eigen::Matrix<var, R2, C2> &b) {
  check_multiplicable("mdivide_left_ldlt", "A", A, "b", b);
  if (A.cols() == 0) {
    return {0, b.cols()};
  }

  internal::mdivide_left_ldlt_dv_vari<R1, C1, R2, C2> *baseVari
      = new internal::mdivide_left_ldlt_dv_vari<R1, C1, R2, C2>(A, b);

  Eigen::Matrix<var, R1, C2> res(b.rows(), b.cols());
  res.vi() = Eigen::Map<matrix_vi>(baseVari->variRefC_, res.rows(), res.cols());

  return res;
}

}  // namespace math
}  // namespace stan
#endif
<|endoftext|>


<commit_before>#include "../src/meshoptimizer.h"

#include <vector>

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

#ifdef __EMSCRIPTEN__
#include <emscripten.h>

double timestamp()
{
	return emscripten_get_now() * 1e-3;
}
#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 benchCodecs(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));
		}
	}
}

void benchFilters(size_t count)
{
	// note: the filters are branchless so we just run them on runs of zeroes
	size_t count4 = (count + 3) & ~3;
	std::vector<unsigned char> d4(count4 * 4);
	std::vector<unsigned char> d8(count4 * 8);

	printf("filters: oct8 data %d bytes, oct12/quat12 data %d bytes\n", int(d4.size()), int(d8.size()));

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

		meshopt_decodeFilterOct(&d4[0], count4, 4);

		double t1 = timestamp();

		meshopt_decodeFilterOct(&d8[0], count4, 8);

		double t2 = timestamp();

		meshopt_decodeFilterQuat(&d8[0], count4, 8);

		double t3 = timestamp();

		meshopt_decodeFilterExp(&d8[0], count4, 8);

		double t4 = timestamp();

		double GB = 1024 * 1024 * 1024;

		printf("filter: oct8 %.2f ms (%.2f GB/sec), oct12 %.2f ms (%.2f GB/sec), quat12 %.2f ms (%.2f GB/sec), exp %.2f ms (%.2f GB/sec)\n",
			(t1 - t0) * 1000, double(d4.size()) / GB / (t1 - t0),
			(t2 - t1) * 1000, double(d8.size()) / GB / (t2 - t1),
			(t3 - t2) * 1000, double(d8.size()) / GB / (t3 - t2),
			(t4 - t3) * 1000, double(d8.size()) / GB / (t4 - t3));
	}
}

int main()
{
	meshopt_encodeIndexVersion(1);

	const int N = 1000;

	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));
		}
	}

	benchCodecs(vertices, indices);
	benchFilters(8 * N * N);
}
<commit_msg>tools: Add Win32 timers to codecbench<commit_after>#include "../src/meshoptimizer.h"

#include <vector>

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

#ifdef __EMSCRIPTEN__
#include <emscripten.h>

double timestamp()
{
	return emscripten_get_now() * 1e-3;
}
#elif defined(_WIN32)
struct LARGE_INTEGER
{
	__int64 QuadPart;
};
extern "C" __declspec(dllimport) int __stdcall QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount);
extern "C" __declspec(dllimport) int __stdcall QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency);

double timestamp()
{
	LARGE_INTEGER freq, counter;
	QueryPerformanceFrequency(&freq);
	QueryPerformanceCounter(&counter);
	return double(counter.QuadPart) / double(freq.QuadPart);
}
#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 benchCodecs(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));
		}
	}
}

void benchFilters(size_t count)
{
	// note: the filters are branchless so we just run them on runs of zeroes
	size_t count4 = (count + 3) & ~3;
	std::vector<unsigned char> d4(count4 * 4);
	std::vector<unsigned char> d8(count4 * 8);

	printf("filters: oct8 data %d bytes, oct12/quat12 data %d bytes\n", int(d4.size()), int(d8.size()));

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

		meshopt_decodeFilterOct(&d4[0], count4, 4);

		double t1 = timestamp();

		meshopt_decodeFilterOct(&d8[0], count4, 8);

		double t2 = timestamp();

		meshopt_decodeFilterQuat(&d8[0], count4, 8);

		double t3 = timestamp();

		meshopt_decodeFilterExp(&d8[0], count4, 8);

		double t4 = timestamp();

		double GB = 1024 * 1024 * 1024;

		printf("filter: oct8 %.2f ms (%.2f GB/sec), oct12 %.2f ms (%.2f GB/sec), quat12 %.2f ms (%.2f GB/sec), exp %.2f ms (%.2f GB/sec)\n",
			(t1 - t0) * 1000, double(d4.size()) / GB / (t1 - t0),
			(t2 - t1) * 1000, double(d8.size()) / GB / (t2 - t1),
			(t3 - t2) * 1000, double(d8.size()) / GB / (t3 - t2),
			(t4 - t3) * 1000, double(d8.size()) / GB / (t4 - t3));
	}
}

int main()
{
	meshopt_encodeIndexVersion(1);

	const int N = 1000;

	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));
		}
	}

	benchCodecs(vertices, indices);
	benchFilters(8 * N * N);
}
<|endoftext|>