Improved PGO profiling

Reworked panic structuring (now has overloadable handler.)

Started work on out-of-place shuffler.

Started work on progress-bar.

Fortune for shuffle3's current commit: Great curse − 大凶
progress
Avril 2 years ago
parent 3e3b5d1cf6
commit ecf72a7526
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -22,6 +22,7 @@ CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors
CFLAGS += $(COMMON_FLAGS) --std=gnu11
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 +=
STRIP=strip
@ -50,6 +51,8 @@ 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
@ -112,18 +115,21 @@ pgo-reset:
pgo-profile: | pgo-reset pgo-generate
mkdir -p $(PGO_SET_LOC)
for i in {1..$(PGO_ITERATIONS)}; do \
dd if=/dev/urandom of=$(PGO_SET_LOC)/small bs=1024 count=1 >> /dev/null 2>&1; \
printf "Iteration $$i / $(PGO_ITERATIONS)\r"; \
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; \
./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

@ -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,12 +21,22 @@ 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);
#ifdef __cplusplus
}
@ -28,21 +44,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 {
inline static mmap allocate(const char* file, std::size_t sz)
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 mmap(map);
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 explicit mmap(mmap_t raw) :inner(raw){}
inline explicit mmap(mmap_t raw) noexcept :inner(raw){}
inline mmap(const char* file)
: inner(create_raw(file)) {}
@ -53,29 +96,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,77 @@
#pragma once
#ifndef __cplusplus
#error "C++ header only"
#endif
namespace pr {
namespace details [[gnu::visibility("inernal")]]{
template<typename T>
T* take(T*& ptr) noexcept { return std::exchange(ptr, nullptr); }
}
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;
};
}

@ -14,7 +14,7 @@ namespace rng {
inline void shuffle(R& rng, span<T> span)
{
if(!span.size()) return;
std::cout << " -> shuffling " << span.size() << " objects...";
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);
@ -63,7 +63,7 @@ namespace rng {
#undef MAP
#undef DYN
std::cout << " -> unshuffling " << span.size() << " objects...";
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));

@ -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;
@ -48,16 +93,16 @@ int open_and_alloc(const char* file, mmap_t* restrict ptr, size_t sz)
return 0;
}
if(fallocate(fd, 0, 0, sz))
if(ftruncate(fd, sz))
{
perror("Failed to allocate");
close(fd);
return 0;
}
register struct mmap map = { .fd = fd, .ptr = NULL, .len = sz };
struct mmap map = { .fd = fd, .ptr = NULL, .len = sz };
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;
@ -68,10 +113,12 @@ int open_and_alloc(const char* file, mmap_t* restrict ptr, size_t sz)
return 1;
}
int unmap_and_close(mmap_t map)
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;
@ -83,3 +130,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);
}

@ -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
}
}

@ -48,6 +48,7 @@ namespace work
int xshuffle_ip(const char* file)
{
mm::mmap map(file);
map.access(mm::Access::Random, true);
if constexpr(unshuffle)
{
@ -89,6 +90,11 @@ 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)
{

Loading…
Cancel
Save