Compare commits

...

36 Commits

Author SHA1 Message Date
Avril 2458a2b969
Added skeleton for new RNG type: Lorenz Attractor.
1 year ago
Avril 295da7df02
Started branch "progress", for working on timed-out progress indicator. Current build succeeds but does not use progress indicators at all.
1 year ago
Avril dd54a4823d
Started progress bar
1 year ago
Avril 0a0fb4555a
Improved test.sh
1 year ago
Avril ef7f06e8e0
Makefile: Added better control over used intrinsics and target arch
1 year ago
Avril 16ae82f05d
Changed `debug` target: Now uses -Og instead of -O0.
1 year ago
Avril 454378acfe
Added prototype for `unmap_and_close_s()` for non-inlining?
1 year ago
Avril ecf72a7526
Improved PGO profiling
1 year ago
Avril 3e3b5d1cf6
Fixed issue with `constexpr` debug statements.
1 year ago
Avril f648646317
fix make failing on clean builds
3 years ago
Avril b882bfa8ea
added _FS_SPILL_BUFFER=MAP
3 years ago
Avril 9270fe49ab
lazy init of spill buffer
3 years ago
Avril d1c5484ada
memusage on arm
3 years ago
Avril fb2ecf7bec
allocation bugfix
3 years ago
Avril ed9c43e910
update makefile
3 years ago
Avril f955b8f375
update makefile
3 years ago
Avril 784d148e50
spill
3 years ago
Avril 2382c6103d
spill
3 years ago
Avril e6da741a3b
update readme
3 years ago
Avril a89f6ed47e
added pgo target
3 years ago
Avril 6b2b9a4a9d
_FS_SPILL_BUFFER
3 years ago
Avril 7d34ed2496
spilling start
3 years ago
Avril 4a7d93f24b
update makefile
3 years ago
Avril d05e0b46a0
whoops
3 years ago
Avril 6dfe7e59dc
added incomplete feature _FS_SPILL_BUFFER
3 years ago
Avril dd15ad3295
fsvec
3 years ago
Avril eab4b33a9a
fsvec impl start
3 years ago
Avril 360dc8a069
improve test script and makefile
3 years ago
Avril 66cac674a9
remove dependancy on libfmt
3 years ago
Avril c5bce05fe3
update README
3 years ago
Avril 5ce3e4f9aa
update README
3 years ago
Avril 72026ccff1
update README
3 years ago
Avril 128adae14d
update README
3 years ago
Avril 130016eaee
update README
3 years ago
Avril aaedf1d585
update TODO
3 years ago
Avril 0d45fa7b94
added test target
3 years ago

1
.gitignore vendored

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

@ -6,25 +6,51 @@ INCLUDE = include
PROJECT=shuffle3
COMMON_FLAGS = -Wall -pedantic $(addprefix -I,$(INCLUDE)) -fno-strict-aliasing
# Link to these libraries dynamicalls
SHARED_LIBS=fmt
# Link to these libraries statically
STATIC_LIBS=
OPT_FLAGS?= -march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
# Currently supported:
# _FS_SPILL_BUFFER: Use file backed buffer instead of memory backed one for unshuffling. See `shuffle3.h`.
# Setting the value to `DYN` enables the dynamic buffer, setting it to `MAP` enabled memory-mapped buffer. `MAP` is usually the fastest of the 3 modes.
# DEBUG: Pretend we're building a debug release even though we're not. Will enable additional output messages and may interfere with some optimisations
FEATURE_FLAGS?=
COMMON_FLAGS+= $(addprefix -D,$(FEATURE_FLAGS)) -Wall -Wstrict-aliasing $(addprefix -I,$(INCLUDE)) -fno-strict-aliasing
ARCH?=native
INTRINSICS=avx sse3
OPT_FLAGS?=-fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \
-fno-stack-check
OPT_FLAGS+=$(addprefix -m,$(INTRINSICS))
ifneq ($(ARCH),)
OPT_FLAGS+=$(addprefix -march=,$(ARCH))
endif
CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors
CFLAGS += $(COMMON_FLAGS) --std=gnu11
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++20 -fno-exceptions
LDFLAGS += -lfmt
CXXFLAGS += $(COMMON_FLAGS) --std=gnu++23 -fno-exceptions
# XXX: We might need exceptions soon, for OOP usage, because we try multiple approaches from most efficient to least.
LDFLAGS += $(addsuffix .a,$(addprefix -l:lib,$(STATIC_LIBS))) $(addprefix -l,$(SHARED_LIBS))
STRIP=strip
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS)
RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS)
RELEASE_LDFLAGS?= -O3 -flto
RELEASE_COMMON_FLAGS?= -DNOTRACE
RELEASE_COMMON_FLAGS+= -DRELEASE
RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS) $(RELEASE_COMMON_FLAGS)
RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS) $(RELEASE_COMMON_FLAGS)
RELEASE_LDFLAGS?= -Wl,-O3 -Wl,-flto
DEBUG_CFLAGS?= -O0 -g -DDEBUG
DEBUG_CFLAGS?= -Og -g -DDEBUG
DEBUG_CXXFLAGS?= $(DEBUG_CFLAGS)
DEBUG_LDFLAGS?=
@ -34,6 +60,20 @@ OBJ_C = $(addprefix obj/c/,$(SRC_C:.c=.o))
OBJ_CXX = $(addprefix obj/cxx/,$(SRC_CXX:.cpp=.o))
OBJ = $(OBJ_C) $(OBJ_CXX)
# PGO stuff
PGO_OBJ_C = $(addprefix obj/pgo/c/,$(SRC_C:.c=.o))
PGO_OBJ_CXX = $(addprefix obj/pgo/cxx/,$(SRC_CXX:.cpp=.o))
PGO_OBJ = $(PGO_OBJ_C) $(PGO_OBJ_CXX)
PGO_ITERATIONS=5
PGO_BLOCKS={1..4}
PGO_SIZE={1024..4096}
PGO_SET_LOC?=/tmp/$(PROJECT)-pgo
PGO_FLAGS = -fprofile-generate
PGO_LDFLAGS=
# Phonies
.PHONY: release
@ -42,11 +82,16 @@ release: | dirs $(PROJECT)-release
.PHONY: debug
debug: | dirs $(PROJECT)-debug
.PHONY: pgo
pgo: | dirs $(PROJECT)-pgo
.PHONY: test
test: test-all
# Targets
dirs:
@mkdir -p obj/c{,xx}/src
@mkdir -p obj/cxx/src/rng
@mkdir -p obj/{pgo/,}c{,xx}/src{,/rng}
obj/c/%.o: %.c
$(CC) -c $< $(CFLAGS) -o $@ $(LDFLAGS)
@ -54,6 +99,12 @@ obj/c/%.o: %.c
obj/cxx/%.o: %.cpp
$(CXX) -c $< $(CXXFLAGS) -o $@ $(LDFLAGS)
obj/pgo/c/%.o: %.c
$(CC) -c $< $(CFLAGS) $(PGO_FLAGS) -o $@ $(LDFLAGS) $(PGO_LDFLAGS)
obj/pgo/cxx/%.o: %.cpp
$(CXX) -c $< $(CXXFLAGS) $(PGO_FLAGS) -o $@ $(LDFLAGS) $(PGO_LDFLAGS)
$(PROJECT)-release: CFLAGS+= $(RELEASE_CFLAGS)
$(PROJECT)-release: CXXFLAGS += $(RELEASE_CXXFLAGS)
@ -68,6 +119,59 @@ $(PROJECT)-debug: LDFLAGS += $(DEBUG_LDFLAGS)
$(PROJECT)-debug: $(OBJ)
$(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS)
clean:
pgo-generate: CFLAGS+= $(RELEASE_CFLAGS)
pgo-generate: CXXFLAGS += $(RELEASE_CXXFLAGS)
pgo-generate: LDFLAGS += $(RELEASE_LDFLAGS)
pgo-generate: $(PGO_OBJ)
$(CXX) $^ $(CXXFLAGS) $(PGO_FLAGS) -o $@ $(LDFLAGS) $(PGO_LDFLAGS)
pgo-reset:
-find ./obj/pgo -name \*.gcda -exec rm {} +
pgo-profile: | pgo-reset pgo-generate
mkdir -p $(PGO_SET_LOC)
for i in {1..$(PGO_ITERATIONS)}; do \
block=$$(rng --of $(PGO_SIZE)); \
block_count=$$(rng --of $(PGO_BLOCKS)); \
dd if=/dev/urandom of=$(PGO_SET_LOC)/small bs=$$block count=$$block_count >> /dev/null 2>&1; \
printf "Iteration $$i / $(PGO_ITERATIONS) ($$block * $$block_count)\r"; \
( echo ">> $$i" >&2; \
echo ">> $$i"; \
./pgo-generate -s $(PGO_SET_LOC)/small && \
./pgo-generate -u $(PGO_SET_LOC)/small && \
./pgo-generate -h && \
FCNT=1 ./test.sh ./pgo-generate && \
FCNT=2 ./test.sh ./pgo-generate && \
FCNT=3 ./test.sh ./pgo-generate && \
FCNT=4 ./test.sh ./pgo-generate && \
: \
) >>$(PGO_SET_LOC)/stdout.log 2>>$(PGO_SET_LOC)/stderr.log || \exit $$?; \
done
$(shell command -v bat >/dev/null && echo "bat --pager=none" || echo cat) $(PGO_SET_LOC)/stdout.log; \
$(shell command -v bat >/dev/null && echo "bat --pager=none" || echo cat) $(PGO_SET_LOC)/stderr.log >&2
rm -rf $(PGO_SET_LOC)
rm pgo-generate
pgo-use: PGO_FLAGS = -fprofile-use
pgo-use: PGO_LDFLAGS = -lgcov -fprofile-use
pgo-use: CFLAGS+= $(RELEASE_CFLAGS)
pgo-use: CXXFLAGS += $(RELEASE_CXXFLAGS)
pgo-use: LDFLAGS += $(RELEASE_LDFLAGS)
pgo-use: $(PGO_OBJ)
$(CXX) $^ $(CXXFLAGS) $(PGO_FLAGS) -o $@ $(LDFLAGS) $(PGO_LDFLAGS)
$(PROJECT)-pgo: | pgo-profile
find ./obj/pgo -name \*.o -exec rm {} +
$(MAKE) pgo-use
mv pgo-use $@
$(STRIP) $@
clean-rebuild:
rm -rf obj
rm -f $(PROJECT)-{release,debug}
clean: clean-rebuild
rm -f $(PROJECT)-{release,debug,pgo}
test-all:
@./test.sh ./$(PROJECT)-*

