Merge branch 'lean'

progress
Avril 3 years ago
commit 07cd0c252d
Signed by: flanchan
GPG Key ID: 284488987C31F630

2
.gitignore vendored

@ -2,3 +2,5 @@
obj
build/
test/
shuffle3-*
profiling/

@ -17,6 +17,8 @@ RELEASE_LDFLAGS?= -O3 -flto
DEBUG_CFLAGS?= -g -O0
DEBUG_LDFLAGS?= -O0
STRIP=strip
OBJ = $(addprefix obj/,$(SRC:.c=.o))
.PHONY: release
@ -36,7 +38,7 @@ $(BUILD)/$(PROJECT)-release: CFLAGS+= $(RELEASE_CFLAGS)
$(BUILD)/$(PROJECT)-release: LDFLAGS+= $(RELEASE_LDFLAGS)
$(BUILD)/$(PROJECT)-release: $(OBJ)
$(CC) $^ $(CFLAGS) -o $@ $(LDFLAGS)
strip $@
$(STRIP) $@
$(BUILD)/$(PROJECT)-debug: CFLAGS+= $(DEBUG_CFLAGS)
$(BUILD)/$(PROJECT)-debug: LDFLAGS+= $(DEBUG_LDFLAGS)

@ -0,0 +1 @@
Complete rewrite using `mmap` et al. Maybe in Rust of C++ idk