@ -19,6 +19,44 @@ $ shuffle3 -u file
## Other options
Run with `-h` for more options.
# Improvements from `v1`
* **~70-80x** speedup from shuffle3 v1.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
## Performance
[hyperfine](https://github.com/sharkdp/hyperfine) reports a **700-800%** speedup over `v1`.
It's easy to see why.
### V1 flamegraph
V1 uses a pesudo-array adaptor to perform filesystem reads, seeks, and writes. This causes a massive syscall overhead.
![](./profiling/release-flame-old.png)
### V2 flamegraph
Whereas V2 uses a single `mmap()`.
![](./profiling/release-flame.png)
## Memory usage
The [memusage](https://www.systutorials.com/docs/linux/man/1-memusage/) graph for `v1` shows extremely inefficient stack usage.
![](./profiling/old-mem.png)
( the green is supposed to be a line, not a bar! )
This is due to how the unshuffler buffers RNG results.
`v1` naively used VLAs to store this buffer, which can baloon to 8 times the size of the file being unshuffled.
It dumps this massive buffer onto the stack frame of a function that is called multiple times, causing massive and inefficient stack usage.
This can cause a segfault when attempting to unshuffle a large file, while shuffling a file of the same size might succeed.
### V2 improvement
The `memusage` graph for `v2` is a lot more sane.
![](./profiling/mem.png)
`v2` instead allocates this buffer on the heap. Note the stable stack and heap usage.
# Building
Run `make` to build the normal binary. It will output to `shuffle3-release`.
@ -33,9 +71,26 @@ To build with debug information, run `make debug`. Extra debug flags can be prov
The build and unstripped binary will be `shuffle3-debug`.
## PGO target
To build with Profile Guided Optimisation run `make pgo`, the stripped and optimised binary will be output to `shuffle3-pgo`.
## Notes
Before switching between `release` and `debug` targets, remember to run `make clean`.
To disable stripping of release build binaries, run with `make STRIP=: release`
### Compile-time flags
There are some build-time flags you can switch while building by appending to the `FEATURE_FLAGS` variable.
| Flag | Description |
|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `DEBUG` | Pretend we're building a debug release even though we're not. |
| `_FS_SPILL_BUFFER` | Spill buffers into a file if they grow over a threshold. Can cause massive slowdowns but prevent OOMs while unshuffling on systems with low available memory. See [shuffle3.h](./include/shuffle3.h) for more details |
| `_FS_SPILL_BUFFER=DYN` | Same as above except allocates memory dynamically. Might be faster. |
| `_FS_SPILL_BUFFER=MAP` | Same as above except it calls `fallocate()` and `mmap()` to prodive a buffer of the full size needed. Is usually the fastest of the options for `_FS_SPILL_BUFFER` and is preferrable if possible. |
## Gentoo ebuild
There is a gentoo ebuild for this project in the overlay [test-overlay](https://git.flanchan.moe/birb/test-overlay).
[direct link](https://git.flanchan.moe/birb/test-overlay/src/branch/master/app-misc/shuffle3/shuffle3-2.0.0.ebuild)
# License
GPL'd with <3

@ -26,6 +26,33 @@
- Can properly handle large files without core dumping
- Doesn't dump huge amounts of trash onto each stack frame
** Performance
[[https://github.com/sharkdp/hyperfine][hyperfine]] reports a *700-800%* speedup over =v1=.
It's easy to see why.
*** V1 flamegraph
V1 uses a pesudo-array adaptor to perform filesystem reads, seeks, and writes. This causes a massive syscall overhead.
[[./profiling/release-flame-old.svg]]
*** V2 flamegraph
Whereas V2 uses a single ~mmap()~.
[[./profiling/release-flame.svg]]
** Memory usage
The [[https://www.systutorials.com/docs/linux/man/1-memusage/][memusage]] graph for =v1= shows extremely inefficient stack usage.
[[./profiling/old-mem.png]]
( the green is supposed to be a line, not a bar)
This is due to how the unshuffler buffers RNG results.
=v1= naively used VLAs to store this buffer, which can baloon to 8 times the size of the file being unshuffled.
It dumps this massive buffer onto the stack frame of a function that is called multiple times, causing massive and inefficient stack usage.
This can cause a segfault when attempting to unshuffle a large file, while shuffling a file of the same size might succeed.
*** V2 improvement
The ~memusage~ graph for =v2= is a lot more sane.
[[./profiling/mem.png]]
~v2~ instead allocates this buffer on the heap. Note the stable stack and heap usage.
* Todo
- [X] impl rng
- [X] impl shuffling

@ -1,6 +1,14 @@
#ifndef _DEBUG_H
#define _DEBUG_H
// If `TRACE` is defined, or if `NOTRACE` is defined, use simplified debug info
#if defined(TRACE) || !defined(NOTRACE)
#define D_TRACE
#define _D__FUNC __PRETTY_FUNCTION__
#else
#define _D__FUNC __func__
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -8,12 +16,14 @@ extern "C" {
struct debuginfo {
const char* file;
const char* function;
//const char* pfunction; //TODO: for `#if defined(TRACE)`, __PRETTY_FUNCTION__ goes here, __func__ goes above.
int line;
};
void _do_dprintf(struct debuginfo di, const char* fmt, ...);
#ifdef __cplusplus
#define _D_COMPTIME constexpr
extern "C++" {
#include <utility>
template<typename... Args>
@ -24,14 +34,15 @@ extern "C++" {
_do_dprintf(i, fmt, std::forward<Args>(args)...);
#endif
}
#define D_dprintf(fmt, ...) _real_dprintf(__FILE__, __func__, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__)
#define D_dprintf(fmt, ...) do { if consteval { (void)0; } else { _real_dprintf(__FILE__, _D__FUNC, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__); } } while(0)
}
#else
#define _D_COMPTIME
#ifdef DEBUG
#define D_dprintf(fmt, ...) _do_dprintf( (struct debuginfo){.file = __FILE__, .function = __func__, .line = __LINE__}, fmt __VA_OPT__(,) __VA_ARGS__)
#define D_dprintf(fmt, ...) _do_dprintf( (struct debuginfo){.file = __FILE__, .function = _D__FUNC, .line = __LINE__}, fmt __VA_OPT__(,) __VA_ARGS__)
#else
static inline void _do__nothing(const char* fmt, ...) {}
static _D_COMPTIME 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
@ -39,5 +50,6 @@ static inline void _do__nothing(const char* fmt, ...) {}
#ifdef __cplusplus
}
#endif
#undef _D_COMPTIME
#endif /* _DEBUG_H */

@ -0,0 +1,36 @@
#ifndef _ERROR_H
#define _ERROR_H
#ifndef $PASTE
# define $_PASTE(x,y) x ## y
# define $PASTE(x,y) $_PASTE(x,y)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
constexpr inline bool is_noexcept=
#if __cpp_exceptions
#define EXCEPT 1
//#define try try
//#define catch(...) catch(__VA_ARGS__)
false
#else
#define EXCEPT 0
#define NOEXCEPT
//#define catch(...) __try {} catch(__VA_ARGS__)
//#define try if constexpr(!is_noexcept)
//#define throw (void)0
true
#endif
;
#endif
#endif /* _ERROR_H */

@ -0,0 +1,31 @@
#ifndef _FSVEC_H
#define _FSVEC_H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#define restrict __restrict__
#endif
// A simple file-backed back inserter
typedef struct {
FILE* backing;
size_t len;
} fvec_t;
int fvec_new(fvec_t* restrict obj, const char* path);
void fvec_close(fvec_t* restrict obj);
int fvec_pop_end(fvec_t* restrict obj, size_t sz);
void fvec_push_whole_buffer(fvec_t* restrict obj, const void* _buffer, size_t sz);
int fvec_get_whole_buffer(const fvec_t* restrict obj, void* _buffer, size_t _sz);
#ifdef __cplusplus
#undef restrict
}
#endif
#endif /* _FSVEC_H */

@ -0,0 +1,324 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <memory>
#include <utility>
#include <vector>
#include <map.h>
#include <tempfile.hpp>
#include <panic.h>
#include <debug.h>
#include <shuffle3.h>
template<typename T>
struct i_back_inserter
{
virtual void push_back(T&& value) =0;
virtual void pop_back() =0;
virtual const T& back() const =0;
virtual T& back() =0;
virtual const std::size_t size() const =0;
inline bool is_empty() const { return size()==0; }
virtual inline ~i_back_inserter() =default;
};
struct i_shunt
{
virtual bool is_full() const =0;
virtual inline ~i_shunt() =default;
};
struct file_back_buffer
{
const static constexpr std::size_t DEFAULT_CAP = 1024;
typedef std::uint8_t byte;
file_back_buffer();
file_back_buffer(std::size_t cap);
file_back_buffer(const file_back_buffer& c) = delete;
file_back_buffer(file_back_buffer&& m);
void push_buf(byte* buf, std::size_t len);
bool back(byte* buf, std::size_t len) const;
bool pop_n(std::size_t len);
~file_back_buffer();
private:
struct impl;
std::unique_ptr<impl> inner;
};
namespace {
template<typename T>
inline const T* _die_if_null(const T* input, const char* msg)
{
if(!input) panic(msg);
return input;
}
template<typename T>
inline T* _die_if_null(T* input, const char* msg)
{
if(!input) panic(msg);
return input;
}
}
template<typename T>
struct file_vector : public i_back_inserter<T>
{
inline file_vector() : file_vector(file_back_buffer::DEFAULT_CAP){}
inline file_vector(std::size_t cap) : inserter(file_back_buffer(cap)), len(0), current_back(std::vector<unsigned char>(sizeof(T))) {current_back.resize(sizeof(T));}
inline file_vector(const file_vector<T>& c) = delete;
inline file_vector(file_vector<T>&& m) : inserter(std::move(m.inserter)), len(m.len), current_back(std::move(m.current_back)){}
inline void push_back(T&& value) override
{
inserter.push_buf((file_back_buffer::byte*)&value, sizeof(T));
len += 1;
}
inline T& back() override
{
if(!len) panic("back() called on empty file_vector");
if(!inserter.back(&current_back[0], sizeof(T))) panic("back() failed");
return *_die_if_null((T*)&current_back[0], "file_vector::back() returned null pointer");
}
inline const T& back() const override
{
if(!len) panic("back() called on empty file_vector");
if(!inserter.back(&current_back[0], sizeof(T))) panic("back() failed");
return *_die_if_null((const T*)&current_back[0], "file_vector::back() (const) returned null pointer");
}
inline void pop_back() override
{
if(!len) return;
if(!inserter.pop_n(sizeof(T))) panic("pop_back(): 0 elements");
len-=1;
}
inline const std::size_t size() const override { return len; }
private:
file_back_buffer inserter;
std::size_t len=0;
mutable std::vector<unsigned char> current_back; // what an awful hack...
};
template<typename T, std::size_t Spill = FSV_DEFAULT_SPILL_AT >
requires (Spill > 0)
struct fixed_spill_vector : public i_back_inserter<T>
{
constexpr const static std::size_t SPILL_AT = Spill;
inline fixed_spill_vector() : mem(std::make_unique<std::array<T, Spill> >()){
D_dprintf("alloc cap (static): %lu", Spill);
}
inline fixed_spill_vector(const fixed_spill_vector<T>& c) = delete;
inline fixed_spill_vector(fixed_spill_vector<T>&& m)
: mem(std::move(m.mem)),
mem_fill_ptr(m.mem_fill_ptr),
fil(std::move(m.fil))
{}
inline ~fixed_spill_vector() = default;
inline void push_back(T&& value) override
{
if(mem_is_full()) {
//D_dprintf("Inserting value into fs");
fil.push_back(std::move(value));
} else {
//D_dprintf("Inserting value into memory");
(*mem)[++mem_fill_ptr] = value;
}
}
inline void pop_back() override
{
if(!size()) return;
if(fil.size()) {
//D_dprintf("Popping from fs");
fil.pop_back();
} else {
//D_dprintf("Popping from memory %ld", mem_fill_ptr);
mem_fill_ptr -= 1;
}
}
inline const T& back() const override
{
if (!size()) panic("back() (const) called on no elements");
if(fil.size()) return fil.back();
else return (*mem)[mem_fill_ptr];
}
inline T& back() override
{
if (!size()) panic("back() called on no elements");
if(fil.size()) return fil.back();
else return (*mem)[mem_fill_ptr];
}
inline const std::size_t size() const override
{
return fil.size() + (std::size_t)(mem_fill_ptr+1);
}
private:
inline bool mem_is_full() const { return mem_fill_ptr >= (ssize_t)(Spill-1); }
ssize_t mem_fill_ptr=-1;
std::unique_ptr<std::array<T, Spill>> mem;
file_vector<T> fil;
};
template<typename T>
struct mapped_vector : public i_back_inserter<T>, public i_shunt
{
inline static mapped_vector<T> from_temp(std::size_t sz)
{
D_dprintf("generating with %lu size", sz);
temp_file file;
mapped_vector<T> mvec(file.full_path().c_str(), sz);
D_dprintf("generated?");
mvec.temp = std::make_unique<temp_file>(std::move(file));
return mvec;
}
inline mapped_vector(const char* file, std::size_t sz)
: sz(sz),
temp(nullptr),
map(mm::mmap::allocate(file, sz * sizeof(T))){}
inline mapped_vector(const mapped_vector<T>& c) = delete;
inline mapped_vector(mapped_vector<T>&& m)
: sz(m.sz),
fill_ptr(m.fill_ptr),
temp(std::move(m.temp)),
map(std::move(m.map)){}
inline mapped_vector() : mapped_vector(nullptr, 0)
{
panic("unsupported");
}
inline void push_back(T&& value) override
{
if(is_full()) panic("Tried to push past end of map");
else memory()[++fill_ptr] = value;
}
inline void pop_back() override
{
if(fill_ptr>=0) fill_ptr-=1;
}
inline T& back() override
{
if(fill_ptr>=0)
{
return memory()[fill_ptr];
} else panic("back() called with no elements");
}
inline const T& back() const override
{
if(fill_ptr>=0)
{
return memory()[fill_ptr];
} else panic("back() const called with no elements");
}
inline const std::size_t size() const override { return ((std::size_t)fill_ptr)+1; }
inline std::size_t cap() const { return sz; }
inline bool is_full() const { return fill_ptr >= (ssize_t)(sz-1); }
protected:
inline const span<T> memory() const { return map.as_span().reinterpret<T>(); }
inline span<T> memory() { return map.as_span().reinterpret<T>(); }
private:
std::size_t sz;
ssize_t fill_ptr=-1;
std::unique_ptr<temp_file> temp;
mm::mmap map;
};
template<typename T, typename Shunt>
requires(std::is_base_of<i_back_inserter<T>, Shunt >::value)
struct shunt : public i_back_inserter<T>, protected i_shunt
{
typedef Shunt spill_type;
inline shunt() : shunt(FSV_DEFAULT_SPILL_AT){}
inline shunt(spill_type&& into) : shunt(FSV_DEFAULT_SPILL_AT, std::move(into)){}
inline shunt(std::size_t cap) : shunt(cap, cap){}
inline shunt(std::size_t cap, spill_type&& into) : shunt(cap, cap, std::move(into)){}
inline shunt(std::size_t cap, std::size_t spill, spill_type&& into)
: _spill_at(spill), mem(std::vector<T>()), fil(std::make_unique<spill_type>(std::move(into))) {
mem.reserve(cap);
D_dprintf("alloc (explicit) cap %lu (sz %lu == 0?), spill %lu", cap, mem.size(), spill_at());
}
inline shunt(std::size_t cap, std::size_t spill) : _spill_at(spill), mem(std::vector<T>()), fil(nullptr) {
mem.reserve(cap);
D_dprintf("alloc cap %lu (sz %lu == 0?), spill %lu", cap, mem.size(), spill_at());
}
inline shunt(const shunt<T, Shunt>& c) = delete;
inline shunt(shunt<T, Shunt>&& m) :
_spill_at(m._spill_at),
mem(std::move(m.mem)),
fil(std::move(m.fil)){}
inline void push_back(T&& value) override
{
if(is_full()) {
spl()->push_back(std::move(value));
}
else mem.push_back(std::move(value));
}
inline void pop_back() override
{
if(is_full() && spl()->size()) spl()->pop_back();
else mem.pop_back();
}
inline const T& back() const override
{
if(is_full() && spl()->size()) return spl()->back();
else return mem.back();
}
inline T& back() override
{
if(is_full() && spl()->size()) return spl()->back();
else return mem.back();
}
inline const std::size_t size() const override { return mem.size() + (fil ? fil->size() : 0); }
inline const std::size_t spill_at() const { return _spill_at; }
inline bool is_spilling() const { return is_full(); }
protected:
inline bool is_full() const override { return size()>=spill_at(); }
private:
inline const spill_type* spl() const
{
if(is_full()) {
if(!fil) fil = std::make_unique<spill_type>();
return fil.get();
} else {
return fil.get();
}
}
inline spill_type* spl()
{
if(is_full()) {
if(!fil) fil = std::make_unique<spill_type>();
return fil.get();
} else {
return fil.get();
}
}
std::size_t _spill_at;
std::vector<T> mem;
mutable std::unique_ptr<spill_type> fil;
};
template<typename T>
using dynamic_spill_vector = shunt<T, file_vector<T> >;
template<typename T>
using mapped_spill_vector = shunt<T, mapped_vector<T> >;

@ -2,8 +2,14 @@
#define _MAP_H
#ifdef __cplusplus
#include <stdexcept>
extern "C" {
#define restrict __restrict__
#define MIFCXX(y, n) y
#define MIFC(y, n) n
#else
#define MIFCXX(y, n) n
#define MIFC(y, n) y
#endif
#include <stddef.h>
@ -15,11 +21,23 @@ typedef struct mmap {
size_t len;
} mmap_t;
enum map_flags {
MMF_SHARED,
MMF_PRIVATE,
MMF_READONLY,
};
int map_raw_fd(int fd, mmap_t* restrict ptr, const size_t MIFCXX(* sz, sz[static 1]));
int open_and_alloc(const char* file, mmap_t* restrict ptr, size_t sz);
int open_and_map(const char* file, mmap_t* restrict ptr);
int unmap_and_close(mmap_t map);
int dup_map(const mmap_t *in, mmap_t* restrict out, const size_t *new_size, enum map_flags flags) __attribute__((nonnull(1)));
int map_advise_rand(mmap_t* restrict ptr, int need);
typedef void* (*map_cb)(mmap_t map, void* user);
void* map_and_then(const char* file, map_cb callback, void* user);
void* map_fd_and_then(int fd, map_cb callback, void* user);
int unmap_and_close_s(mmap_t map, int flags);
#ifdef __cplusplus
}
@ -27,15 +45,48 @@ void* map_and_then(const char* file, map_cb callback, void* user);
#include "reinterpret.h"
#include <cstdint>
namespace mm {
enum class Access {
Random,
};
struct mmap;
template<typename T>
concept MemMap = std::derived_from<T, mmap>
and std::is_constructible_v<T, mmap_t>;
struct mmap {
template<MemMap M = mmap>
inline static M allocate(const char* file, std::size_t sz)
{
mmap_t map;
if(!open_and_alloc(file, &map, sz)) panic("Failed to allocmap file");
return M{map};
}
inline static mmap_t create_raw(const char* file)
{
mmap_t map;
if (!open_and_map(file, &map)) panic("Failed to map file");
if (!::open_and_map(file, &map)) panic("Failed to map file");
return map;
}
inline static mmap_t create_raw_fd(int fd, size_t sz)
{
mmap_t map;
if (!::map_raw_fd(fd, &map, &sz)) panic("Failed to allocmap fd");
return map;
}
inline static mmap_t create_raw_fd(int fd)
{
mmap_t map;
if (!::map_raw_fd(fd, &map, nullptr)) panic("Failed to map fd");
return map;
}
template<MemMap M = mmap>
inline static M map_raw_fd(int fd, std::size_t sz = 0)
{
if(sz)
return M{fd, sz};
else return M{fd};
}
inline mmap(mmap_t raw) :inner(raw){}
inline explicit mmap(mmap_t raw) noexcept :inner(raw){}
inline mmap(const char* file)
: inner(create_raw(file)) {}
@ -46,29 +97,106 @@ namespace mm {
}
inline mmap(const mmap& copt) = delete;
inline ~mmap()
inline virtual ~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 virtual mmap&& access(Access a, bool need=false) && noexcept
{
if(inner.ptr)
static_cast<mmap&>(*this).access(a, need);
return std::move(*this);
}
inline virtual mmap& access(Access a, bool need=false) & noexcept
{
switch(a) {
case Access::Random: ::map_advise_rand(const_cast<mmap_t*>(&inner), int(need));
default: break;
}
return *this;
}
inline const span<const unsigned char> as_span() const noexcept { return span(as_ptr(), size()); }
inline span<unsigned char> as_span() noexcept { 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 virtual const std::uint8_t* as_ptr() const noexcept { return static_cast<const std::uint8_t*>(inner.ptr); }
inline virtual std::uint8_t* as_ptr() noexcept { return static_cast<std::uint8_t*>(inner.ptr); }
inline std::size_t size() const { return inner.len; }
inline virtual std::size_t size() const noexcept { return inner.len; }
inline int as_fd() const { return inner.fd; }
inline const mmap_t& as_raw() const { return inner; }
inline virtual int as_fd() const { return inner.fd; }
inline virtual const mmap_t& as_raw() const noexcept { return inner; }
protected:
inline mmap(int fd, size_t sz)
: inner(create_raw_fd(fd, sz)) {}
inline explicit mmap(int fd)
: inner(create_raw_fd(fd)) {}
inline virtual mmap_t& as_raw() noexcept { return const_cast<mmap_t&>( inner ); }
private:
const mmap_t inner;
};
//template mmap::map_raw_fd<mmap>(int, size_t); // Probably not needed?
//template mmap::allocate<mmap>(int, size_t);
struct vmap : public mmap {
inline explicit vmap(mmap_t m) noexcept : mmap(m) {}
vmap();
vmap(size_t);
inline explicit vmap(int fd) noexcept : mmap(fd) {}
//vmap(const char* file);
//int as_fd() const override; unneeded
inline virtual ~vmap() { /* unmap_and_close() is called by super */ }
//TODO: Implement this ^ with `memfd_create()`, etc.
};
template<MemMap M, MemMap D = M>
inline D dup_map(const M& map, size_t resize, map_flags flags =0)
{
const mmap& m = static_cast<const mmap&>(map);
mmap_t out;
if(! ::dup_map(&m.as_raw(), &out, &resize, flags)) panic("Failed to duplicate mapping");
return D{out};
}
template<MemMap M, MemMap D = M>
inline D dup_map(const M& map, map_flags flags =0)
{
const mmap& m = static_cast<const mmap&>(map);
mmap_t out;
if(! ::dup_map(&m.as_raw(), &out, nullptr, flags)) panic("Failed to duplicate mapping");
return D{out};
}
#if 0
// dup_map() deduction guids (XXX: Are these needed, or even valid?)
template<MemMap D>
dup_map(const mmap& m, size_t s, map_flags f) -> dup_map<mmap, D>;
template<>
dup_map(const mmap& m, size_t s, map_flags f) -> dup_map<mmap, mmap>;
template<MemMap M>
dup_map(const M& m, size_t s, map_flags f) -> dup_map<M, M>;
template<MemMap D>
dup_map(const mmap& m, map_flags f) -> dup_map<mmap, D>;
template<>
dup_map(const mmap& m, map_flags f) -> dup_map<mmap, mmap>;
template<MemMap M>
dup_map(const M& m, map_flags f) -> dup_map<M, M>;
#endif
}
#undef restrict
#undef MIFCXX
#undef MIFC
#endif
#endif /* _MAP_H */

@ -20,7 +20,7 @@ extern "C++" {
__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)...);
_do_panic(i, fmt, std::move(args)...);
}
#define panic(fmt, ...) _real_panic(__FILE__, __func__, __LINE__, fmt __VA_OPT__(,) __VA_ARGS__)
}

@ -0,0 +1,406 @@
#pragma once
#include <string>
#include <memory>
#include <utility>
#include <array>
#include <utility>
#include <cstdio>
#ifndef __cplusplus
#error "C++ header only"
#endif
namespace pr {
namespace details [[gnu::visibility("internal")]] {
/*
template<template<typename...> typename P>
struct generic_valid { template<typename... Args> constexpr static inline bool value = requires{ typename P<Args...>; }; };
template<template<typename...> class P>
struct generic : std::conditional_t<generic_valid<P>::value,
std::true_type,
std::false_type>
{ template<typename... Args> using type = P<Args...>; };
template<template<typename> typename T, typename... Args>
concept Generic = generic_valid<T>::template value<Args...>;
template<Generic T, typename... Args>
using generic_t = generic<T>::template type<Args...>;
template<template<typename> template T>
concept AnyGeneric = requires{ typename generic_valid<T>; };
*/
template<typename T>
T* take(T*&& ptr) noexcept { return std::exchange(ptr, nullptr); }
/*
template<template<typename U> Inst, typename T>
struct is_inst_of { constexpr static inline bool value = std::is_same_v<T, Inst<U>>; };
template<typename T, typename I>
constexpr inline bool is_inst_of_v = is_inst_of<I, T>::value;
*/
template<typename U, typename T>
constexpr std::unique_ptr<U> static_ucast(std::unique_ptr<T>&& from) noexcept
{ return std::unique_ptr<U>{static_cast<U*>(std::move(from).release())}; }
template<typename U, typename T>
constexpr std::shared_ptr<U> static_ucast(std::shared_ptr<T>&& from) noexcept
{ return std::static_pointer_cast<U>(std::move(from)); }
template<typename Ptr, typename From> requires(requires(From&& p) {
{ p.release() };
requires(std::is_constructible_v<Ptr, decltype(p.release())>);
})
constexpr Ptr static_pcast(From&& p) noexcept { return Ptr{p.release()}; }
}
using increment_t = long double;
template<typename T>
concept Bar = requires(std::remove_const_t<std::remove_reference_t<T>>& v) {
{ v.spin(increment_t(0)) };
{ std::as_const(v).aux() } -> std::convertible_to<std::string_view>;
//{ v.aux() } -> std::assignable_from<std::string_view>;
{ (v.aux() = std::string{}) } -> std::same_as<std::add_lvalue_reference_t<decltype(v.aux())>>;
//requires(std::assignable_from<std::add_lvalue_reference_t<decltype(v.aux())>, std::string_view>);
{ v.aux() += std::string_view{} } -> std::convertible_to<decltype(v.aux())>;
{ v.aux(std::declval<std::string&&>()) } -> std::convertible_to<std::string_view>;
};
template<Bar>
struct Carrier;
struct Dynamic {
template<Bar> friend class Carrier;
constexpr auto make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>);
constexpr virtual void spin(increment_t) =0;
constexpr virtual std::string& aux() =0;
constexpr virtual std::string const& aux() const noexcept =0;
constexpr virtual std::string aux(std::string&& s) { return std::exchange(aux(), std::move(s)); }
constexpr virtual ~Dynamic() {}
private:
constexpr Dynamic() noexcept {}
};
template<Bar B>
struct Carrier : public virtual Dynamic {
using held_type = B;
template<typename... Args> requires(std::constructible_from<B, Args...>)
constexpr Carrier(Args&&... args) noexcept(std::is_nothrow_constructible_v<B, Args...>)
: Dynamic(), member_(std::forward<decltype(args)>(args)...) {}
constexpr Carrier(Carrier&& m) noexcept(std::is_nothrow_move_constructible_v<B>)
requires(std::is_move_constructible_v<B>)
: Dynamic(), member_(std::move(m.member_)) {}
constexpr Carrier(Carrier const& c) noexcept(std::is_nothrow_copy_constructible_v<B>)
requires(std::is_copy_constructible_v<B>)
: Dynamic(), member_(c.member_) {}
constexpr Carrier& operator=(Carrier&& m) noexcept(std::is_nothrow_move_assignable_v<B>) requires(std::is_move_assignable_v<B>)
{ if(this != &m) member_ = std::move(m.member_); return *this; }
constexpr Carrier& operator=(Carrier const& c) noexcept(std::is_nothrow_copy_assignable_v<B>) requires(std::is_copy_assignable_v<B>)
{ if(this != &c) member_ = c.member_; return *this; }
constexpr void spin(increment_t a) override { member_.spin(a); }
constexpr std::string& aux() override { return member_.aux(); }
constexpr std::string const& aux() const noexcept override { return member_.aux(); }
constexpr std::string aux(std::string&& s) override { return member_.aux(std::move(s)); }
template<typename... Args> requires(std::is_invocable_v<B::spin, B&, increment_t, Args...>)
constexpr void spin(increment_t a, Args&&... args) noexcept(std::is_nothrow_invocable_v<B::spin, B&, increment_t, decltype(args)...>)
{ return member_.spin(a, std::forward<decltype(args)>(args)...); }
constexpr virtual ~Carrier() {}
private:
B member_;
};
template<Bar T>
Carrier(T) -> Carrier<T>;
template<Bar T>
Carrier(T const&) -> Carrier<T>;
template<Bar T>
Carrier(T&&) -> Carrier<T>;
constexpr auto Dynamic::make(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>) { return Carrier(std::move(bar)); }
template<Bar T, typename Ptr = std::unique_ptr<Dynamic>>
constexpr Ptr make_dyn(T&& bar) noexcept(std::is_nothrow_move_constructible_v<T>)
{
using namespace details;
if constexpr(requires{
typename T::held_type;
requires(std::is_same_v<Carrier<typename T::held_type>, T>);
}) return static_pcast<Ptr>(static_ucast<Dynamic>(std::make_unique<T>(std::move(bar))));
else return static_pcast<Ptr>(static_ucast<Dynamic>(std::make_unique<Carrier<T>>(std::move(bar))));
}
template<Bar T, typename Ptr = std::unique_ptr<Dynamic>>
constexpr Ptr make_dyn(T const& bar) noexcept(std::is_nothrow_move_constructible_v<T>)
{
if constexpr(std::is_copy_constructible_v<T>) {
T nbar{bar};
return make_dyn<T, Ptr>(std::move(nbar));
} else {
struct unsafe_ref {
constexpr unsafe_ref(const T& ba) noexcept : b(std::addressof(ba)) {}
constexpr unsafe_ref(const unsafe_ref&) noexcept = default;
constexpr ~unsafe_ref() noexcept = default;
constexpr unsafe_ref(unsafe_ref&& b) noexcept : b(std::exchange(b.b, nullptr)) {}
constexpr unsafe_ref& operator=(unsafe_ref const&) noexcept = default;
constexpr unsafe_ref& operator=(unsafe_ref&& m) noexcept {
if(this != &m)
b = std::exchange(m.b, nullptr);
return *this;
}
const T* b;
constexpr operator T const&() const noexcept { return *b; }
};
unsafe_ref re{bar};
return make_dyn<unsafe_ref, Ptr>(std::move(re));
}
}
template<typename Ptr> requires(requires(std::unique_ptr<Dynamic>&& p) { details::static_pcast<Ptr>(std::move(p)); })
constexpr Ptr make_dyn_for(Bar auto&& bar) noexcept(std::is_nothrow_move_constructible_v<std::remove_reference_t<decltype(bar)>>)
{ return make_dyn<decltype(bar), Ptr>(std::move(bar)); }
constexpr void spin(Bar auto& bar, std::convertible_to<increment_t> auto&& a) { return bar.spin(increment_t(a)); }
struct None {
constexpr None() noexcept = default;
constexpr ~None() noexcept = default;
constexpr void spin(increment_t) const noexcept {}
constexpr auto aux() noexcept { return not_string(); }
inline std::string const& aux() const noexcept { return none_; }
constexpr std::string aux(std::convertible_to<std::string> auto&& s) const noexcept { return {}; } //{ return std::exchange(none_, std::string(std::move(s))); }
private:
struct not_string {
constexpr not_string() noexcept= default;
constexpr ~not_string() noexcept= default;
[[gnu::const]]
constexpr not_string& operator+=(std::convertible_to<std::string_view> auto&&) { return *this; }
[[gnu::const]]
constexpr not_string& operator=(std::convertible_to<std::string> auto&&) { return *this; }
inline operator std::string&() noexcept { return none_; }
inline operator std::string const&() const noexcept { return none_; }
};
/*constinit*/ thread_local static inline std::string none_;
};
constexpr inline None disable{};
// A bounded progress-bar
struct Progress {
using fract_t = increment_t;
constexpr static inline bool DEFAULT_WIDTH = 50;
constexpr static inline fract_t DEFAULT_TERM_FRACT=1.0l/3.0l;
[[gnu::nonnull(1)]]
Progress(FILE* p = stdout) noexcept;
Progress(const Progress& p) noexcept;
Progress(Progress&& p) noexcept;
Progress& operator=(Progress const& c) noexcept;
Progress& operator=(Progress&& m) noexcept;
virtual ~Progress();
inline std::string& aux() noexcept { return tag(); }
inline std::string const& aux() const noexcept { return tag(); }
inline std::string aux(std::convertible_to<std::string> auto&& s)
{ return std::exchange(tag(), std::string(std::move(s))); }
protected:
std::string& tag() noexcept;
const std::string& tag() const noexcept;
public:
FILE* output() const noexcept;
FILE*& output() noexcept;
size_t& width() noexcept;
size_t width() const noexcept;
fract_t& fraction() noexcept;
fract_t fraction() const noexcept;
fract_t percentage() const noexcept;
inline auto percentage() noexcept {
struct v final {
Progress* p_;
v(Progress* p) noexcept : p_(p) {}
v(v&&) noexcept = default;
v(v const&) noexcept = default;
v& operator=(v const&) noexcept = default;
v& operator=(v&&) noexcept = default;
~v() noexcept = default;
const v& operator=(fract_t f) const noexcept { p_->percentage(f); return *this; }
operator fract_t() const noexcept { return static_cast<const Progress*>(p_)->percentage(); }
};
return v(this);
}
void spin(increment_t by, bool render=true, bool flush=true) noexcept;
void render(bool flush = true) const noexcept;
private:
void percentage(fract_t fract) noexcept;
struct _impl;
std::shared_ptr<_impl> inner_;
};
#ifdef SPINNER
class Spinner {
consteval static auto _GENERATE_MAP(auto const& map, size_t size) noexcept
-> std::array<size_t, 256>
{
static_assert(sizeof(map) == size, "Bad map size");
std::array<size_t, 256> out{};
while( size --> 0 ) out[int(map[size])] = size;
return out;
}
constexpr static increment_t range(increment_t by)
{
if(by < -1.0l) return -1.0l;
else if(by > 1.0l) return 1.0l;
return by;
}
public:
constexpr static inline auto ROUTINE = "|/-\\|/-\\|";
constexpr static inline auto ROUTINE_SIZE = sizeof(ROUTINE);
constexpr static inline auto REVERSE_MAP = _GENERATE_MAP(ROUTINE, ROUTINE_SIZE);
static_assert(ROUTINE_SIZE != sizeof(char*), "Invalid routine size");
constexpr ssize_t range(int sz) noexcept
{
/*if(__builtin_expect(sz < 0, false)) {
std::terminate(); // TODO: How to handle wrapping negatives?? Ugh.
}*/
return ssize_t(sz) % ssize_t(ROUTINE_SIZE);
}
constexpr Spinner(size_t n) noexcept
: cur_(ROUTINE[range(n)]) {}
constexpr Spinner(char c = ROUTINE[0]) noexcept
: cur_(range(REVERSE_MAP[int(c)])) {}
constexpr Spinner(Spinner const&) noexcept = default;
constexpr Spinner(Spinner &&) noexcept = default;
~Spinner();
inline void spin(int by) noexcept
{
operator+=(by);
render();
}
inline void spin(increment_by by) noexcept
{
spin(int(range(by) * increment_t(ROUTINE_SIZE)));
}
inline Spinner& operator+=(int by) noexcept
{
cur_ = ROUTINE[size_t(ssize_t(REVERSE_MAP[int(cur_)]) + range(by))];
return *this;
}
inline Spinner& operator++() noexcept { spin(1); return *this; }
inline Spinner operator++(int) noexcept { Spinner s = *this; ++s; return *this; }
constexpr Spinner& operator=(Spinner&&) noexcept = default;
constexpr Spinner& operator=(Spinner const&) noexcept = default;
constexpr char& character() noexcept { return cur_; }
constexpr char character() const noexcept { return cur_; }
void render(bool flush=true);
private:
char cur_ = '|';
};
//TODO: Spinny bar thing (unbounded)
#endif
/*
struct Bar {
Bar() noexcept = default;
Bar(const Bar&) noexcept = default;
Bar(Bar&&) noexcept = default;
Bar& operator=(const Bar&) noexcept = default;
Bar& operator=(Bar&&) noexcept = default;
virtual ~Bar() = default;
virtual void spin(int) =0;
};
enum bar_kind {
BRK_UNTRACKED,
BRK_TRACKED,
};
template<bar_kind = BRK_UNTRACKED>
struct progress;
template<>
struct progress<BRK_UNTRACKED>
: public virtual Bar {
progress(FILE* to);
progress(progress const&);
inline progress(progress&& mv) noexcept
: Bar()
, _perc(mv._perc)
, _output(details::take(mv._output)) {}
virtual ~progress();
void spin(int) override;
//TODO: this
protected:
union {
double _perc;
size_t _num; // not used here
};
FILE* _output;
};
template<>
class progress<BRK_TRACKED>
: public progress<BRK_UNTRACKED> {
using base_t = progress<BRK_UNTRACKED>;
public:
inline progress(FILE* to, size_t max) : base_t(to), _num(0), _max(max) {}
progress(const progress&) = default;
progress(progress&& m) : base_t(std::move(m)), _num(std::exchange(m._num, 0)), _max(m._max) {}
void spin(int) override;
//TODO: this
virtual ~progress() {}
protected:
using base_t::_output;
private:
using base_t::_num;
size_t _max;
};
*/
}

@ -4,6 +4,7 @@
#include "shuffle3.h"
#ifdef __cplusplus
#include "rng/impl.hpp"
extern "C" {
#endif
@ -11,8 +12,12 @@ enum rng_kind {
RNG_KIND_FRNG,
RNG_KIND_DRNG,
RNG_KIND_XORNG,
RNG_KIND_LORENZ,
};
typedef long double rng_st_lorenz_t;
typedef _Complex long double rng_lorenz_t;
typedef struct rng_init
{
enum rng_kind kind;
@ -25,7 +30,12 @@ typedef struct rng_init
} drng;
struct {
uint64_t state[2];
} xorng;
} xorng;
struct {
rng_lorenz_t point;
const rng_st_lorenz_t (* _UNIQUE state)[5];
uint64_t iter;
} lorenz;
} init;
} rng_init_opt;
@ -38,14 +48,15 @@ void rng_free(rng_t ptr);
// Tests
extern void rng_test();
extern void rng_test_spec(rng_t rng);
extern void rng_test_spec(rng_t rng) __attribute__((nonnull(1)));
#ifdef __cplusplus
}
// RNG interfaces
#include <rng/frng.hpp>
#include <rng/drng.hpp>
#include <rng/xoroshiro128plus.hpp>
#include "rng/xoroshiro128plus.hpp"
#include "rng/frng.hpp"
#include "rng/drng.hpp"
#include "rng/lorenz.hpp"
namespace rng {
void test_algo(RNG&& rng);

@ -34,6 +34,7 @@ namespace rng
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}
constexpr virtual ~frng() = default;
#undef P
inline constexpr double next_double() override { return sample(); }
inline constexpr float next_float() override { return (float)sample(); }

@ -6,6 +6,8 @@
/// Base class for RNG impls
struct RNG {
constexpr RNG() noexcept = default;
virtual unsigned char byte();
virtual void bytes(unsigned char* ptr, std::size_t len);
@ -29,7 +31,12 @@ struct RNG {
inline virtual float next_float() { return (float)sample(); }
inline virtual double next_double() { return sample(); }
virtual ~RNG() = default;
constexpr virtual ~RNG() = default;
//explicit operator rng_t() const noexcept;
//friend operator RNG*(rng_t) noexcept;
protected:
virtual double sample() = 0;
//private:
//struct rng_impl* _UNIQUE _held = nullptr;
};

@ -0,0 +1,2 @@
#pragma once

@ -9,10 +9,11 @@ namespace 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}
inline constexpr xoroshiro128plus(std::uint64_t s0, std::uint64_t s1) : RNG(), state({s0, s1}){P}
inline constexpr xoroshiro128plus(std::array<std::uint64_t, 2>&& ar) : RNG(), state(ar){P}
inline constexpr xoroshiro128plus(const std::array<std::uint64_t, 2>& ar) : RNG(), state(ar){P}
inline constexpr xoroshiro128plus(const std::uint64_t (&ar)[2]) : RNG(), state({ar[0], ar[1]}){P}
inline virtual ~xoroshiro128plus() {}
#undef P
std::uint64_t next_ulong();
using RNG::next_long;
@ -25,5 +26,6 @@ namespace rng
private:
State state;
};
static_assert(std::derived_from<xoroshiro128plus, RNG>, "Wtf???");
}

@ -3,29 +3,67 @@
#include <rng.h>
#include <reinterpret.h>
#include <algorithm>
#include <fmt/format.h>
#include <iostream>
#include <fsvec.hpp>
#include <shuffle3.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());
std::cout << " -> shuffling " << span.size() << " objects..." << std::flush;
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");
std::cout << " OK" << std::endl;
}
namespace {
extern "C" int _can_allocate(std::size_t bytes);
template<typename T>
inline bool can_allocate(std::size_t len)
{
return !!_can_allocate(len*sizeof(T));
}
}
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());
#define DYN 2
#define MAP 3
#if defined(_FS_SPILL_BUFFER) && (_FS_SPILL_BUFFER == DYN)
D_dprintf("spill=dyn");
dynamic_spill_vector<std::size_t> rng_values =
//can_allocate<std::size_t>(span.size()) //Is there any way we can not waste this malloc() when it's valid?
span.size() <= FSV_DEFAULT_SPILL_AT
? dynamic_spill_vector<std::size_t> (span.size(), FSV_DEFAULT_SPILL_AT)
: dynamic_spill_vector<std::size_t> (FSV_DEFAULT_SPILL_AT);
#elif defined(_FS_SPILL_BUFFER) && (_FS_SPILL_BUFFER == MAP)
D_dprintf("spill=map");
mapped_spill_vector<std::size_t> rng_values =
span.size() <= FSV_DEFAULT_SPILL_AT
? mapped_spill_vector<std::size_t> (span.size(), FSV_DEFAULT_SPILL_AT)
: mapped_spill_vector<std::size_t> (FSV_DEFAULT_SPILL_AT, mapped_vector<std::size_t>::from_temp(span.size() - FSV_DEFAULT_SPILL_AT));
#elif defined(_FS_SPILL_BUFFER)
D_dprintf("spill=static");
fixed_spill_vector<std::size_t> rng_values;
#else
D_dprintf("spill=none");
std::vector<std::size_t> rng_values;
rng_values.reserve(span.size());
#endif
#undef MAP
#undef DYN
std::cout << " -> unshuffling " << span.size() << " objects..." << std::flush;
for(std::size_t i=span.size()-1;i>0;i--)
rng_values.push_back(rng.next_long(i));
@ -33,6 +71,6 @@ namespace rng {
std::swap(span[i], span[rng_values.back()]);
rng_values.pop_back();
}
fmt::print(" OK\n");
std::cout << " OK" << std::endl;
}
}