@ -0,0 +1,73 @@
SRC_C = $(wildcard src/*.c)
SRC_CXX = $(wildcard src/*.cpp)
SRC_CXX+= $(wildcard src/rng/*.cpp)
INCLUDE = include
PROJECT=shuffle3
COMMON_FLAGS = -Wall -pedantic $(addprefix -I,$(INCLUDE)) -fno-strict-aliasing
OPT_FLAGS?= -march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \
-fno-stack-check
CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors
CFLAGS += $(COMMON_FLAGS) --std=gnu11
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20 -fno-exceptions
LDFLAGS += -lfmt
STRIP=strip
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS)
RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS)
RELEASE_LDFLAGS?= -O3 -flto
DEBUG_CFLAGS?= -O0 -g -DDEBUG
DEBUG_CXXFLAGS?= $(DEBUG_CFLAGS)
DEBUG_LDFLAGS?=
# Objects
OBJ_C = $(addprefix obj/c/,$(SRC_C:.c=.o))
OBJ_CXX = $(addprefix obj/cxx/,$(SRC_CXX:.cpp=.o))
OBJ = $(OBJ_C) $(OBJ_CXX)
# Phonies
.PHONY: release
release: | dirs $(PROJECT)-release
.PHONY: debug
debug: | dirs $(PROJECT)-debug
# Targets
dirs:
@mkdir -p obj/c{,xx}/src
@mkdir -p obj/cxx/src/rng
obj/c/%.o: %.c
$(CC) -c $< $(CFLAGS) -o $@ $(LDFLAGS)
obj/cxx/%.o: %.cpp
$(CXX) -c $< $(CXXFLAGS) -o $@ $(LDFLAGS)
$(PROJECT)-release: CFLAGS+= $(RELEASE_CFLAGS)
$(PROJECT)-release: CXXFLAGS += $(RELEASE_CXXFLAGS)
$(PROJECT)-release: LDFLAGS += $(RELEASE_LDFLAGS)
$(PROJECT)-release: $(OBJ)
$(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS)
$(STRIP) $@
$(PROJECT)-debug: CFLAGS+= $(DEBUG_CFLAGS)
$(PROJECT)-debug: CXXFLAGS += $(DEBUG_CXXFLAGS)
$(PROJECT)-debug: LDFLAGS += $(DEBUG_LDFLAGS)
$(PROJECT)-debug: $(OBJ)
$(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS)
clean:
rm -rf obj
rm -f $(PROJECT)-{release,debug}

@ -0,0 +1,35 @@
* shuffle3-lean
Redegisn/upgrade of =shuffle3=
* Goals
- [X] Functioning in-place shuffle/unshuffle
- [X] Shuffle
- [X] Unshuffle
- [X] Usable in-place s/us from command line
- [X] Shuffle
- [X] Unshuffle
- [ ] Functioning out-of-place/in-memory shuffle/unshuffle
- [ ] Shuffle
- [ ] Unshuffle
- [ ] Usable out-of-place s/us from command line
- [ ] Shuffle
- [ ] Unshuffle
** NO compatibility with =shuffle3=
=shuffle3='s ~drng~ PRNG algorithm uses an outdated global state backend. We don't want to reuse this.
As a result, output from =shuffle3= and =shuffle3-lean= is different.
* Improvements
- *~70-80x* speedup from shuffle3 1.0
- Huge reduction in syscalls
- Takes advantage of the kernel's fs cache
- Can properly handle large files without core dumping
- Doesn't dump huge amounts of trash onto each stack frame
* Todo
- [X] impl rng
- [X] impl shuffling
- [ ] impl out-of-place shuffling
- [-] arg parsing and dispatch
- [X] simple parsing
- [ ] complex parsing

@ -0,0 +1,28 @@
// https://stackoverflow.com/a/30304782
#ifndef _COLORS_
#define _COLORS_
/* FOREGROUND */
#define RST "\x1B[0m"
#define KRED "\x1B[31m"
#define KGRN "\x1B[32m"
#define KYEL "\x1B[33m"
#define KBLU "\x1B[34m"
#define KMAG "\x1B[35m"
#define KCYN "\x1B[36m"
#define KWHT "\x1B[37m"
#define FRED(x) KRED x RST
#define FGRN(x) KGRN x RST
#define FYEL(x) KYEL x RST
#define FBLU(x) KBLU x RST
#define FMAG(x) KMAG x RST
#define FCYN(x) KCYN x RST
#define FWHT(x) KWHT x RST
#define BOLD(x) "\x1B[1m" x RST
#define UNDL(x) "\x1B[4m" x RST
#endif /* _COLORS_ */

@ -0,0 +1,43 @@
#ifndef _DEBUG_H
#define _DEBUG_H
#ifdef __cplusplus
extern "C" {
#endif
struct debuginfo {
const char* file;
const char* function;
int line;
};
void _do_dprintf(struct debuginfo di, const char* fmt, ...);
#ifdef __cplusplus
extern "C++" {
#include <utility>
template<typename... Args>
inline void _real_dprintf(const char* file, const char* function, int line, const char* fmt, Args&&... args)
{
#ifdef DEBUG
debuginfo i = { file, function, line };
_do_dprintf(i, fmt, std::forward<Args>(args)...);
#endif
}
#define D_dprintf(fmt, ...) _real_dprintf(__FILE__, __func__, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__)
}
#else
#ifdef DEBUG
#define D_dprintf(fmt, ...) _do_dprintf( (struct debuginfo){.file = __FILE__, .function = __func__, .line = __LINE__}, fmt __VA_OPT__(,) __VA_ARGS__)
#else
static inline void _do__nothing(const char* fmt, ...) {}
#define D_dprintf(fmt, ...) _do__nothing(fmt __VA_OPT__(,) __VA_ARGS__) //(fmt __VA_OPT__(,) __VA_ARGS__, (void)0)
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif /* _DEBUG_H */

@ -0,0 +1,74 @@
#ifndef _MAP_H
#define _MAP_H
#ifdef __cplusplus
extern "C" {
#define restrict __restrict__
#endif
#include <stddef.h>
typedef struct mmap {
int fd;
void* ptr;
size_t len;
} mmap_t;
int open_and_map(const char* file, mmap_t* restrict ptr);
int unmap_and_close(mmap_t map);
typedef void* (*map_cb)(mmap_t map, void* user);
void* map_and_then(const char* file, map_cb callback, void* user);
#ifdef __cplusplus
}
#include <panic.h>
#include "reinterpret.h"
#include <cstdint>
namespace mm {
struct mmap {
inline static mmap_t create_raw(const char* file)
{
mmap_t map;
if (!open_and_map(file, &map)) panic("Failed to map file");
return map;
}
inline mmap(mmap_t raw) :inner(raw){}
inline mmap(const char* file)
: inner(create_raw(file)) {}
inline mmap(mmap&& move) : inner(move.inner)
{
auto other = const_cast<mmap_t*>(&move.inner);
other->ptr = nullptr;
}
inline mmap(const mmap& copt) = delete;
inline ~mmap()
{
if (inner.ptr) {
::unmap_and_close(inner);
}
}
inline const span<const unsigned char> as_span() const { return span(as_ptr(), size()); }
inline span<unsigned char> as_span() { return span(as_ptr(), size()); }
inline const std::uint8_t* as_ptr() const { return (const std::uint8_t*)inner.ptr; }
inline std::uint8_t* as_ptr() { return (std::uint8_t*)inner.ptr; }
inline std::size_t size() const { return inner.len; }
inline int as_fd() const { return inner.fd; }
inline const mmap_t& as_raw() const { return inner; }
private:
const mmap_t inner;
};
}
#undef restrict
#endif
#endif /* _MAP_H */

@ -0,0 +1,35 @@
#ifndef _PANIC_H
#define _PANIC_H
#ifdef __cplusplus
extern "C" {
#endif
struct panicinfo {
const char* file;
const char* function;
int line;
};
void _do_panic(struct panicinfo pi, const char* fmt, ...) __attribute__((noreturn));
#ifdef __cplusplus
extern "C++" {
#include <utility>
template<typename... Args>
__attribute__((noreturn)) inline void _real_panic(const char* file, const char* function, int line, const char* fmt, Args&&... args)
{
panicinfo i = { file, function, line };
_do_panic(i, fmt, std::forward<Args>(args)...);
}
#define panic(fmt, ...) _real_panic(__FILE__, __func__, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__)
}
#else
#define panic(fmt, ...) _do_panic( (struct panicinfo){.file = __FILE__, .function = __func__, .line = __LINE__}, fmt __VA_OPT__(,) __VA_ARGS__)
#endif
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1 @@
reinterpret.hpp

@ -0,0 +1,61 @@
#ifndef _REINTERPRET_H
#define _REINTERPRET_H
#include <panic.h>
#ifdef __cplusplus
#define restrict __restrict__
#include <cstddef>
#include <cstdint>
#else
#include <stddef.h>
#include <stdint.h>
#endif
#ifdef __cplusplus
template<typename T>
struct span {
inline span(T* ptr, std::size_t len) : ptr(ptr), len(len) {}
template<typename U>
inline span<U> reinterpret() const
{
auto bytes = size_bytes();
//if (len_b % sizeof(U) != 0) panic("Cannot reinterpret T to U due to unmatch sizing constraints.");
return span<U>((U*)ptr, bytes / sizeof(U));
}
inline const T& operator[](std::size_t i) const
{
if (i >= len) panic("Out of bounds access: %lu >= %lu", i, len);
return ptr[i];
}
inline T& operator[](std::size_t i)
{
if (i >= len) panic("Out of bounds access: %lu >= %lu", i, len);
return ptr[i];
}
inline const T* as_ptr() const { return ptr; }
inline T* as_ptr() { return ptr; }
inline const T* operator&() const { return as_ptr(); }
inline T* operator&() { return as_ptr(); }
inline std::size_t size_bytes() const { return len * sizeof(T); }
inline std::size_t size() const { return len; }
private:
T* const ptr;
const std::size_t len;
};
extern "C" {
#endif
uint64_t* bytes_to_long(uint8_t* ptr, size_t ptr_sz, size_t* restrict nsize);
float* bytes_to_float(uint8_t* ptr, size_t ptr_sz, size_t* restrict nsize);
#ifdef __cplusplus
}
#endif
#endif /* _REINTERPRET_H */

@ -0,0 +1,59 @@
#ifndef _RNG_H
#define _RNG_H
#include "shuffle3.h"
#ifdef __cplusplus
extern "C" {
#endif
enum rng_kind {
RNG_KIND_FRNG,
RNG_KIND_DRNG,
RNG_KIND_XORNG,
};
typedef struct rng_init
{
enum rng_kind kind;
union {
struct {
double state[2];
} frng;
struct {
int32_t state;
} drng;
struct {
uint64_t state[2];
} xorng;
} init;
} rng_init_opt;
typedef struct rng_impl* _UNIQUE rng_t;
rng_t rng_new(rng_init_opt kind);
#define RNG_INIT(_kind,...) ((rng_init_opt){.kind=(_kind), .init.__VA_ARGS__ })
void rng_free(rng_t ptr);
// Tests
extern void rng_test();
extern void rng_test_spec(rng_t rng);
#ifdef __cplusplus
}
// RNG interfaces
#include <rng/frng.hpp>
#include <rng/drng.hpp>
#include <rng/xoroshiro128plus.hpp>
namespace rng {
void test_algo(RNG&& rng);
template<class R,
typename std::enable_if<std::is_base_of<RNG, R>::value>::value __fuck>
inline void test_algo(R&& rng) { test_algo(static_cast<RNG&&>(rng)); }
}
#endif
#endif /* _RNG_H */

@ -0,0 +1,22 @@
#include "impl.hpp"
#include <debug.h>
namespace rng
{
struct drng : public RNG
{
inline drng(std::uint32_t seed) : state(seed){
D_dprintf("drng: seeded with %u", seed);
//dprintf(" dummy run sample: %f", sample());
}
inline drng() : drng(1){}
static drng from_time();
int rand();
protected:
double sample() override;
private:
std::uint32_t state;
};
}

@ -0,0 +1,66 @@
#include "impl.hpp"
#include <cmath>
#include <debug.h>
namespace rng
{
struct frng : public RNG
{
template<std::size_t N>
static constexpr inline double dot(const std::array<double, N>& v, const std::array<double, N>& u)
{
double res=0;
for(std::size_t i=0;i<N;i++)
{
res += v[i] * u[i];
}
return res;
}
static inline constexpr double fract(double x)
{
return x - floor(x);
}
static inline constexpr double sample_double(const std::array<double, 2>& state)
{
const constexpr std::array<double, 2> vec2 = { 12.9898, 78.223 };
return fract(sin(dot(state, vec2)) * 43758.5453);
}
#define P D_dprintf("frng: seeded with (%f, %f)", state[0], state[1]);
inline constexpr frng(double s1, double s2) : state({s1, s2}){P}
inline constexpr frng(const std::array<double, 2>& ar) : state(ar){P}
inline constexpr frng(std::array<double, 2>&& ar) : state(ar){P}
inline constexpr frng(const double (&ar)[2]) : state({ar[0], ar[1]}) {P}
#undef P
inline constexpr double next_double() override { return sample(); }
inline constexpr float next_float() override { return (float)sample(); }
protected:
inline constexpr double sample() override
{
double res = sample_double(state);
update_state(state, res);
return res;
}
private:
std::array<double, 2> state;
static inline constexpr void update_state(std::array<double, 2>& state, double r)
{
float v1 = (float)state[0];
float v2 = (float)state[1];
std::array<double, 2> nvec = {
r,
(double)v2,
};
state[0] = sample_double(nvec);
nvec[1] = (double)v1;
state[1] = sample_double(nvec);
}
};
}

@ -0,0 +1,35 @@
#pragma once
#include <array>
#include <vector>
#include <cstdint>
/// Base class for RNG impls
struct RNG {
virtual unsigned char byte();
virtual void bytes(unsigned char* ptr, std::size_t len);
template<std::size_t N>
inline void bytes(unsigned char (&arr)[N]) { return bytes(arr, N); }
template<std::size_t N>
inline void bytes(std::array<unsigned char, N>& array) { return bytes(&array[0], N); }
inline void bytes(std::vector<unsigned char>& vec) { return bytes(&vec[0], vec.size()); }
bool chance();
virtual bool chance(double chance);
virtual std::int32_t next_int();
inline std::int32_t next_int(std::int32_t max) { return next_int(0, max); }
std::int32_t next_int(std::int32_t min, std::int32_t max);
virtual std::int64_t next_long();
inline std::int64_t next_long(std::int64_t max) { return next_long(0, max); }
std::int64_t next_long(std::int64_t min, std::int64_t max);
inline virtual float next_float() { return (float)sample(); }
inline virtual double next_double() { return sample(); }
virtual ~RNG() = default;
protected:
virtual double sample() = 0;
};

@ -0,0 +1,29 @@
#pragma once
#include "impl.hpp"
#include <debug.h>
namespace rng
{
struct xoroshiro128plus : public RNG
{
using State = std::array<std::uint64_t, 2>;
#define P D_dprintf("xorng: seeded with (%lu, %lu)", state[0], state[1]);
inline constexpr xoroshiro128plus(std::uint64_t s0, std::uint64_t s1) : state({s0, s1}){P}
inline constexpr xoroshiro128plus(std::array<std::uint64_t, 2>&& ar) : state(ar){P}
inline constexpr xoroshiro128plus(const std::array<std::uint64_t, 2>& ar) : state(ar){P}
inline constexpr xoroshiro128plus(const std::uint64_t (&ar)[2]) : state({ar[0], ar[1]}){P}
#undef P
std::uint64_t next_ulong();
using RNG::next_long;
std::int64_t next_long() override;
void jump();
void long_jump();
protected:
double sample() override;
private:
State state;
};
}

@ -0,0 +1,38 @@
#pragma once
#include <rng.h>
#include <reinterpret.h>
#include <algorithm>
#include <fmt/format.h>
namespace rng {
template<typename T, typename R>
inline void shuffle(R& rng, span<T> span)
{
if(!span.size()) return;
fmt::print(" -> shuffling {} objects...", span.size());
for(std::size_t i=span.size()-1;i>0;i--)
{
auto j = rng.next_long(i);
std::swap(span[i], span[j]);
}
fmt::print(" OK\n");
}
template<typename T, typename R>
inline void unshuffle(R& rng, span<T> span)
{
if(!span.size()) return;
std::vector<std::size_t> rng_values(span.size());
fmt::print(" -> unshuffling {} objects...", span.size());
for(std::size_t i=span.size()-1;i>0;i--)
rng_values.push_back(rng.next_long(i));
for(std::size_t i=1;i<span.size();i++) {
std::swap(span[i], span[rng_values.back()]);
rng_values.pop_back();
}
fmt::print(" OK\n");
}
}

@ -0,0 +1,30 @@
#ifndef _SHUFFLE3_H
#define _SHUFFLE3_H
#ifdef __cplusplus
#define _UNIQUE __restrict
extern "C" {
#else
#define _UNIQUE restrict
#endif
#ifdef DEBUG
#define _FORCE_INLINE __attribute__((gnu_inline)) static inline
#else
#define _FORCE_INLINE __attribute__((gnu_inline)) extern inline
#endif
/*
#ifdef DEBUG
#define dprintf(fmt, ...) printf("[dbg @" __FILE__ "->%s:%d] " fmt "\n", __func__, __LINE__ __VA_OPT__(,) __VA_ARGS__)
#else
#define dprintf(fmt, ...)
#endif
*/
extern const char* _prog_name;
#ifdef __cplusplus
}
#endif
#endif /* _SHUFFLE3_H */

@ -0,0 +1,52 @@
#ifndef _WORK_H
#define _WORK_H
#ifdef __cplusplus
extern "C" {
#endif
enum work_buffer_opts {
WORK_BO_CPIP=0, // Copy then run in place on output file (default)
WORK_BO_BUFFERED=1, // Read whole input into memory then perform
};
typedef struct _work_args {
enum {
OP_SHUFFLE_IP, // Shuffle in place
OP_SHUFFLE_OP, // Shuffle out of place
OP_UNSHUFFLE_IP, // Unshuffle in place
OP_UNSHUFFLE_OP, // Unshuffle out of place
OP_HELP, // Print help then exit
} op;
union {
struct {
const char* file;
} op_shuffle_ip;
struct {
enum work_buffer_opts buffered;
const char* ifile;
const char* ofile;
} op_shuffle_op;
struct {
const char* file;
} op_unshuffle_ip;
struct {
enum work_buffer_opts buffered;
const char* ifile;
const char* ofile;
} op_unshuffle_op;
} data;
} work_args_t;
int do_work(const work_args_t args);
#ifdef __cplusplus
}
#endif
#endif /* _WORK_H */

@ -0,0 +1,19 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <debug.h>
#include <colours.h>
void _do_dprintf(struct debuginfo info, const char* fmt, ...)
{
#ifdef DEBUG
va_list li;
va_start(li, fmt);
fprintf(stderr, "["FGRN("dbg")" " FYEL("@") BOLD("%s") "->" FWHT("%s") ":" FYEL("%d") "]: ", info.file, info.function,info.line);
vfprintf(stderr, fmt, li);
fprintf(stderr, "\n");
va_end(li);
#endif
}

@ -0,0 +1,78 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <shuffle3.h>
#include <panic.h>
#include <reinterpret.h>
#include <map.h>
#include <rng.h>
#include <work.h>
#include <debug.h>
#define noreturn __attribute__((noreturn)) void
_Static_assert(sizeof(float)==sizeof(uint32_t), "float is not 32 bits");
const char* _prog_name;
noreturn help_then_exit()
{
fprintf(stderr, "Try passing `-h`\n");
exit(1);
}
void usage()
{
printf( "shuffle3 - 3 pass binary shuffler\n"
"Usage: %s -s <file>\n"
"Usage: %s -u <file>\n", _prog_name, _prog_name);
printf("\nOPTIONS:\n"
" -s\tShuffle file in place\n"
" -u\tUnshuffle file in place\n");
}
int main(int argc, char** argv)
{
_prog_name = argv[0];
work_args_t parsed;
if( !argv[1] || *(argv[1]) != '-') help_then_exit();
D_dprintf("Parsing `%c'", argv[1][1]);
switch(argv[1][1])
{
case 's':
parsed.op = OP_SHUFFLE_IP;
if(!(parsed.data.op_shuffle_ip.file = argv[2]))
{
fprintf(stderr, "Error: -s expected file argument.\n");
return 1;
}
D_dprintf("parsed.op = %d", OP_SHUFFLE_IP);
break;
case 'u':
parsed.op = OP_UNSHUFFLE_IP;
if(!(parsed.data.op_unshuffle_ip.file = argv[2]))
{
fprintf(stderr, "Error: -u expected file argument.\n");
return 1;
}
D_dprintf("parsed.op = %d", OP_UNSHUFFLE_IP);
break;
case 'h':
usage();
return 0;
default:
fprintf(stderr, "Error: unknown argument `%s'\n\n", argv[1]);
help_then_exit();
panic("Unreachable");
}
return do_work(parsed);
}

@ -0,0 +1,56 @@
#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<sys/mman.h>
#include<fcntl.h>
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH
#include <map.h>
int open_and_map(const char* file, mmap_t* restrict ptr)
{
int fd;
struct stat st;
if ((fd = open(file, O_RDWR, FILEMODE)) < 0) {
perror("Failed to open file");
return 0;
}
if (fstat(fd, &st) < 0) {
perror("Failed to stat file");
close(fd);
return 0;
}
register struct mmap map = { .fd = fd, .ptr = NULL, .len = st.st_size };
if ((map.ptr = mmap(NULL, map.len, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0)) == MAP_FAILED) {
perror("mmap() failed");
close(fd);
return 0;
}
*ptr = map;
return 1;
}
int unmap_and_close(mmap_t map)
{
register int rval=1;
if (munmap(map.ptr, map.len) < 0) {
perror("munmap() failed");
rval=0;
}
if (close(map.fd) <0) {
perror("Failed to close fd");
rval=0;
}
return rval;
}

@ -0,0 +1,11 @@
#include <shuffle3.h>
#include <reinterpret.hpp>
#include <map.h>
extern "C" void* map_and_then(const char* file, map_cb callback, void* user)
{
mm::mmap map(file);
return callback(map.as_raw(), user);
}

@ -0,0 +1,18 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <panic.h>
#include <colours.h>
__attribute__((noreturn)) void _do_panic(struct panicinfo info, const char* fmt, ...)
{
va_list li;
va_start(li, fmt);
fprintf(stderr, BOLD(UNDL(FRED("[!]"))) " (" BOLD("%s") "->" BOLD(FRED("%s")) ":" FYEL("%d") ") " BOLD(FRED("fatal error")) ": ", info.file, info.function, info.line);
vfprintf(stderr, fmt, li);
fprintf(stderr, "\n");
va_end(li);
abort();
}

@ -0,0 +1,22 @@
#include <shuffle3.h>
#include <reinterpret.hpp>
template<typename T>
static inline T* bytes_to_t(std::uint8_t* ptr, std::size_t ptr_sz, std::size_t* restrict nsize)
{
span<uint8_t> bytes(ptr, ptr_sz);
auto tout = bytes.reinterpret<T>();
*nsize = tout.size();
return tout.as_ptr();
}
extern "C" {
uint64_t* bytes_to_long(uint8_t* ptr, size_t ptr_sz, size_t* restrict nsize)
{
return bytes_to_t<uint64_t>(ptr, ptr_sz, nsize);
}
float* bytes_to_float(uint8_t* ptr, size_t ptr_sz, size_t* restrict nsize)
{
return bytes_to_t<float>(ptr, ptr_sz, nsize);
}
}

@ -0,0 +1,92 @@
#include <iostream>
#include <cmath>
#include <rng/impl.hpp>
inline unsigned char RNG::byte()
{
return (unsigned char)next_int(255);
}
void RNG::bytes(unsigned char* ptr, std::size_t len)
{
for(std::size_t i=0;i<len;i++)
{
ptr[i] = byte();
}
}
bool RNG::chance()
{
return chance(.5);
}
bool RNG::chance(double d)
{
if (d<=0) return false;
return sample() <= d;
}
std::int32_t RNG::next_int(std::int32_t min, std::int32_t max)
{
return (std::int32_t)floor(sample() * (double)(max-min))+min;
}
std::int32_t RNG::next_int()
{
return (chance() ? 1 : -1) * (std::int32_t)floor(sample() * (double)INT32_MAX);
}
std::int64_t RNG::next_long(std::int64_t min, std::int64_t max)
{
return (std::int64_t)floor(sample() * (double)(max-min))+min;
}
std::int64_t RNG::next_long()
{
return (chance() ? 1l : -1l) * (std::int64_t)floor(sample() * (double)INT64_MAX);
}
#include <rng.h>
#include <panic.h>
namespace { // C interface
using namespace std;
#define extract_ptr(ptr) ((RNG*)(ptr))
static inline RNG& extract_ref(rng_t rng)
{
return *extract_ptr(rng);
}
template<typename T>
static inline T* extract_downcast_ptr(rng_t rng)
{
return dynamic_cast<T*>(extract_ptr(rng));
}
extern "C"
{
rng_t rng_new(rng_init_opt opt)
{
switch(opt.kind)
{
case RNG_KIND_FRNG: return (rng_t) new rng::frng(opt.init.frng.state);
case RNG_KIND_DRNG: return (rng_t) new rng::drng(opt.init.drng.state);
case RNG_KIND_XORNG: return (rng_t) new rng::xoroshiro128plus(opt.init.xorng.state);
default: panic("Unknown RNG init opt: %d", opt.kind);
}
return nullptr;
}
void rng_free(rng_t rng)
{
RNG* ptr = (RNG*)rng;
delete ptr;
}
void rng_test_spec(rng_t rng)
{
cout << "rng_test_spec:" << endl;
rng::test_algo(std::move(extract_ref(rng)));
}
}
}

@ -0,0 +1,21 @@
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <rng/drng.hpp>
namespace rng
{
drng drng::from_time() { return drng(time(NULL)); }
int drng::rand()
{
return rand_r(&state);
}
double drng::sample()
{
int val = rand_r(&state);
return (double)val / (double)RAND_MAX;
}
}

@ -0,0 +1,3 @@
#include <rng/impl.hpp>
#include <rng/frng.hpp>
#include <iostream>

@ -0,0 +1,37 @@
#include <iostream>
#include <rng.h>
namespace rng {
void test_algo(RNG&& rng)
{
using namespace std;
for(int i=0;i<10;i++) {
double d = rng.next_double();
long l = rng.next_long(-10, 10);
std::array<bool, 10> ar;
for(auto& i : ar) i = rng.chance();
cout << "\t(Sampled: " << d;
cout << ", Long: " << l;
cout << ", Bools: [ ";
for(const auto& i : ar) cout << i << " ";
cout << "])" << endl;
}
}
}
extern "C" void rng_test()
{
using namespace std;
cout << "frng:" << endl;
rng::test_algo(rng::frng(1.0, 1.2));
cout << "drng:" << endl;
rng::test_algo(rng::drng(10));
cout << "xoroshiro128+:" << endl;
rng::test_algo(rng::xoroshiro128plus(100ul, 200ul));
}

@ -0,0 +1,86 @@
#include <rng/xoroshiro128plus.hpp>
#include <iostream>
using u64 = std::uint64_t;
#define XO xoroshiro128plus
static inline constexpr u64 rotl(u64 x, int k)
{
return (x << k) | (x >> (64 - k));
}
namespace rng
{
inline constexpr u64 next(XO::State& s)
{
u64 s0 = s[0];
u64 s1 = s[1];
u64 result = s0 + s1;
s1 ^= s0;
s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16);
s[1] = rotl(s1, 37);
return result;
}
inline constexpr void xo_jump(XO::State& s)
{
constexpr const std::uint64_t JUMP[] = { 0xdf900294d8f554a5, 0x170865df4b3201fc };
std::uint64_t s0 = 0;
std::uint64_t s1 = 0;
for(u64 i = 0; i < sizeof JUMP / sizeof *JUMP; i++)
for(int b = 0; b < 64; b++) {
if (JUMP[i] & UINT64_C(1) << b) {
s0 ^= s[0];
s1 ^= s[1];
}
next(s);
}
s[0] = s0;
s[1] = s1;
}
inline constexpr void xo_long_jump(XO::State& s)
{
constexpr const uint64_t LONG_JUMP[] = { 0xd2a98b26625eee7b, 0xdddf9b1090aa7ac1 };
std::uint64_t s0 = 0;
std::uint64_t s1 = 0;
for(u64 i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++)
for(int b = 0; b < 64; b++) {
if (LONG_JUMP[i] & UINT64_C(1) << b) {
s0 ^= s[0];
s1 ^= s[1];
}
next(s);
}
s[0] = s0;
s[1] = s1;
}
void XO::jump() { xo_jump(state); }
void XO::long_jump() { xo_long_jump(state); }
std::uint64_t XO::next_ulong()
{
return next(state);
}
std:: int64_t XO::next_long()
{
const u64 v = next_ulong();
static_assert(sizeof(v) == sizeof(decltype(next_long())));
return *(const std::int64_t*)&v;
}
double XO::sample()
{
return (next(state) & ((INT64_C(1) << 53) - 1)) * (1.00 / (INT64_C(1) << 53));
}
}

@ -0,0 +1,129 @@
#include <tuple>
#include <functional>
#include <cfloat>
#include <fmt/format.h>
#include <shuffle3.h>
#include <panic.h>
#include <map.h>
#include <reinterpret.h>
#include <shuffle.hpp>
#include <work.h>
#include <debug.h>
template<typename T, typename Fn>
std::tuple<T, T> minmax_t(const span<T>& array, Fn keep)
{
T highest;
T lowest;
bool init=false;
D_dprintf("minmax_t: %p (%lu)", array.as_ptr(), array.size());
for(std::size_t i=0;i<array.size();i++)
{
if(!keep(array[i])) continue;
auto item = array[i];
if(!init) {
init = true;
lowest = highest = item;
}
else if(item < lowest) lowest = item;
else if(item > highest) highest = item;
}
//fmt::print("MMX {}, {}\n", lowest, highest);
return {lowest, highest};
}
template<typename T>
inline std::tuple<T, T> minmax_t(const span<T>& array)
{
return minmax_t(array, [](T _val) { return true; });
}
namespace work
{
/// Shuffle or unshuffle in place
template<bool unshuffle>
int xshuffle_ip(const char* file)
{
mm::mmap map(file);
if constexpr(unshuffle)
{
auto [byte_l, byte_h] = minmax_t(map.as_span().reinterpret<std::int8_t>());
D_dprintf("MMX res (s8): %d -- %d", byte_l, byte_h);
rng::drng drng((std::int32_t) ((0xfffa << 16) | (byte_l<<7) | byte_h ));
rng::unshuffle(drng, map.as_span());
auto [float_l, float_h] = minmax_t(map.as_span().reinterpret<float>(), [](float f) -> bool { return !( (f!=f) || f < -FLT_MAX || f > FLT_MAX); });
D_dprintf("MMX res (f32): %f -- %f", float_l, float_h);
rng::frng frng(float_l, float_h);
rng::unshuffle(frng, map.as_span().reinterpret<float>());
auto [long_l, long_h] = minmax_t(map.as_span().reinterpret<std::int64_t>());
D_dprintf("MMX res (u64): %ld -- %ld", long_l, long_h);
rng::xoroshiro128plus xorng(*(const std::uint64_t*)&long_l, *(const std::uint64_t*)&long_h);
rng::unshuffle(xorng, map.as_span().reinterpret<std::int64_t>());
} else {
auto [long_l, long_h] = minmax_t(map.as_span().reinterpret<std::int64_t>());
D_dprintf("MMX res (u64): %ld -- %ld", long_l, long_h);
rng::xoroshiro128plus xorng(*(const std::uint64_t*)&long_l, *(const std::uint64_t*)&long_h);
rng::shuffle(xorng, map.as_span().reinterpret<std::int64_t>());
auto [float_l, float_h] = minmax_t(map.as_span().reinterpret<float>(), [](float f) -> bool { return !( (f!=f) || f < -FLT_MAX || f > FLT_MAX); });
D_dprintf("MMX res (f32): %f -- %f", float_l, float_h);
rng::frng frng(float_l, float_h);
rng::shuffle(frng, map.as_span().reinterpret<float>());
auto [byte_l, byte_h] = minmax_t(map.as_span().reinterpret<std::int8_t>());
D_dprintf("MMX res (s8): %d -- %d", byte_l, byte_h);
rng::drng drng((std::int32_t) ((0xfffa << 16) | (byte_l<<7) | byte_h ));
rng::shuffle(drng, map.as_span());
}
return 0;
}
/// Shuffle or unshuffle out of place
template<bool unshuffle>
int xshuffle_op(const char* ifile, const char* ofile, bool is_buffered)
{
if constexpr(unshuffle)
{
} else {
}
panic("Unimplemented");
return 0;
}
}
int help()
{
//Print help then exit
return 1;
}
extern "C" int do_work(const work_args_t args)
{
using A = decltype(args.op);
switch (args.op) {
case A::OP_SHUFFLE_IP: return work::xshuffle_ip<false >(args.data.op_shuffle_ip.file);
case A::OP_SHUFFLE_OP: return work::xshuffle_op<false >(args.data.op_shuffle_op.ifile,
args.data.op_shuffle_op.ofile,
args.data.op_shuffle_op.buffered == WORK_BO_BUFFERED);
case A::OP_UNSHUFFLE_IP: return work::xshuffle_ip<true >(args.data.op_unshuffle_ip.file);
case A::OP_UNSHUFFLE_OP: return work::xshuffle_op<true>(args.data.op_unshuffle_op.ifile,
args.data.op_unshuffle_op.ofile,
args.data.op_unshuffle_op.buffered == WORK_BO_BUFFERED);
case A::OP_HELP: return help();
default: panic("Unknown op %d", (int)args.op);
}
return 0;
}
Loading…
Cancel
Save