@ -14,13 +14,25 @@ extern "C" {
#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, ...)
//** Features **//
//#define _FS_SPILL_BUFFER /* Use a file-backed buffer when unshuffling in cases of too high memory usage. Will cause massive slowdowns but can stop OOMs when unshuffling large files */
/// When to spill a file-backed buffer onto the fs (only used when `_FS_SPILL_BUFFER` is enabled).
#define FSV_DEFAULT_SPILL_AT ((1024 * 1024) * 10) //10MB
/*
#ifdef _FS_SPILL_BUFFER
#define DYN 1
#if _FS_SPILL_BUFFER == DYN
#undef _FS_SPILL_BUFFER
#define _FS_SPILL_BUFFER DYN
#endif
#undef DYN
#endif
*/
//** Globals *//
extern const char* _prog_name;
#ifdef __cplusplus

@ -0,0 +1,49 @@
#pragma once
#include <filesystem>
#include <string>
#include <utility>
#include "uuid.hpp"
#include <debug.h>
using std::size_t;
namespace fs = std::filesystem;
/// A temporary file name
struct temp_file
{
inline temp_file(const temp_file& c) = delete;
inline temp_file(temp_file&& m) : name(std::move(m.name)), _full_path(std::move(m._full_path)) {}
inline temp_file() : name(uuid::generate().to_string()+"-s3"){}
inline temp_file(const char* name) : name(name) {}
inline temp_file(std::string&& name) : name(name) {}
inline ~temp_file()
{
if(name.empty() && _full_path.empty()) return;
D_dprintf("~tempfile(): %s", _full_path.c_str());
if(!_full_path.empty() && fs::exists(_full_path) ) {
D_dprintf("tfile removing: %s", _full_path.c_str());
fs::remove(_full_path);
}
}
inline const fs::path& full_path() const
{
if(_full_path.empty()) {
_full_path = fs::absolute( fs::temp_directory_path() / name );
D_dprintf("tfile path: %s", _full_path.c_str());
}
return _full_path;
}
inline const std::string& base_name() const { return name; }
inline const fs::path* operator->() const { return &full_path(); }
private:
std::string name;
mutable fs::path _full_path;
};

@ -0,0 +1,32 @@
#pragma once
#include <array>
#include <random>
#include <string>
struct uuid //not to spec but idc
{
const static constexpr int SIZE=16;
inline static uuid generate()
{
using namespace std;
static thread_local random_device dev;
static thread_local mt19937 rng(dev());
uniform_int_distribution<int> dist(0, 15);
constexpr const char hex[] = "0123456789abcdef";
uuid id;
for(int i=0;i<SIZE;i++) id.str[i] = hex[dist(rng)];
id.str[SIZE] =0;
return id;
}
inline operator const char*() const { return &str[0]; }
inline operator const std::array<char, SIZE+1>&() const { return str; }
inline std::string to_string() const
{
return std::string(&str[0]);
}
private:
inline uuid(){}
std::array<char, SIZE+1> str;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

@ -0,0 +1,567 @@
desc: (none)
cmd: ./shuffle3-release small-u
time_unit: i
#-----------
snapshot=0
#-----------
time=0
mem_heap_B=0
mem_heap_extra_B=0
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=1
#-----------
time=4048133
mem_heap_B=32816
mem_heap_extra_B=8
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=2
#-----------
time=4050820
mem_heap_B=8
mem_heap_extra_B=16
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=3
#-----------
time=4057134
mem_heap_B=72712
mem_heap_extra_B=24
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=4
#-----------
time=4282565
mem_heap_B=73736
mem_heap_extra_B=32
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=5
#-----------
time=4289663
mem_heap_B=74248
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=6
#-----------
time=4295729
mem_heap_B=75784
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=7
#-----------
time=4303612
mem_heap_B=77832
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=8
#-----------
time=4318766
mem_heap_B=86024
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=9
#-----------
time=4322930
mem_heap_B=81928
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=10
#-----------
time=4353188
mem_heap_B=98312
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=11
#-----------
time=4361448
mem_heap_B=90120
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=12
#-----------
time=4421914
mem_heap_B=122888
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=13
#-----------
time=4438366
mem_heap_B=122888
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=detailed
n3: 122888 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
n1: 72704 0x493F45A: pool (eh_alloc.cc:123)
n1: 72704 0x493F45A: __static_initialization_and_destruction_0 (eh_alloc.cc:262)
n1: 72704 0x493F45A: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:338)
n1: 72704 0x40112DD: call_init.part.0 (in /usr/lib/ld-2.32.so)
n1: 72704 0x40113C7: _dl_init (in /usr/lib/ld-2.32.so)
n1: 72704 0x40020C9: ??? (in /usr/lib/ld-2.32.so)
n1: 72704 0x1: ???
n1: 72704 0x1FFF000AC6: ???
n0: 72704 0x1FFF000AD9: ???
n1: 49152 0x10A384: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 49152 0x109385: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 49152 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n0: 1032 in 3 places, all below massif's threshold (1.00%)
#-----------
snapshot=14
#-----------
time=4438366
mem_heap_B=106504
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=15
#-----------
time=4559248
mem_heap_B=172040
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=16
#-----------
time=4592084
mem_heap_B=172040
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=detailed
n3: 172040 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
n1: 98304 0x10A384: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 98304 0x109385: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 98304 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n1: 72704 0x493F45A: pool (eh_alloc.cc:123)
n1: 72704 0x493F45A: __static_initialization_and_destruction_0 (eh_alloc.cc:262)
n1: 72704 0x493F45A: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:338)
n1: 72704 0x40112DD: call_init.part.0 (in /usr/lib/ld-2.32.so)
n1: 72704 0x40113C7: _dl_init (in /usr/lib/ld-2.32.so)
n1: 72704 0x40020C9: ??? (in /usr/lib/ld-2.32.so)
n1: 72704 0x1: ???
n1: 72704 0x1FFF000AC6: ???
n0: 72704 0x1FFF000AD9: ???
n0: 1032 in 3 places, all below massif's threshold (1.00%)
#-----------
snapshot=17
#-----------
time=4592084
mem_heap_B=139272
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=18
#-----------
time=4833798
mem_heap_B=270344
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=19
#-----------
time=4899402
mem_heap_B=270344
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=detailed
n3: 270344 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
n1: 196608 0x10A384: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 196608 0x109385: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 196608 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n1: 72704 0x493F45A: pool (eh_alloc.cc:123)
n1: 72704 0x493F45A: __static_initialization_and_destruction_0 (eh_alloc.cc:262)
n1: 72704 0x493F45A: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:338)
n1: 72704 0x40112DD: call_init.part.0 (in /usr/lib/ld-2.32.so)
n1: 72704 0x40113C7: _dl_init (in /usr/lib/ld-2.32.so)
n1: 72704 0x40020C9: ??? (in /usr/lib/ld-2.32.so)
n1: 72704 0x1: ???
n1: 72704 0x1FFF000AC6: ???
n0: 72704 0x1FFF000AD9: ???
n0: 1032 in 3 places, all below massif's threshold (1.00%)
#-----------
snapshot=20
#-----------
time=4899402
mem_heap_B=204808
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=21
#-----------
time=5382780
mem_heap_B=466952
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=22
#-----------
time=5513920
mem_heap_B=466952
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=peak
n3: 466952 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
n1: 393216 0x10A384: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 393216 0x109385: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 393216 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n1: 72704 0x493F45A: pool (eh_alloc.cc:123)
n1: 72704 0x493F45A: __static_initialization_and_destruction_0 (eh_alloc.cc:262)
n1: 72704 0x493F45A: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:338)
n1: 72704 0x40112DD: call_init.part.0 (in /usr/lib/ld-2.32.so)
n1: 72704 0x40113C7: _dl_init (in /usr/lib/ld-2.32.so)
n1: 72704 0x40020C9: ??? (in /usr/lib/ld-2.32.so)
n1: 72704 0x1: ???
n1: 72704 0x1FFF000AC6: ???
n0: 72704 0x1FFF000AD9: ???
n0: 1032 in 3 places, all below massif's threshold (1.00%)
#-----------
snapshot=23
#-----------
time=5513920
mem_heap_B=335880
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=24
#-----------
time=5892087
mem_heap_B=73736
mem_heap_extra_B=32
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=25
#-----------
time=5989421
mem_heap_B=73744
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=26
#-----------
time=5996279
mem_heap_B=73992
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=27
#-----------
time=6002698
mem_heap_B=74248
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=28
#-----------
time=6015289
mem_heap_B=75272
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=29
#-----------
time=6015414
mem_heap_B=74760
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=30
#-----------
time=6040651
mem_heap_B=76808
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=31
#-----------
time=6040828
mem_heap_B=75784
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=32
#-----------
time=6091310
mem_heap_B=79880
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=detailed
n4: 79880 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
n1: 72704 0x493F45A: pool (eh_alloc.cc:123)
n1: 72704 0x493F45A: __static_initialization_and_destruction_0 (eh_alloc.cc:262)
n1: 72704 0x493F45A: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:338)
n1: 72704 0x40112DD: call_init.part.0 (in /usr/lib/ld-2.32.so)
n1: 72704 0x40113C7: _dl_init (in /usr/lib/ld-2.32.so)
n1: 72704 0x40020C9: ??? (in /usr/lib/ld-2.32.so)
n1: 72704 0x1: ???
n1: 72704 0x1FFF000AC6: ???
n0: 72704 0x1FFF000AD9: ???
n2: 6144 0x10A384: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 6144 0x1095C2: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 6144 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n0: 0 in 1 place, below massif's threshold (1.00%)
n1: 1024 0x4CB6E03: _IO_file_doallocate (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC570F: _IO_doallocbuf (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC48A7: _IO_file_overflow@@GLIBC_2.2.5 (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC3955: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CB8330: fwrite (in /usr/lib/libc-2.32.so)
n1: 1024 0x4877FD7: fmt::v7::vprint(_IO_FILE*, fmt::v7::basic_string_view<char>, fmt::v7::format_args) (in /usr/lib/libfmt.so.7.1.2)
n1: 1024 0x10A258: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 1024 0x10933E: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 1024 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n0: 8 in 2 places, all below massif's threshold (1.00%)
#-----------
snapshot=33
#-----------
time=6091591
mem_heap_B=77832
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=34
#-----------
time=6192490
mem_heap_B=86024
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=35
#-----------
time=6196654
mem_heap_B=81928
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=36
#-----------
time=6398233
mem_heap_B=98312
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=37
#-----------
time=6406493
mem_heap_B=90120
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=38
#-----------
time=6809904
mem_heap_B=122888
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=39
#-----------
time=6826356
mem_heap_B=106504
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=40
#-----------
time=7633039
mem_heap_B=172040
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=41
#-----------
time=7665875
mem_heap_B=139272
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=42
#-----------
time=7951930
mem_heap_B=73736
mem_heap_extra_B=32
mem_stacks_B=0
heap_tree=detailed
n3: 73736 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
n1: 72704 0x493F45A: pool (eh_alloc.cc:123)
n1: 72704 0x493F45A: __static_initialization_and_destruction_0 (eh_alloc.cc:262)
n1: 72704 0x493F45A: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:338)
n1: 72704 0x40112DD: call_init.part.0 (in /usr/lib/ld-2.32.so)
n1: 72704 0x40113C7: _dl_init (in /usr/lib/ld-2.32.so)
n1: 72704 0x40020C9: ??? (in /usr/lib/ld-2.32.so)
n1: 72704 0x1: ???
n1: 72704 0x1FFF000AC6: ???
n0: 72704 0x1FFF000AD9: ???
n1: 1024 0x4CB6E03: _IO_file_doallocate (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC570F: _IO_doallocbuf (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC48A7: _IO_file_overflow@@GLIBC_2.2.5 (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC3955: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CB8330: fwrite (in /usr/lib/libc-2.32.so)
n1: 1024 0x4877FD7: fmt::v7::vprint(_IO_FILE*, fmt::v7::basic_string_view<char>, fmt::v7::format_args) (in /usr/lib/libfmt.so.7.1.2)
n1: 1024 0x10A258: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 1024 0x10933E: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 1024 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n0: 8 in 3 places, all below massif's threshold (1.00%)
#-----------
snapshot=43
#-----------
time=7971885
mem_heap_B=73744
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=44
#-----------
time=7979276
mem_heap_B=75784
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=45
#-----------
time=7985879
mem_heap_B=77832
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=46
#-----------
time=7998473
mem_heap_B=86024
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=detailed
n4: 86024 (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
n1: 72704 0x493F45A: pool (eh_alloc.cc:123)
n1: 72704 0x493F45A: __static_initialization_and_destruction_0 (eh_alloc.cc:262)
n1: 72704 0x493F45A: _GLOBAL__sub_I_eh_alloc.cc (eh_alloc.cc:338)
n1: 72704 0x40112DD: call_init.part.0 (in /usr/lib/ld-2.32.so)
n1: 72704 0x40113C7: _dl_init (in /usr/lib/ld-2.32.so)
n1: 72704 0x40020C9: ??? (in /usr/lib/ld-2.32.so)
n1: 72704 0x1: ???
n1: 72704 0x1FFF000AC6: ???
n0: 72704 0x1FFF000AD9: ???
n2: 12288 0x10A384: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 12288 0x10973E: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 12288 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n0: 0 in 2 places, all below massif's threshold (1.00%)
n1: 1024 0x4CB6E03: _IO_file_doallocate (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC570F: _IO_doallocbuf (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC48A7: _IO_file_overflow@@GLIBC_2.2.5 (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CC3955: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib/libc-2.32.so)
n1: 1024 0x4CB8330: fwrite (in /usr/lib/libc-2.32.so)
n1: 1024 0x4877FD7: fmt::v7::vprint(_IO_FILE*, fmt::v7::basic_string_view<char>, fmt::v7::format_args) (in /usr/lib/libfmt.so.7.1.2)
n1: 1024 0x10A258: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n1: 1024 0x10933E: ??? (in /home/avril/work/shuffle3/lean/shuffle3-release)
n0: 1024 0x4C6A151: (below main) (in /usr/lib/libc-2.32.so)
n0: 8 in 2 places, all below massif's threshold (1.00%)
#-----------
snapshot=47
#-----------
time=8002637
mem_heap_B=81928
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=48
#-----------
time=8027775
mem_heap_B=98312
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=49
#-----------
time=8036035
mem_heap_B=90120
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=50
#-----------
time=8086261
mem_heap_B=122888
mem_heap_extra_B=48
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=51
#-----------
time=8102713
mem_heap_B=106504
mem_heap_extra_B=40
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=52
#-----------
time=8147526
mem_heap_B=73736
mem_heap_extra_B=32
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=53
#-----------
time=8156172
mem_heap_B=1032
mem_heap_extra_B=24
mem_stacks_B=0
heap_tree=empty
#-----------
snapshot=54
#-----------
time=8157131
mem_heap_B=8
mem_heap_extra_B=16
mem_stacks_B=0
heap_tree=empty

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@ -0,0 +1,419 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" width="1200" height="182" onload="init(evt)" viewBox="0 0 1200 182" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples.--><!--NOTES: --><defs><linearGradient id="background" y1="0" y2="1" x1="0" x2="0"><stop stop-color="#eeeeee" offset="5%"/><stop stop-color="#eeeeb0" offset="95%"/></linearGradient></defs><style type="text/css">
text { font-family:"Verdana"; font-size:12px; fill:rgb(0,0,0); }
#title { text-anchor:middle; font-size:17px; }
#search { opacity:0.1; cursor:pointer; }
#search:hover, #search.show { opacity:1; }
#subtitle { text-anchor:middle; font-color:rgb(160,160,160); }
#unzoom { cursor:pointer; }
#frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; }
.hide { display:none; }
.parent { opacity:0.5; }
</style><script type="text/ecmascript"><![CDATA[var nametype = 'Function:';
var fontsize = 12;
var fontwidth = 0.59;
var xpad = 10;
var inverted = false;
var searchcolor = 'rgb(230,0,230)';
var fluiddrawing = true;
var truncate_text_right = false;]]><![CDATA["use strict";
var details, searchbtn, unzoombtn, matchedtxt, svg, searching, frames;
function init(evt) {
details = document.getElementById("details").firstChild;
searchbtn = document.getElementById("search");
unzoombtn = document.getElementById("unzoom");
matchedtxt = document.getElementById("matched");
svg = document.getElementsByTagName("svg")[0];
frames = document.getElementById("frames");
searching = 0;
// Use GET parameters to restore a flamegraph's state.
var restore_state = function() {
var params = get_params();
if (params.x && params.y)
zoom(find_group(document.querySelector('[x="' + params.x + '"][y="' + params.y + '"]')));
if (params.s)
search(params.s);
};
if (fluiddrawing) {
// Make width dynamic so the SVG fits its parent's width.
svg.removeAttribute("width");
// Edge requires us to have a viewBox that gets updated with size changes.
var isEdge = /Edge\/\d./i.test(navigator.userAgent);
if (!isEdge) {
svg.removeAttribute("viewBox");
}
var update_for_width_change = function() {
if (isEdge) {
svg.attributes.viewBox.value = "0 0 " + svg.width.baseVal.value + " " + svg.height.baseVal.value;
}
// Keep consistent padding on left and right of frames container.
frames.attributes.width.value = svg.width.baseVal.value - xpad * 2;
// Text truncation needs to be adjusted for the current width.
var el = frames.children;
for(var i = 0; i < el.length; i++) {
update_text(el[i]);
}
// Keep search elements at a fixed distance from right edge.
var svgWidth = svg.width.baseVal.value;
searchbtn.attributes.x.value = svgWidth - xpad - 100;
matchedtxt.attributes.x.value = svgWidth - xpad - 100;
};
window.addEventListener('resize', function() {
update_for_width_change();
});
// This needs to be done asynchronously for Safari to work.
setTimeout(function() {
unzoom();
update_for_width_change();
restore_state();
}, 0);
} else {
restore_state();
}
}
// event listeners
window.addEventListener("click", function(e) {
var target = find_group(e.target);
if (target) {
if (target.nodeName == "a") {
if (e.ctrlKey === false) return;
e.preventDefault();
}
if (target.classList.contains("parent")) unzoom();
zoom(target);
// set parameters for zoom state
var el = target.querySelector("rect");
if (el && el.attributes && el.attributes.y && el.attributes._orig_x) {
var params = get_params()
params.x = el.attributes._orig_x.value;
params.y = el.attributes.y.value;
history.replaceState(null, null, parse_params(params));
}
}
else if (e.target.id == "unzoom") {
unzoom();
// remove zoom state
var params = get_params();
if (params.x) delete params.x;
if (params.y) delete params.y;
history.replaceState(null, null, parse_params(params));
}
else if (e.target.id == "search") search_prompt();
}, false)
// mouse-over for info
// show
window.addEventListener("mouseover", function(e) {
var target = find_group(e.target);
if (target) details.nodeValue = nametype + " " + g_to_text(target);
}, false)
// clear
window.addEventListener("mouseout", function(e) {
var target = find_group(e.target);
if (target) details.nodeValue = ' ';
}, false)
// ctrl-F for search
window.addEventListener("keydown",function (e) {
if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
e.preventDefault();
search_prompt();
}
}, false)
// functions
function get_params() {
var params = {};
var paramsarr = window.location.search.substr(1).split('&');
for (var i = 0; i < paramsarr.length; ++i) {
var tmp = paramsarr[i].split("=");
if (!tmp[0] || !tmp[1]) continue;
params[tmp[0]] = decodeURIComponent(tmp[1]);
}
return params;
}
function parse_params(params) {
var uri = "?";
for (var key in params) {
uri += key + '=' + encodeURIComponent(params[key]) + '&';
}
if (uri.slice(-1) == "&")
uri = uri.substring(0, uri.length - 1);
if (uri == '?')
uri = window.location.href.split('?')[0];
return uri;
}
function find_child(node, selector) {
var children = node.querySelectorAll(selector);
if (children.length) return children[0];
return;
}
function find_group(node) {
var parent = node.parentElement;
if (!parent) return;
if (parent.id == "frames") return node;
return find_group(parent);
}
function orig_save(e, attr, val) {
if (e.attributes["_orig_" + attr] != undefined) return;
if (e.attributes[attr] == undefined) return;
if (val == undefined) val = e.attributes[attr].value;
e.setAttribute("_orig_" + attr, val);
}
function orig_load(e, attr) {
if (e.attributes["_orig_"+attr] == undefined) return;
e.attributes[attr].value = e.attributes["_orig_" + attr].value;
e.removeAttribute("_orig_" + attr);
}
function g_to_text(e) {
var text = find_child(e, "title").firstChild.nodeValue;
return (text)
}
function g_to_func(e) {
var func = g_to_text(e);
// if there's any manipulation we want to do to the function
// name before it's searched, do it here before returning.
return (func);
}
function update_text(e) {
var r = find_child(e, "rect");
var t = find_child(e, "text");
var w = parseFloat(r.attributes.width.value) * frames.attributes.width.value / 100 - 3;
var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,"");
t.attributes.x.value = format_percent((parseFloat(r.attributes.x.value) + (100 * 3 / frames.attributes.width.value)));
// Smaller than this size won't fit anything
if (w < 2 * fontsize * fontwidth) {
t.textContent = "";
return;
}
t.textContent = txt;
// Fit in full text width
if (/^ *\$/.test(txt) || t.getComputedTextLength() < w)
return;
if (truncate_text_right) {
// Truncate the right side of the text.
for (var x = txt.length - 2; x > 0; x--) {
if (t.getSubStringLength(0, x + 2) <= w) {
t.textContent = txt.substring(0, x) + "..";
return;
}
}
} else {
// Truncate the left side of the text.
for (var x = 2; x < txt.length; x++) {
if (t.getSubStringLength(x - 2, txt.length) <= w) {
t.textContent = ".." + txt.substring(x, txt.length);
return;
}
}
}
t.textContent = "";
}
// zoom
function zoom_reset(e) {
if (e.attributes != undefined) {
orig_load(e, "x");
orig_load(e, "width");
}
if (e.childNodes == undefined) return;
for(var i = 0, c = e.childNodes; i < c.length; i++) {
zoom_reset(c[i]);
}
}
function zoom_child(e, x, ratio) {
if (e.attributes != undefined) {
if (e.attributes.x != undefined) {
orig_save(e, "x");
e.attributes.x.value = format_percent((parseFloat(e.attributes.x.value) - x) * ratio);
if (e.tagName == "text") {
e.attributes.x.value = format_percent(parseFloat(find_child(e.parentNode, "rect[x]").attributes.x.value) + (100 * 3 / frames.attributes.width.value));
}
}
if (e.attributes.width != undefined) {
orig_save(e, "width");
e.attributes.width.value = format_percent(parseFloat(e.attributes.width.value) * ratio);
}
}
if (e.childNodes == undefined) return;
for(var i = 0, c = e.childNodes; i < c.length; i++) {
zoom_child(c[i], x, ratio);
}
}
function zoom_parent(e) {
if (e.attributes) {
if (e.attributes.x != undefined) {
orig_save(e, "x");
e.attributes.x.value = "0.0%";
}
if (e.attributes.width != undefined) {
orig_save(e, "width");
e.attributes.width.value = "100.0%";
}
}
if (e.childNodes == undefined) return;
for(var i = 0, c = e.childNodes; i < c.length; i++) {
zoom_parent(c[i]);
}
}
function zoom(node) {
var attr = find_child(node, "rect").attributes;
var width = parseFloat(attr.width.value);
var xmin = parseFloat(attr.x.value);
var xmax = xmin + width;
var ymin = parseFloat(attr.y.value);
var ratio = 100 / width;
// XXX: Workaround for JavaScript float issues (fix me)
var fudge = 0.001;
unzoombtn.classList.remove("hide");
var el = frames.children;
for (var i = 0; i < el.length; i++) {
var e = el[i];
var a = find_child(e, "rect").attributes;
var ex = parseFloat(a.x.value);
var ew = parseFloat(a.width.value);
// Is it an ancestor
if (!inverted) {
var upstack = parseFloat(a.y.value) > ymin;
} else {
var upstack = parseFloat(a.y.value) < ymin;
}
if (upstack) {
// Direct ancestor
if (ex <= xmin && (ex+ew+fudge) >= xmax) {
e.classList.add("parent");
zoom_parent(e);
update_text(e);
}
// not in current path
else
e.classList.add("hide");
}
// Children maybe
else {
// no common path
if (ex < xmin || ex + fudge >= xmax) {
e.classList.add("hide");
}
else {
zoom_child(e, xmin, ratio);
update_text(e);
}
}
}
}
function unzoom() {
unzoombtn.classList.add("hide");
var el = frames.children;
for(var i = 0; i < el.length; i++) {
el[i].classList.remove("parent");
el[i].classList.remove("hide");
zoom_reset(el[i]);
update_text(el[i]);
}
}
// search
function reset_search() {
var el = document.querySelectorAll("#frames rect");
for (var i = 0; i < el.length; i++) {
orig_load(el[i], "fill")
}
var params = get_params();
delete params.s;
history.replaceState(null, null, parse_params(params));
}
function search_prompt() {
if (!searching) {
var term = prompt("Enter a search term (regexp " +
"allowed, eg: ^ext4_)", "");
if (term != null) {
search(term)
}
} else {
reset_search();
searching = 0;
searchbtn.classList.remove("show");
searchbtn.firstChild.nodeValue = "Search"
matchedtxt.classList.add("hide");
matchedtxt.firstChild.nodeValue = ""
}
}
function search(term) {
var re = new RegExp(term);
var el = frames.children;
var matches = new Object();
var maxwidth = 0;
for (var i = 0; i < el.length; i++) {
var e = el[i];
var func = g_to_func(e);
var rect = find_child(e, "rect");
if (func == null || rect == null)
continue;
// Save max width. Only works as we have a root frame
var w = parseFloat(rect.attributes.width.value);
if (w > maxwidth)
maxwidth = w;
if (func.match(re)) {
// highlight
var x = parseFloat(rect.attributes.x.value);
orig_save(rect, "fill");
rect.attributes.fill.value = searchcolor;
// remember matches
if (matches[x] == undefined) {
matches[x] = w;
} else {
if (w > matches[x]) {
// overwrite with parent
matches[x] = w;
}
}
searching = 1;
}
}
if (!searching)
return;
var params = get_params();
params.s = term;
history.replaceState(null, null, parse_params(params));
searchbtn.classList.add("show");
searchbtn.firstChild.nodeValue = "Reset Search";
// calculate percent matched, excluding vertical overlap
var count = 0;
var lastx = -1;
var lastw = 0;
var keys = Array();
for (k in matches) {
if (matches.hasOwnProperty(k))
keys.push(k);
}
// sort the matched frames by their x location
// ascending, then width descending
keys.sort(function(a, b){
return a - b;
});
// Step through frames saving only the biggest bottom-up frames
// thanks to the sort order. This relies on the tree property
// where children are always smaller than their parents.
var fudge = 0.0001; // JavaScript floating point
for (var k in keys) {
var x = parseFloat(keys[k]);
var w = matches[keys[k]];
if (x >= lastx + lastw - fudge) {
count += w;
lastx = x;
lastw = w;
}
}
// display matched percent
matchedtxt.classList.remove("hide");
var pct = 100 * count / maxwidth;
if (pct != 100) pct = pct.toFixed(1);
matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%";
}
function format_percent(n) {
return n.toFixed(4) + "%";
}
]]></script><rect x="0" y="0" width="100%" height="182" fill="url(#background)"/><text id="title" x="50.0000%" y="24.00">Flame Graph</text><text id="details" x="10" y="165.00"> </text><text id="unzoom" class="hide" x="10" y="24.00">Reset Zoom</text><text id="search" x="1090" y="24.00">Search</text><text id="matched" x="1090" y="165.00"> </text><svg id="frames" x="10" width="1180"><g><title>[libm-2.32.so] (9 samples, 26.47%)</title><rect x="58.8235%" y="53" width="26.4706%" height="15" fill="rgb(227,0,7)"/><text x="59.0735%" y="63.50">[libm-2.32.so]</text></g><g><title>__libc_start_main (29 samples, 85.29%)</title><rect x="11.7647%" y="85" width="85.2941%" height="15" fill="rgb(217,0,24)"/><text x="12.0147%" y="95.50">__libc_start_main</text></g><g><title>main (29 samples, 85.29%)</title><rect x="11.7647%" y="69" width="85.2941%" height="15" fill="rgb(221,193,54)"/><text x="12.0147%" y="79.50">main</text></g><g><title>rand_r (4 samples, 11.76%)</title><rect x="85.2941%" y="53" width="11.7647%" height="15" fill="rgb(248,212,6)"/><text x="85.5441%" y="63.50">rand_r</text></g><g><title>all (34 samples, 100%)</title><rect x="0.0000%" y="133" width="100.0000%" height="15" fill="rgb(208,68,35)"/><text x="0.2500%" y="143.50"></text></g><g><title>shuffle3-releas (34 samples, 100.00%)</title><rect x="0.0000%" y="117" width="100.0000%" height="15" fill="rgb(232,128,0)"/><text x="0.2500%" y="127.50">shuffle3-releas</text></g><g><title>_start (34 samples, 100.00%)</title><rect x="0.0000%" y="101" width="100.0000%" height="15" fill="rgb(207,160,47)"/><text x="0.2500%" y="111.50">_start</text></g><g><title>_dl_start (1 samples, 2.94%)</title><rect x="97.0588%" y="85" width="2.9412%" height="15" fill="rgb(228,23,34)"/><text x="97.3088%" y="95.50">_d..</text></g><g><title>_dl_sysdep_start (1 samples, 2.94%)</title><rect x="97.0588%" y="69" width="2.9412%" height="15" fill="rgb(218,30,26)"/><text x="97.3088%" y="79.50">_d..</text></g><g><title>dl_main (1 samples, 2.94%)</title><rect x="97.0588%" y="53" width="2.9412%" height="15" fill="rgb(220,122,19)"/><text x="97.3088%" y="63.50">dl..</text></g><g><title>_dl_lookup_symbol_x (1 samples, 2.94%)</title><rect x="97.0588%" y="37" width="2.9412%" height="15" fill="rgb(250,228,42)"/><text x="97.3088%" y="47.50">_d..</text></g></svg></svg>

After

Width:  |  Height:  |  Size: 17 KiB

@ -0,0 +1,419 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" width="1200" height="214" onload="init(evt)" viewBox="0 0 1200 214" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Flame graph stack visualization. See https://github.com/brendangregg/FlameGraph for latest version, and http://www.brendangregg.com/flamegraphs.html for examples.--><!--NOTES: --><defs><linearGradient id="background" y1="0" y2="1" x1="0" x2="0"><stop stop-color="#eeeeee" offset="5%"/><stop stop-color="#eeeeb0" offset="95%"/></linearGradient></defs><style type="text/css">
text { font-family:"Verdana"; font-size:12px; fill:rgb(0,0,0); }
#title { text-anchor:middle; font-size:17px; }
#search { opacity:0.1; cursor:pointer; }
#search:hover, #search.show { opacity:1; }
#subtitle { text-anchor:middle; font-color:rgb(160,160,160); }
#unzoom { cursor:pointer; }
#frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; }
.hide { display:none; }
.parent { opacity:0.5; }
</style><script type="text/ecmascript"><![CDATA[var nametype = 'Function:';
var fontsize = 12;
var fontwidth = 0.59;
var xpad = 10;
var inverted = false;
var searchcolor = 'rgb(230,0,230)';
var fluiddrawing = true;
var truncate_text_right = false;]]><![CDATA["use strict";
var details, searchbtn, unzoombtn, matchedtxt, svg, searching, frames;
function init(evt) {
details = document.getElementById("details").firstChild;
searchbtn = document.getElementById("search");
unzoombtn = document.getElementById("unzoom");
matchedtxt = document.getElementById("matched");
svg = document.getElementsByTagName("svg")[0];
frames = document.getElementById("frames");
searching = 0;
// Use GET parameters to restore a flamegraph's state.
var restore_state = function() {
var params = get_params();
if (params.x && params.y)
zoom(find_group(document.querySelector('[x="' + params.x + '"][y="' + params.y + '"]')));
if (params.s)
search(params.s);
};
if (fluiddrawing) {
// Make width dynamic so the SVG fits its parent's width.
svg.removeAttribute("width");
// Edge requires us to have a viewBox that gets updated with size changes.
var isEdge = /Edge\/\d./i.test(navigator.userAgent);
if (!isEdge) {
svg.removeAttribute("viewBox");
}
var update_for_width_change = function() {
if (isEdge) {
svg.attributes.viewBox.value = "0 0 " + svg.width.baseVal.value + " " + svg.height.baseVal.value;
}
// Keep consistent padding on left and right of frames container.
frames.attributes.width.value = svg.width.baseVal.value - xpad * 2;
// Text truncation needs to be adjusted for the current width.
var el = frames.children;
for(var i = 0; i < el.length; i++) {
update_text(el[i]);
}
// Keep search elements at a fixed distance from right edge.
var svgWidth = svg.width.baseVal.value;
searchbtn.attributes.x.value = svgWidth - xpad - 100;
matchedtxt.attributes.x.value = svgWidth - xpad - 100;
};
window.addEventListener('resize', function() {
update_for_width_change();
});
// This needs to be done asynchronously for Safari to work.
setTimeout(function() {
unzoom();
update_for_width_change();
restore_state();
}, 0);
} else {
restore_state();
}
}
// event listeners
window.addEventListener("click", function(e) {
var target = find_group(e.target);
if (target) {
if (target.nodeName == "a") {
if (e.ctrlKey === false) return;
e.preventDefault();
}
if (target.classList.contains("parent")) unzoom();
zoom(target);
// set parameters for zoom state
var el = target.querySelector("rect");
if (el && el.attributes && el.attributes.y && el.attributes._orig_x) {
var params = get_params()
params.x = el.attributes._orig_x.value;
params.y = el.attributes.y.value;
history.replaceState(null, null, parse_params(params));
}
}
else if (e.target.id == "unzoom") {
unzoom();
// remove zoom state
var params = get_params();
if (params.x) delete params.x;
if (params.y) delete params.y;
history.replaceState(null, null, parse_params(params));
}
else if (e.target.id == "search") search_prompt();
}, false)
// mouse-over for info
// show
window.addEventListener("mouseover", function(e) {
var target = find_group(e.target);
if (target) details.nodeValue = nametype + " " + g_to_text(target);
}, false)
// clear
window.addEventListener("mouseout", function(e) {
var target = find_group(e.target);
if (target) details.nodeValue = ' ';
}, false)
// ctrl-F for search
window.addEventListener("keydown",function (e) {
if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
e.preventDefault();
search_prompt();
}
}, false)
// functions
function get_params() {
var params = {};
var paramsarr = window.location.search.substr(1).split('&');
for (var i = 0; i < paramsarr.length; ++i) {
var tmp = paramsarr[i].split("=");
if (!tmp[0] || !tmp[1]) continue;
params[tmp[0]] = decodeURIComponent(tmp[1]);
}
return params;
}
function parse_params(params) {
var uri = "?";
for (var key in params) {
uri += key + '=' + encodeURIComponent(params[key]) + '&';
}
if (uri.slice(-1) == "&")
uri = uri.substring(0, uri.length - 1);
if (uri == '?')
uri = window.location.href.split('?')[0];
return uri;
}
function find_child(node, selector) {
var children = node.querySelectorAll(selector);
if (children.length) return children[0];
return;
}
function find_group(node) {
var parent = node.parentElement;
if (!parent) return;
if (parent.id == "frames") return node;
return find_group(parent);
}
function orig_save(e, attr, val) {
if (e.attributes["_orig_" + attr] != undefined) return;
if (e.attributes[attr] == undefined) return;
if (val == undefined) val = e.attributes[attr].value;
e.setAttribute("_orig_" + attr, val);
}
function orig_load(e, attr) {
if (e.attributes["_orig_"+attr] == undefined) return;
e.attributes[attr].value = e.attributes["_orig_" + attr].value;
e.removeAttribute("_orig_" + attr);
}
function g_to_text(e) {
var text = find_child(e, "title").firstChild.nodeValue;
return (text)
}
function g_to_func(e) {
var func = g_to_text(e);
// if there's any manipulation we want to do to the function
// name before it's searched, do it here before returning.
return (func);
}
function update_text(e) {
var r = find_child(e, "rect");
var t = find_child(e, "text");
var w = parseFloat(r.attributes.width.value) * frames.attributes.width.value / 100 - 3;
var txt = find_child(e, "title").textContent.replace(/\([^(]*\)$/,"");
t.attributes.x.value = format_percent((parseFloat(r.attributes.x.value) + (100 * 3 / frames.attributes.width.value)));
// Smaller than this size won't fit anything
if (w < 2 * fontsize * fontwidth) {
t.textContent = "";
return;
}
t.textContent = txt;
// Fit in full text width
if (/^ *\$/.test(txt) || t.getComputedTextLength() < w)
return;
if (truncate_text_right) {
// Truncate the right side of the text.
for (var x = txt.length - 2; x > 0; x--) {
if (t.getSubStringLength(0, x + 2) <= w) {
t.textContent = txt.substring(0, x) + "..";
return;
}
}
} else {
// Truncate the left side of the text.
for (var x = 2; x < txt.length; x++) {
if (t.getSubStringLength(x - 2, txt.length) <= w) {
t.textContent = ".." + txt.substring(x, txt.length);
return;
}
}
}
t.textContent = "";
}
// zoom
function zoom_reset(e) {
if (e.attributes != undefined) {
orig_load(e, "x");
orig_load(e, "width");
}
if (e.childNodes == undefined) return;
for(var i = 0, c = e.childNodes; i < c.length; i++) {
zoom_reset(c[i]);
}
}
function zoom_child(e, x, ratio) {
if (e.attributes != undefined) {
if (e.attributes.x != undefined) {
orig_save(e, "x");
e.attributes.x.value = format_percent((parseFloat(e.attributes.x.value) - x) * ratio);
if (e.tagName == "text") {
e.attributes.x.value = format_percent(parseFloat(find_child(e.parentNode, "rect[x]").attributes.x.value) + (100 * 3 / frames.attributes.width.value));
}
}
if (e.attributes.width != undefined) {
orig_save(e, "width");
e.attributes.width.value = format_percent(parseFloat(e.attributes.width.value) * ratio);
}
}
if (e.childNodes == undefined) return;
for(var i = 0, c = e.childNodes; i < c.length; i++) {
zoom_child(c[i], x, ratio);
}
}
function zoom_parent(e) {
if (e.attributes) {
if (e.attributes.x != undefined) {
orig_save(e, "x");
e.attributes.x.value = "0.0%";
}
if (e.attributes.width != undefined) {
orig_save(e, "width");
e.attributes.width.value = "100.0%";
}
}
if (e.childNodes == undefined) return;
for(var i = 0, c = e.childNodes; i < c.length; i++) {
zoom_parent(c[i]);
}
}
function zoom(node) {
var attr = find_child(node, "rect").attributes;
var width = parseFloat(attr.width.value);
var xmin = parseFloat(attr.x.value);
var xmax = xmin + width;
var ymin = parseFloat(attr.y.value);
var ratio = 100 / width;
// XXX: Workaround for JavaScript float issues (fix me)
var fudge = 0.001;
unzoombtn.classList.remove("hide");
var el = frames.children;
for (var i = 0; i < el.length; i++) {
var e = el[i];
var a = find_child(e, "rect").attributes;
var ex = parseFloat(a.x.value);
var ew = parseFloat(a.width.value);
// Is it an ancestor
if (!inverted) {
var upstack = parseFloat(a.y.value) > ymin;
} else {
var upstack = parseFloat(a.y.value) < ymin;
}
if (upstack) {
// Direct ancestor
if (ex <= xmin && (ex+ew+fudge) >= xmax) {
e.classList.add("parent");
zoom_parent(e);
update_text(e);
}
// not in current path
else
e.classList.add("hide");
}
// Children maybe
else {
// no common path
if (ex < xmin || ex + fudge >= xmax) {
e.classList.add("hide");
}
else {
zoom_child(e, xmin, ratio);
update_text(e);
}
}
}
}
function unzoom() {
unzoombtn.classList.add("hide");
var el = frames.children;
for(var i = 0; i < el.length; i++) {
el[i].classList.remove("parent");
el[i].classList.remove("hide");
zoom_reset(el[i]);
update_text(el[i]);
}
}
// search
function reset_search() {
var el = document.querySelectorAll("#frames rect");
for (var i = 0; i < el.length; i++) {
orig_load(el[i], "fill")
}
var params = get_params();
delete params.s;
history.replaceState(null, null, parse_params(params));
}
function search_prompt() {
if (!searching) {
var term = prompt("Enter a search term (regexp " +
"allowed, eg: ^ext4_)", "");
if (term != null) {
search(term)
}
} else {
reset_search();
searching = 0;
searchbtn.classList.remove("show");
searchbtn.firstChild.nodeValue = "Search"
matchedtxt.classList.add("hide");
matchedtxt.firstChild.nodeValue = ""
}
}
function search(term) {
var re = new RegExp(term);
var el = frames.children;
var matches = new Object();
var maxwidth = 0;
for (var i = 0; i < el.length; i++) {
var e = el[i];
var func = g_to_func(e);
var rect = find_child(e, "rect");
if (func == null || rect == null)
continue;
// Save max width. Only works as we have a root frame
var w = parseFloat(rect.attributes.width.value);
if (w > maxwidth)
maxwidth = w;
if (func.match(re)) {
// highlight
var x = parseFloat(rect.attributes.x.value);
orig_save(rect, "fill");
rect.attributes.fill.value = searchcolor;
// remember matches
if (matches[x] == undefined) {
matches[x] = w;
} else {
if (w > matches[x]) {
// overwrite with parent
matches[x] = w;
}
}
searching = 1;
}
}
if (!searching)
return;
var params = get_params();
params.s = term;
history.replaceState(null, null, parse_params(params));
searchbtn.classList.add("show");
searchbtn.firstChild.nodeValue = "Reset Search";
// calculate percent matched, excluding vertical overlap
var count = 0;
var lastx = -1;
var lastw = 0;
var keys = Array();
for (k in matches) {
if (matches.hasOwnProperty(k))
keys.push(k);
}
// sort the matched frames by their x location
// ascending, then width descending
keys.sort(function(a, b){
return a - b;
});
// Step through frames saving only the biggest bottom-up frames
// thanks to the sort order. This relies on the tree property
// where children are always smaller than their parents.
var fudge = 0.0001; // JavaScript floating point
for (var k in keys) {
var x = parseFloat(keys[k]);
var w = matches[keys[k]];
if (x >= lastx + lastw - fudge) {
count += w;
lastx = x;
lastw = w;
}
}
// display matched percent
matchedtxt.classList.remove("hide");
var pct = 100 * count / maxwidth;
if (pct != 100) pct = pct.toFixed(1);
matchedtxt.firstChild.nodeValue = "Matched: " + pct + "%";
}
function format_percent(n) {
return n.toFixed(4) + "%";
}
]]></script><rect x="0" y="0" width="100%" height="214" fill="url(#background)"/><text id="title" x="50.0000%" y="24.00">Flame Graph</text><text id="details" x="10" y="197.00"> </text><text id="unzoom" class="hide" x="10" y="24.00">Reset Zoom</text><text id="search" x="1090" y="24.00">Search</text><text id="matched" x="1090" y="197.00"> </text><svg id="frames" x="10" width="1180"><g><title>VTT for std::basic_ostream&lt;wchar_t, std::char_traits&lt;wchar_t&gt; &gt; (1 samples, 1.89%)</title><rect x="0.0000%" y="133" width="1.8868%" height="15" fill="rgb(227,0,7)"/><text x="0.2500%" y="143.50">V..</text></g><g><title>std::vector&lt;unsigned long, std::allocator&lt;unsigned long&gt; &gt;::emplace_back&lt;unsigned long&gt; (1 samples, 1.89%)</title><rect x="0.0000%" y="117" width="1.8868%" height="15" fill="rgb(217,0,24)"/><text x="0.2500%" y="127.50">s..</text></g><g><title>[libm-2.32.so] (1 samples, 1.89%)</title><rect x="1.8868%" y="117" width="1.8868%" height="15" fill="rgb(221,193,54)"/><text x="2.1368%" y="127.50">[..</text></g><g><title>main (1 samples, 1.89%)</title><rect x="3.7736%" y="117" width="1.8868%" height="15" fill="rgb(248,212,6)"/><text x="4.0236%" y="127.50">m..</text></g><g><title>[unknown] (4 samples, 7.55%)</title><rect x="1.8868%" y="133" width="7.5472%" height="15" fill="rgb(208,68,35)"/><text x="2.1368%" y="143.50">[unknown]</text></g><g><title>std::vector&lt;unsigned long, std::allocator&lt;unsigned long&gt; &gt;::emplace_back&lt;unsigned long&gt; (2 samples, 3.77%)</title><rect x="5.6604%" y="117" width="3.7736%" height="15" fill="rgb(232,128,0)"/><text x="5.9104%" y="127.50">std:..</text></g><g><title>[libm-2.32.so] (23 samples, 43.40%)</title><rect x="45.2830%" y="85" width="43.3962%" height="15" fill="rgb(207,160,47)"/><text x="45.5330%" y="95.50">[libm-2.32.so]</text></g><g><title>std::vector&lt;unsigned long, std::allocator&lt;unsigned long&gt; &gt;::emplace_back&lt;unsigned long&gt; (4 samples, 7.55%)</title><rect x="88.6792%" y="85" width="7.5472%" height="15" fill="rgb(228,23,34)"/><text x="88.9292%" y="95.50">std::vecto..</text></g><g><title>__memmove_avx_unaligned_erms (1 samples, 1.89%)</title><rect x="94.3396%" y="69" width="1.8868%" height="15" fill="rgb(218,30,26)"/><text x="94.5896%" y="79.50">_..</text></g><g><title>__libc_start_main (43 samples, 81.13%)</title><rect x="16.9811%" y="117" width="81.1321%" height="15" fill="rgb(220,122,19)"/><text x="17.2311%" y="127.50">__libc_start_main</text></g><g><title>main (43 samples, 81.13%)</title><rect x="16.9811%" y="101" width="81.1321%" height="15" fill="rgb(250,228,42)"/><text x="17.2311%" y="111.50">main</text></g><g><title>std::vector&lt;unsigned long, std::allocator&lt;unsigned long&gt; &gt;::vector (1 samples, 1.89%)</title><rect x="96.2264%" y="85" width="1.8868%" height="15" fill="rgb(240,193,28)"/><text x="96.4764%" y="95.50">s..</text></g><g><title>__memset_avx2_erms (1 samples, 1.89%)</title><rect x="96.2264%" y="69" width="1.8868%" height="15" fill="rgb(216,20,37)"/><text x="96.4764%" y="79.50">_..</text></g><g><title>all (53 samples, 100%)</title><rect x="0.0000%" y="165" width="100.0000%" height="15" fill="rgb(206,188,39)"/><text x="0.2500%" y="175.50"></text></g><g><title>shuffle3-releas (53 samples, 100.00%)</title><rect x="0.0000%" y="149" width="100.0000%" height="15" fill="rgb(217,207,13)"/><text x="0.2500%" y="159.50">shuffle3-releas</text></g><g><title>_start (48 samples, 90.57%)</title><rect x="9.4340%" y="133" width="90.5660%" height="15" fill="rgb(231,73,38)"/><text x="9.6840%" y="143.50">_start</text></g><g><title>_dl_start (1 samples, 1.89%)</title><rect x="98.1132%" y="117" width="1.8868%" height="15" fill="rgb(225,20,46)"/><text x="98.3632%" y="127.50">_..</text></g><g><title>_dl_sysdep_start (1 samples, 1.89%)</title><rect x="98.1132%" y="101" width="1.8868%" height="15" fill="rgb(210,31,41)"/><text x="98.3632%" y="111.50">_..</text></g><g><title>dl_main (1 samples, 1.89%)</title><rect x="98.1132%" y="85" width="1.8868%" height="15" fill="rgb(221,200,47)"/><text x="98.3632%" y="95.50">d..</text></g><g><title>_dl_init_paths (1 samples, 1.89%)</title><rect x="98.1132%" y="69" width="1.8868%" height="15" fill="rgb(226,26,5)"/><text x="98.3632%" y="79.50">_..</text></g><g><title>fillin_rpath.isra.0 (1 samples, 1.89%)</title><rect x="98.1132%" y="53" width="1.8868%" height="15" fill="rgb(249,33,26)"/><text x="98.3632%" y="63.50">f..</text></g><g><title>__strsep (1 samples, 1.89%)</title><rect x="98.1132%" y="37" width="1.8868%" height="15" fill="rgb(235,183,28)"/><text x="98.3632%" y="47.50">_..</text></g></svg></svg>

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

@ -1,17 +1,85 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <debug.h>
#include <colours.h>
enum jt_comp {
JT_COMP_UNIX,
JT_COMP_DATE,
JT_COMP_TIME,
JT_COMP_DATETIME,
};
#define CLONE(p) ({ __auto_type $__p = (p); \
__auto_type $__r = __builtin_choose_expr(__builtin_classify_type($__p) == 5, *$__p, $__p); \
/* ((__typeof__($__r) []) { $__r }); }) */ \
__typeof__($__r) $__rp[1]; \
$__rp[0] = $__r; \
&$__rp[0]; })
__attribute__((returns_nonnull, nonnull(1)))
static inline
const char* jtime(const struct tm* restrict lt, enum jt_comp kind)
{
_Thread_local static char datebuf[1
+ 4 + 2 + 4 + 1 // `dd/mm/yyyy`
+ 6 + 3 // ` hh:MM:ss`
];
_Static_assert(sizeof(datebuf) != sizeof(char*), "Unexpected decay");
//memset(datebuf, '0', sizeof(datebuf)-1);
if(!kind)
snprintf(datebuf, sizeof(datebuf), "%lu", mktime(CLONE(lt)));
else {
size_t w = 0, sz = sizeof(datebuf);
if(kind & JT_COMP_DATE)
w += snprintf(datebuf, sz, "%02u/%02u/%4.4u"
, (unsigned)lt->tm_mday
, (unsigned)lt->tm_mon
, (unsigned)(1900 + lt->tm_year));
if(kind & JT_COMP_TIME)
w += snprintf(datebuf+w, sz - w, " %02u:%02u:%02u"
, (unsigned)lt->tm_hour
, (unsigned)lt->tm_min
, (unsigned)lt->tm_sec);
}
return datebuf;
}
void _do_dprintf(struct debuginfo info, const char* fmt, ...)
{
#ifdef DEBUG
va_list li;
time_t utime;
struct tm ltime;
time(&utime);
localtime_r(&utime, &ltime);
va_start(li, fmt);
fprintf(stderr, "["FGRN("dbg")" " FYEL("@") BOLD("%s") "->" FWHT("%s") ":" FYEL("%d") "]: ", info.file, info.function,info.line);
#ifdef D_TRACE
#define FMT_FUNC "%s"
#else
#define FMT_FUNC "%s()"
#endif
#define TTY_OUT "["FGRN("dbg")" (%s %s) " FYEL("@") BOLD("%s") "->" FWHT(FMT_FUNC) ":" FYEL("%d") "]: "
#define NCOL(x) x
#define FIL_OUT "["NCOL("dbg")" (%s %s) " NCOL("@") NCOL("%s") "->" NCOL(FMT_FUNC) ":" NCOL("%d") "]: "
fprintf(stderr, isatty(fileno(stderr)) ? (TTY_OUT) : (FIL_OUT)
, jtime(&ltime, JT_COMP_DATETIME), ltime.tm_zone
, info.file
, info.function
, info.line);
vfprintf(stderr, fmt, li);
fprintf(stderr, "\n");
va_end(li);

@ -0,0 +1,67 @@
#include <unistd.h>
#include <fsvec.h>
#include <panic.h>
int fvec_new(fvec_t* restrict obj, const char* path)
{
obj->backing = fopen(path, "w+b");
if(!obj->backing) {
perror("fvec_new(): Failed to open file");
return 0;
}
obj->len=0;
return 1;
}
void fvec_close(fvec_t* restrict obj)
{
if(obj->backing) fclose(obj->backing);
obj->backing=NULL;
obj->len=0;
}
int fvec_pop_end(fvec_t* restrict obj, size_t sz)
{
if(!obj->len) return 0;
if(ftruncate(fileno(obj->backing), (obj->len-=sz))<0) {
perror("Failed to pop buffer");
return 0;
}
return 1;
}
void fvec_push_whole_buffer(fvec_t* restrict obj, const void* _buffer, size_t sz)
{
size_t w= 0;
size_t c;
const unsigned char* buffer = _buffer;
while ( w<sz && (c=fwrite(buffer+w, 1, sz-w, obj->backing))>0 ) w+=c;
if(w!=sz) {
perror("Corrupted buffer state, aborting");
panic("Cannot continue");
}
obj->len += sz;
}
int fvec_get_whole_buffer(const fvec_t* restrict obj, void* _buffer, size_t _sz)
{
ssize_t sz = (ssize_t)_sz;
if(_sz != (size_t)sz) panic("Buffer size %lu too large", _sz);
if(obj->len<(size_t)sz) return 0;
if(fseek(obj->backing, -sz, SEEK_END)<0) {
perror("fvec_get_whole_buffer() failed to seek");
return 0;
}
size_t w=0;
ssize_t c;
unsigned char* buffer = _buffer;
while ( w<_sz && (c=fread(buffer+w, 1, sz-w, obj->backing))>0 ) w+=c;
if (w!=_sz) {
perror("Corrupted buffer state, aborting");
panic("Cannot continue on FD %d (%p)", fileno(obj->backing), obj->backing);
}
return 1;
}

@ -0,0 +1,73 @@
#include <filesystem>
#include <fsvec.hpp>
#include <uuid.hpp>
#include <fsvec.h>
#include <debug.h>
#include <tempfile.hpp>
#define FB file_back_buffer
struct FB::impl
{
size_t cap;
temp_file file;
fvec_t backing;
};
FB::FB(size_t cap) : inner(std::make_unique<FB::impl>())
{
// Set cap
inner->cap = cap;
// Create then open `inner->file`
if(!fvec_new(&inner->backing, inner->file->c_str())) panic("Failed to open backing for temp file buffer");
}
FB::FB(file_back_buffer&& m) : inner(std::move(m.inner)){}
FB::FB() : FB(DEFAULT_CAP){}
FB::~FB()
{
fvec_close(&inner->backing);
}
void FB::push_buf(byte* buf, size_t len)
{
fvec_push_whole_buffer(&inner->backing, (void*)buf, len);
}
bool FB::back(byte* buf, size_t len) const
{
return !!fvec_get_whole_buffer(&inner->backing, (void*)buf, len);
}
bool FB::pop_n(size_t len)
{
return !!fvec_pop_end(&inner->backing, len);
}
extern "C" void _fb_run_tests()
{
fixed_spill_vector<int, 8> test;
int r0,r1=0;
for(int i=0;i<10;i++) {
D_dprintf("push: %d", (10-i));
test.push_back(10-i);
r1+= (10-i);
}
D_dprintf("r1: %d", r1);
r0=0;
while(test.size())
{
r0+=test.back();
D_dprintf("back: %d", test.back());
test.pop_back();
}
D_dprintf("r0: %d", r0);
if(r0!=r1) panic("fb failed test: %d, %d", r0, r1);
D_dprintf("test successful");
}

@ -19,6 +19,8 @@ _Static_assert(sizeof(float)==sizeof(uint32_t), "float is not 32 bits");
const char* _prog_name;
extern void _fb_run_tests();
noreturn help_then_exit()
{
fprintf(stderr, "Try passing `-h`\n");
@ -41,6 +43,7 @@ int main(int argc, char** argv)
{
_prog_name = argv[0];
//_fb_run_tests();
work_args_t parsed;
if( !argv[1] || *(argv[1]) != '-') help_then_exit();

@ -12,6 +12,51 @@
#include <map.h>
__attribute__((pure, nonnull(1)))
static inline int _map(mmap_t* restrict map)
{
//TODO: Based on map->len, choose (or calculate) appropriate `MAP_HUGE_*` flag
return (map->ptr = mmap(NULL, map->len, PROT_READ | PROT_WRITE, MAP_SHARED, map->fd, 0)) != MAP_FAILED;
}
//#define _map(...) _map(__VA_ARGS__, 0)
int map_advise_rand(mmap_t* restrict ptr, int need)
{
int flags = MADV_RANDOM | (need ? MADV_WILLNEED : 0);
if(madvise(ptr->ptr, ptr->len, flags)) {
perror("madvise() failed");
return 0;
}
return 1;
}
int map_raw_fd(int fd, mmap_t* restrict ptr, const size_t sz[static 1])
{
if(fd < 0) return 0;
struct stat st;
if(sz && ftruncate(fd, *sz)) {
perror("Failed to allocate");
return 0;
} else if(fstat(fd, &st) < 0) {
perror("Failed to stat file");
return 0;
}
struct mmap map = { .fd = fd, .ptr = NULL, .len = sz ? *sz : st.st_size };
if (!_map(&map)) {
perror("mmap() failed");
return 0;
}
*ptr = map;
return 1;
}
int open_and_map(const char* file, mmap_t* restrict ptr)
{
int fd;
@ -27,9 +72,9 @@ int open_and_map(const char* file, mmap_t* restrict ptr)
return 0;
}
register struct mmap map = { .fd = fd, .ptr = NULL, .len = st.st_size };
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) {
if (!_map(&map)) {
perror("mmap() failed");
close(fd);
return 0;
@ -40,9 +85,41 @@ int open_and_map(const char* file, mmap_t* restrict ptr)
return 1;
}
int unmap_and_close(mmap_t map)
int open_and_alloc(const char* file, mmap_t* restrict ptr, size_t sz)
{
int fd;
if ((fd = open(file, O_CREAT | O_RDWR, FILEMODE)) < 0) {
perror("Failed to open file");
return 0;
}
if(ftruncate(fd, sz))
{
perror("Failed to allocate");
close(fd);
return 0;
}
struct mmap map = { .fd = fd, .ptr = NULL, .len = sz };
if (!_map(&map)) {
perror("mmap() failed");
close(fd);
return 0;
}
*ptr = map;
return 1;
}
inline
int unmap_and_close_s(mmap_t map, int flags)
{
register int rval=1;
if (map.fd && msync(map.ptr, map.len, flags))
perror("msync() failed");
if (munmap(map.ptr, map.len) < 0) {
perror("munmap() failed");
rval=0;
@ -54,3 +131,41 @@ int unmap_and_close(mmap_t map)
return rval;
}
int unmap_and_close(mmap_t map)
{
return unmap_and_close_s(map, MS_SYNC /* | MS_INVALIDATE XXX: I don't think this is needed, we son't be using the mapping anymore*/);
}
__attribute__((nonnull(1)))
int dup_map(const mmap_t *in, mmap_t* restrict ptr, const size_t *new_size, enum map_flags flags)
{
int fd;
if ( (fd = dup(in->fd)) < 0 )
{
perror("dup() failed");
return 0;
}
size_t sz;
if(new_size) {
if(ftruncate(fd, sz = *new_size))
{
perror("Failed to set cloned map size");
close(fd);
return 0;
}
} else sz = in->len;
struct mmap map = { .fd = fd, .ptr = NULL, .len = sz };
if ((map.ptr = mmap(NULL, map.len, PROT_READ | (flags & MMF_READONLY ? 0 : PROT_WRITE), (flags & MMF_PRIVATE) ? MAP_PRIVATE : MAP_SHARED,fd, 0)) == MAP_FAILED) {
perror("mmap() failed");
close(fd);
return 0;
}
*ptr = map;
return 1;
}

@ -0,0 +1,31 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <panic.h>
#include <map.h>
namespace {
constexpr inline auto MEMFD_DEFAULT_FLAGS = MFD_CLOEXEC;
int memfd_alloc(const char* name, size_t size = 0, int flags = MEMFD_DEFAULT_FLAGS)
{
int fd = memfd_create(name, flags);
if(fd < 0) panic("memfd_create() failed");
if(size)
if(ftruncate(fd, size)) { close(fd); panic("ftruncate() failed"); }
return fd;
}
}
namespace mm {
vmap::vmap()
: mmap(create_raw_fd(memfd_alloc(__PRETTY_FUNCTION__))) {}
vmap::vmap(size_t sz)
: mmap(create_raw_fd(memfd_alloc(__PRETTY_FUNCTION__, sz))) {}
}

@ -7,5 +7,11 @@
extern "C" void* map_and_then(const char* file, map_cb callback, void* user)
{
mm::mmap map(file);
return callback(map.as_raw(), user);
return callback(std::as_const(map).as_raw(), user);
}
extern "C" void* map_fd_and_then(int fd, map_cb callback, void* user)
{
auto map = mm::mmap::map_raw_fd(fd);
return callback(std::as_const(map).as_raw(), user);
}

@ -0,0 +1,12 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
int _can_allocate(size_t bytes)
{
void* shim = malloc(bytes);
if(shim) {
free(shim);
return 1;
} else return 0;
}

@ -1,18 +1,41 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <panic.h>
#include <colours.h>
__attribute__((noreturn)) void _do_panic(struct panicinfo info, const char* fmt, ...)
#ifndef DEFAULT_PANIC_HANDLER
#if __cpp_exceptions || 1 /* XXX: Cannot detect this here */
# define DEFAULT_PANIC_HANDLER "_panic__start_unwind_cxx"
#else
# define DEFAULT_PANIC_HANDLER "_panic__start_unwind_abort"
#endif
#endif
extern void _panic__start_unwind_cxx(void);
static void _panic__start_unwind_abort(void*)
__attribute__((weakref("abort"), noreturn));
static void _panic__start_unwind(void* payload)
__attribute__((noreturn, weakref(DEFAULT_PANIC_HANDLER)));
__attribute__((noreturn, weak)) 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);
#define FMT_TTY BOLD(UNDL(FRED("[!]"))) " (" BOLD("%s") "->" BOLD(FRED("%s")) ":" FYEL("%d") ") " BOLD(FRED("fatal error")) ": "
#define FMT_FIL "[!]" " (" "%s" "->" "%s" ":" "%d" ") " "fatal error" ": "
fprintf(stderr, isatty(fileno(stderr))
? FMT_TTY
: FMT_FIL , info.file, info.function, info.line);
vfprintf(stderr, fmt, li);
fprintf(stderr, "\n");
va_end(li);
abort();
_panic__start_unwind(&info);
__builtin_unreachable();
}

@ -0,0 +1,47 @@
#include <utility>
#include <stdexcept>
#include <cstdlib>
extern "C" {
struct ErrorPayload {};
}
struct FatalError {
FatalError(void* payload)
: payload(payload) {}
FatalError(FatalError&& m)
: payload(std::exchange(m.payload, nullptr)) {}
FatalError& operator=(FatalError&& m)
{
if(this != &m) {
if(payload) destroy();
payload = std::exchange(m.payload, nullptr);
} return *this;
}
virtual ~FatalError() noexcept {
destroy();
}
protected:
virtual void destroy() noexcept
{
/*if(auto* p = dynamic_cast<ErrorPayload*>(payload))
delete p;*/
}
public:
void* payload;
};
extern "C" {
[[noreturn, gnu::weak]]
void _panic__start_unwind_cxx(void* payload)
{
#if __EXCEPTIONS
throw FatalError(payload);
#else
::abort();
#endif
}
}

@ -0,0 +1,135 @@
#include <string>
#include <vector>
#include <fmt/format.h>
#include <sys/ioctl.h>
#include <perc.h>
using fract_t = pr::Progress::fract_t;
namespace {
using namespace pr;
size_t twidth(int fd = STDIN_FILENO, size_t orr = Progress::DEFAULT_WIDTH) noexcept
{
struct winsize w;
if(ioctl(fd, TIOCGWINSZ, &w) == -1) return orr;
return size_t(w.ws_col);
}
size_t def_bar_width(size_t tw) noexcept
{
return size_t(std::round(Progress::DEFAULT_TERM_FRACT * fract_t(tw)));
}
}
namespace pr {
struct Progress::_impl {
Progress::fract_t fract; // 0..=1
FILE* output;
size_t width;
std::string aux;
};
size_t& Progress::width() noexcept { return inner_->width; }
size_t Progress::width() const noexcept { return inner_->width; }
fract_t& Progress::fraction() noexcept { return inner_->fract; }
fract_t Progress::fraction() const noexcept { return inner_->fract; }
fract_t Progress::percentage() const noexcept { return inner_->fract * 100.0l; }
void Progress::percentage(fract_t fract) noexcept { inner_->fract = fract / 100.0l; }
std::string& Progress::tag() noexcept { return inner_->aux; }
const std::string& Progress::tag() const noexcept { return inner_->aux; }
FILE* Progress::output() const noexcept { return inner_->output; }
FILE*& Progress::output() noexcept { return inner_->output; }
void Progress::render(bool flush) const noexcept
{
constinit thread_local static std::vector<char> buffer{};
const auto& inner = *inner_;
FILE* out = inner.output;
auto wf = fract_t(inner.width) * inner.fract;
auto perc = percentage();
size_t cf = size_t(std::floor(wf));
//auto print = []<typename F> (F f, auto&&... args) {
#define print(...) fmt::format_to(std::back_inserter(buffer), __VA_ARGS__);
//};
buffer.clear();
// Render bar
buffer.push_back('[');
for(size_t i=0;i<inner.width;i++)
if(i<=cf) buffer.push_back( '#' );
else buffer.push_back( ' ' );
buffer.push_back( ']' );
print(": {:00.2}%", perc);
if(inner.aux.size())
print(" ({})", std::string_view(inner.aux));
// Print bar
fprintf(out, "\r%.*s", int(buffer.size() & INT_MAX), static_cast<const char*>(buffer.data()));
// Flush output stream
if(flush) fflush(out);
#undef print
}
void Progress::spin(increment_t by, bool r, bool f) noexcept
{
auto& inner = *inner_;
inner.fract=by;
if(inner.fract > 1.0l)
inner.fract=1.0l;
if(r) this->render(f);
}
[[gnu::nonnull(1)]]
Progress::Progress(FILE* p) noexcept
: inner_(std::make_unique<_impl>(0, p, def_bar_width(twidth(fileno(p))) )) {}
Progress::Progress(const Progress& p) noexcept
: inner_(p.inner_ ? std::make_unique<_impl>(*p.inner_) : nullptr) {}
Progress::Progress(Progress&& p) noexcept
: inner_(std::move(p.inner_)) {}
Progress& Progress::operator=(Progress&& p) noexcept
{
if(this != &p) inner_ = std::move(p.inner_);
return *this;
}
Progress& Progress::operator=(Progress const& p) noexcept
{
if(this != &p) inner_ = p.inner_;
return *this;
}
Progress::~Progress() {
if(inner_)
fputc('\n', inner_->output);
}
#ifdef SPINNER
Spinner::~Spinner() {
//TODO: Add output: if(cur_) fputc('\n', output);
}
void Spinner::render(bool flush) {
/*char arr[] = {
'\r',
cur_,
0,
};
fputs(arr, output);
if(flush) fflush(output);*/
}
#endif
}

@ -1,8 +1,53 @@
#include <stdexcept>
#include <iostream>
#include <cmath>
#include <sys/types.h>
#include <rng/impl.hpp>
#include <error.h>
#include <panic.h>
//#define IMPORT_ONE(NS, NAME) using NAME = NS :: NAME
//IMPORT_ONE(std, ssize_t);
template<typename To, typename T, typename Unit=unsigned char>
static inline To* offset_ptr(T* ptr, ssize_t by) noexcept
{
return reinterpret_cast<To*>(reinterpret_cast<Unit*>(ptr)+by);
}
/*
template<typename To, typename Unit>
offset_ptr(auto* p, std::ssize_t by) -> offset_ptr<To, decltype(p), Unit>;
template<typename To>
offset_ptr(auto* p, std::ssize_t by) -> offset_ptr<To, decltype(p)>;
*/
#ifdef FT_PT_OPERATORS
inline operator RNG*(rng_t ptr) noexcept
{
if(__builtin_expect(!ptr, false)) return nullptr;
#ifdef DEBUG
RNG* op =
#else
return
#endif
reinterpret_cast<RNG*>(offset_ptr<RNG>(ptr, -static_cast<ssize_t>(offsetof(RNG, _held))));
#ifdef DEBUG
if(__builtin_expect(!op, false)) return nullptr;
if(__builtin_expect(op->_held, false)) panic("Invalid rng_t -> RNG conversion");
return ptr;
#endif
}
RNG::operator rng_t() const noexcept
{
return reinterpret_cast<rng_t>(static_cast<RNG*>(this)->_held);
}
#endif
inline unsigned char RNG::byte()
{
return (unsigned char)next_int(255);
@ -48,14 +93,21 @@ std::int64_t RNG::next_long()
}
#include <rng.h>
#include <panic.h>
namespace { // C interface
using namespace std;
#define extract_ptr(ptr) ((RNG*)(ptr))
[[gnu::always_inline, gnu::gnu_inline]]
static inline RNG* extract_ptr(rng_t ptr)
{ return reinterpret_cast<RNG*> (ptr); }
static inline RNG& extract_ref(rng_t rng)
{
return *extract_ptr(rng);
}
template<std::derived_from<RNG> T>
static inline rng_t wrap_ptr(T* ptr) noexcept
{
if(__builtin_expect(!ptr, false)) return nullptr;
return reinterpret_cast<rng_t>(static_cast<RNG*>(ptr));
}
template<typename T>
static inline T* extract_downcast_ptr(rng_t rng)
{
@ -64,29 +116,50 @@ namespace { // C interface
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);
case RNG_KIND_FRNG: return wrap_ptr( new rng::frng(opt.init.frng.state) );
case RNG_KIND_DRNG: return wrap_ptr( new rng::drng(opt.init.drng.state) );
case RNG_KIND_XORNG: return wrap_ptr( new rng::xoroshiro128plus(opt.init.xorng.state) );
//case RNG_KIND_LORENX: return static_cast<rng_t>( new rng::lorenzAttractor(opt.init.lorenz); );
default: panic("Unknown RNG init opt: %d", opt.kind);
__builtin_unreachable();
}
return nullptr;
}
void rng_free(rng_t rng)
{
RNG* ptr = (RNG*)rng;
RNG* ptr = extract_ptr(rng);
delete ptr;
}
[[gnu::nonnull(1)]]
void rng_test_spec(rng_t rng)
{
cout << "rng_test_spec:" << endl;
rng::test_algo(std::move(extract_ref(rng)));
auto& ref = extract_ref(rng);
cout << "rng_test_spec (" << typeid(ref).name() << ")..." << std::flush;
if constexpr(!is_noexcept) {
#if EXCEPT
try {
rng::test_algo(std::move(ref));
}
catch(const std::exception& except) {
cout << "\r\r\r\tERROR: " << (except.what() ?: typeid(except).name()) << endl;
rng_free(rng);
throw;
} catch(...) {
cout << "\r\r\r\tERROR" << endl;
rng_free(rng);
throw;
}
#endif
} else rng::test_algo(std::move(ref));
cout << "\r\r\r\tOK" << endl;
rng_free(rng);
}
}
}

@ -1,9 +1,8 @@
#include <tuple>
#include <functional>
#include <bit>
#include <cfloat>
#include <fmt/format.h>
#include <shuffle3.h>
#include <panic.h>
#include <map.h>
@ -16,8 +15,16 @@
template<typename T, typename Fn>
std::tuple<T, T> minmax_t(const span<T>& array, Fn keep)
{
T highest;
T lowest;
T highest
#ifdef DEBUG
{}
#endif
;
T lowest
#ifdef DEBUG
{}
#endif
;
bool init=false;
D_dprintf("minmax_t: %p (%lu)", array.as_ptr(), array.size());
for(std::size_t i=0;i<array.size();i++)
@ -49,6 +56,7 @@ namespace work
int xshuffle_ip(const char* file)
{
mm::mmap map(file);
map.access(mm::Access::Random, true);
if constexpr(unshuffle)
{
@ -64,12 +72,12 @@ namespace work
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::xoroshiro128plus xorng(std::bit_cast<std::uint64_t>(long_l), std::bit_cast<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::xoroshiro128plus xorng(std::bit_cast<std::uint64_t>(long_l), std::bit_cast<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); });
@ -90,10 +98,15 @@ namespace work
template<bool unshuffle>
int xshuffle_op(const char* ifile, const char* ofile, bool is_buffered)
{
//TODO: Use libcow's `cow_create_fd()`/`Cow::Cow(fd, size)` to try to map `ifile`
// Then, clone it, and use the `Fake` to do the shuffling on, and write the data to `ofile`.
// If `ifile` cannot be mapped, create a shim `Cow(size(ifile))`, `sendfile()/copy_file_range()/splice()` `ifile` into the shim memory fd, and use that (do *not* clone it to a Fake,)
// Then, perform the shuffling on the original `Cow`, and `sendfile()/copy_file_range()/splice()` the `ifile` file memory descriptor used for the `Cow` into `ofile`, and then destroy the `Cow`, **make sure to `msync()` the Cow'd range before the copy**.
mm::vmap imap; //{ifile};
if constexpr(unshuffle)
{
} else {
}

@ -0,0 +1,51 @@
#!/bin/bash
TFILE=/tmp/shuffle3-test-$(uuidgen)
TFILE2=$TFILE-cmp
FSIZE=${FSIZE:-$(( 1024 * 1024 * 10))}
FCNT=${FCNT:-4}
TRUE_SIZE=$(( FSIZE * FCNT))
function cleanup {
rm -f $TFILE $TFILE2
}
trap cleanup EXIT
stest() {
if fcmp $TFILE $TFILE2; then
echo ": shuffling $TRUE_SIZE bytes"
$1 -s $TFILE
if fcmp $TFILE $TFILE2; then
echo "Failed: Shuffle did nothing"
exit 1
else
echo ": unshuffling $TRUE_SIZE bytes"
$1 -u $TFILE
if fcmp $TFILE $TFILE2; then
echo "OK"
else
echo "Failed: Unshuffle didn't reproduce original file"
exit 1
fi
fi
else
echo "Invalid init: Files weren't identical"
exit -1
fi
}
echo ">>> Initialising"
dd if=/dev/urandom of=$TFILE bs=$FSIZE count=$FCNT >> /dev/null 2>&1 || exit -1
cp $TFILE $TFILE2 || exit -1
for ex in "$@"; do
if [[ -f "${ex%% *}" ]] || command -v "${ex%% *}" >/dev/null 2>&1; then
echo ">>> Testing $ex"
stest "$ex" || exit 1
fi
done
echo "Passed."
Loading…
Cancel
Save