mdbx: переделка page_alloc_slowpath() с добавлением профилирования GC.

This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2022-11-06 16:35:06 +03:00
parent acaa1d82d9
commit f680c99116
7 changed files with 868 additions and 540 deletions

81
mdbx.h
View File

@ -2561,9 +2561,6 @@ struct MDBX_envinfo {
msync; /**< Number of explicit msync-to-disk operations (not a pages) */ msync; /**< Number of explicit msync-to-disk operations (not a pages) */
uint64_t uint64_t
fsync; /**< Number of explicit fsync-to-disk operations (not a pages) */ fsync; /**< Number of explicit fsync-to-disk operations (not a pages) */
uint64_t
gcrtime_seconds16dot16; /**< Time spent loading and searching inside
GC (aka FreeDB) in 1/65536 of second */
} mi_pgop_stat; } mi_pgop_stat;
}; };
#ifndef __cplusplus #ifndef __cplusplus
@ -3713,8 +3710,8 @@ struct MDBX_commit_latency {
/** \brief Duration of preparation (commit child transactions, update /** \brief Duration of preparation (commit child transactions, update
* sub-databases records and cursors destroying). */ * sub-databases records and cursors destroying). */
uint32_t preparation; uint32_t preparation;
/** \brief Duration of GC/freeDB handling & updation. */ /** \brief Duration of GC update by wall clock. */
uint32_t gc; uint32_t gc_wallclock;
/** \brief Duration of internal audit if enabled. */ /** \brief Duration of internal audit if enabled. */
uint32_t audit; uint32_t audit;
/** \brief Duration of writing dirty/modified data pages to a filesystem, /** \brief Duration of writing dirty/modified data pages to a filesystem,
@ -3727,6 +3724,80 @@ struct MDBX_commit_latency {
uint32_t ending; uint32_t ending;
/** \brief The total duration of a commit. */ /** \brief The total duration of a commit. */
uint32_t whole; uint32_t whole;
/** \brief User-mode CPU time spent on GC update. */
uint32_t gc_cputime;
/** \brief Информация для профилирования работы GC.
* \note Статистика является общей для всех процессов работающих с одним
* файлом БД и хранится в LCK-файле. Данные аккумулируются при фиксации всех
* транзакций, но только в сборках libmdbx c установленной опцией
* \ref MDBX_ENABLE_PROFGC. Собранная статистика возвращаются любому процессу
* при использовании \ref mdbx_txn_commit_ex() и одновременно обнуляется
* при завершении транзакций верхнего уровня (не вложенных). */
struct {
/** \brief Количество итераций обновления GC,
* больше 1 если были повторы/перезапуски. */
uint32_t wloops;
/** \brief Количество итераций слияния записей GC. */
uint32_t coalescences;
/** \brief Количество уничтожений предыдущих надежных/устойчивых
* точек фиксации при работе в режиме \ref MDBX_UTTERLY_NOSYNC. */
uint32_t wipes;
/** \brief Количество принудительных фиксаций на диск
* во избежания приращения БД при работе вне режима
* \ref MDBX_UTTERLY_NOSYNC. */
uint32_t flushes;
/** \brief Количество обращений к механизму Handle-Slow-Readers
* во избежания приращения БД.
* \see MDBX_hsr_func */
uint32_t kicks;
/** \brief Счетчик выполнения по медленному пути (slow path execution count)
* GC ради данных пользователя. */
uint32_t work_counter;
/** \brief Время "по настенным часам" затраченное на чтение и поиск внутри
* GC ради данных пользователя. */
uint32_t work_rtime_monotonic;
/** \brief Монотонное время по "настенным часам" затраченное
* на подготовку страниц извлекаемых из GC для данных пользователя,
* включая подкачку с диска. */
uint32_t work_xtime_monotonic;
/** \brief Время ЦПУ в режиме пользователе затраченное на чтение и поиск
* внтури GC ради данных пользователя. */
uint32_t work_rtime_cpu;
/** \brief Количество итераций поиска внутри GC при выделении страниц
* ради данных пользователя. */
uint32_t work_rsteps;
/** \brief Количество запросов на выделение последовательностей страниц
* ради данных пользователя. */
uint32_t work_xpages;
/** \brief Количество страничных промахов (page faults) внутри GC
* при выделении и подготовки страниц для данных пользователя. */
uint32_t work_majflt;
/** \brief Счетчик выполнения по медленному пути (slow path execution count)
* GC для целей поддержки и обновления самой GC. */
uint32_t self_counter;
/** \brief Время "по настенным часам" затраченное на чтение и поиск внутри
* GC для целей поддержки и обновления самой GC. */
uint32_t self_rtime_monotonic;
/** \brief Монотонное время по "настенным часам" затраченное на подготовку
* страниц извлекаемых из GC для целей поддержки и обновления самой GC,
* включая подкачку с диска. */
uint32_t self_xtime_monotonic;
/** \brief Время ЦПУ в режиме пользователе затраченное на чтение и поиск
* внтури GC для целей поддержки и обновления самой GC. */
uint32_t self_rtime_cpu;
/** \brief Количество итераций поиска внутри GC при выделении страниц
* для целей поддержки и обновления самой GC. */
uint32_t self_rsteps;
/** \brief Количество запросов на выделение последовательностей страниц
* для самой GC. */
uint32_t self_xpages;
/** \brief Количество страничных промахов (page faults) внутри GC
* при выделении и подготовки страниц для самой GC. */
uint32_t self_majflt;
} gc_prof;
}; };
#ifndef __cplusplus #ifndef __cplusplus
/** \ingroup c_statinfo */ /** \ingroup c_statinfo */

View File

@ -5,8 +5,8 @@ N | MASK | ENV | TXN | DB | PUT | DBI | NOD
2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| | 2 |0000 0004|ALLOC_NEW |TXN_DIRTY |DUPSORT | |DBI_FRESH |F_DUPDATA|P_OVERFLOW| |
3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | | 3 |0000 0008|ALLOC_SLOT |TXN_SPILLS |INTEGERKEY| |DBI_CREAT | |P_META | |
4 |0000 0010|ALLOC_FAKE |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | | 4 |0000 0010|ALLOC_FAKE |TXN_HAS_CHILD |DUPFIXED |NOOVERWRITE|DBI_VALID | |P_BAD | |
5 |0000 0020| | |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | | 5 |0000 0020| |TXN_UPDATE_GC |INTEGERDUP|NODUPDATA |DBI_USRVALID| |P_LEAF2 | |
6 |0000 0040| | |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | | 6 |0000 0040| |TXN_FROZEN_RE |REVERSEDUP|CURRENT |DBI_DUPDATA | |P_SUBP | |
7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | | 7 |0000 0080| | | |ALLDUPS |DBI_AUDITED | | | |
8 |0000 0100| _MAY_MOVE | | | | | | | <= | 8 |0000 0100| _MAY_MOVE | | | | | | | <= |
9 |0000 0200| _MAY_UNMAP| | | | | | | <= | 9 |0000 0200| _MAY_UNMAP| | | | | | | <= |

1189
src/core.c

File diff suppressed because it is too large Load Diff

View File

@ -578,10 +578,30 @@ typedef struct MDBX_page {
#pragma pack(pop) #pragma pack(pop)
#if MDBX_ENABLE_PGOP_STAT typedef struct profgc_stat {
/* Монотонное время по "настенным часам"
* затраченное на чтение и поиск внутри GC */
uint64_t rtime_monotonic;
/* Монотонное время по "настенным часам" затраченное
* на подготовку страниц извлекаемых из GC, включая подкачку с диска. */
uint64_t xtime_monotonic;
/* Процессорное время в режим пользователя
* затраченное на чтение и поиск внутри GC */
uint64_t rtime_cpu;
/* Количество итераций чтения-поиска внутри GC при выделении страниц */
uint32_t rsteps;
/* Количество запросов на выделение последовательностей страниц,
* т.е. когда запрашивает выделение больше одной страницы */
uint32_t xpages;
/* Счетчик выполнения по медленному пути (slow path execution count) */
uint32_t spe_counter;
/* page faults (hard page faults) */
uint32_t majflt;
} profgc_stat_t;
/* Statistics of page operations overall of all (running, completed and aborted) /* Statistics of page operations overall of all (running, completed and aborted)
* transactions */ * transactions */
typedef struct { typedef struct pgop_stat {
MDBX_atomic_uint64_t newly; /* Quantity of a new pages added */ MDBX_atomic_uint64_t newly; /* Quantity of a new pages added */
MDBX_atomic_uint64_t cow; /* Quantity of pages copied for update */ MDBX_atomic_uint64_t cow; /* Quantity of pages copied for update */
MDBX_atomic_uint64_t clone; /* Quantity of parent's dirty pages clones MDBX_atomic_uint64_t clone; /* Quantity of parent's dirty pages clones
@ -592,15 +612,32 @@ typedef struct {
MDBX_atomic_uint64_t unspill; /* Quantity of unspilled/reloaded pages */ MDBX_atomic_uint64_t unspill; /* Quantity of unspilled/reloaded pages */
MDBX_atomic_uint64_t MDBX_atomic_uint64_t
wops; /* Number of explicit write operations (not a pages) to a disk */ wops; /* Number of explicit write operations (not a pages) to a disk */
MDBX_atomic_uint64_t
gcrtime; /* Time spending for reading/searching GC (aka FreeDB). The
unit/scale is platform-depended, see osal_monotime(). */
MDBX_atomic_uint64_t MDBX_atomic_uint64_t
msync; /* Number of explicit msync/flush-to-disk operations */ msync; /* Number of explicit msync/flush-to-disk operations */
MDBX_atomic_uint64_t MDBX_atomic_uint64_t
fsync; /* Number of explicit fsync/flush-to-disk operations */ fsync; /* Number of explicit fsync/flush-to-disk operations */
} MDBX_pgop_stat_t;
#endif /* MDBX_ENABLE_PGOP_STAT */ /* Статистика для профилирования GC.
* Логически эти данные может быть стоит вынести в другую структуру,
* но разница будет сугубо косметическая. */
struct {
/* Затраты на поддержку данных пользователя */
profgc_stat_t work;
/* Затраты на поддержку и обновления самой GC */
profgc_stat_t self;
/* Итераций обновления GC,
* больше 1 если были повторы/перезапуски */
uint32_t wloops;
/* Итерации слияния записей GC */
uint32_t coalescences;
/* Уничтожения steady-точек фиксации в MDBX_UTTERLY_NOSYNC */
uint32_t wipes;
/* Сбросы данные на диск вне MDBX_UTTERLY_NOSYNC */
uint32_t flushes;
/* Попытки пнуть тормозящих читателей */
uint32_t kicks;
} gc_prof;
} pgop_stat_t;
#if MDBX_LOCKING == MDBX_LOCKING_WIN32FILES #if MDBX_LOCKING == MDBX_LOCKING_WIN32FILES
#define MDBX_CLOCK_SIGN UINT32_C(0xF10C) #define MDBX_CLOCK_SIGN UINT32_C(0xF10C)
@ -738,11 +775,9 @@ typedef struct MDBX_lockinfo {
MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/ MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/
#if MDBX_ENABLE_PGOP_STAT
/* Statistics of costly ops of all (running, completed and aborted) /* Statistics of costly ops of all (running, completed and aborted)
* transactions */ * transactions */
MDBX_pgop_stat_t mti_pgop_stat; pgop_stat_t mti_pgop_stat;
#endif /* MDBX_ENABLE_PGOP_STAT*/
MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/ MDBX_ALIGNAS(MDBX_CACHELINE_SIZE) /* cacheline ----------------------------*/
@ -962,9 +997,13 @@ struct MDBX_txn {
/* Additional flag for sync_locked() */ /* Additional flag for sync_locked() */
#define MDBX_SHRINK_ALLOWED UINT32_C(0x40000000) #define MDBX_SHRINK_ALLOWED UINT32_C(0x40000000)
#define MDBX_TXN_UPDATE_GC 0x20 /* GC is being updated */
#define MDBX_TXN_FROZEN_RE 0x40 /* list of reclaimed-pgno must not altered */
#define TXN_FLAGS \ #define TXN_FLAGS \
(MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | \ (MDBX_TXN_FINISHED | MDBX_TXN_ERROR | MDBX_TXN_DIRTY | MDBX_TXN_SPILLS | \
MDBX_TXN_HAS_CHILD | MDBX_TXN_INVALID) MDBX_TXN_HAS_CHILD | MDBX_TXN_INVALID | MDBX_TXN_UPDATE_GC | \
MDBX_TXN_FROZEN_RE)
#if (TXN_FLAGS & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS)) || \ #if (TXN_FLAGS & (MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS)) || \
((MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS | TXN_FLAGS) & \ ((MDBX_TXN_RW_BEGIN_FLAGS | MDBX_TXN_RO_BEGIN_FLAGS | TXN_FLAGS) & \
@ -1023,8 +1062,8 @@ struct MDBX_txn {
struct { struct {
meta_troika_t troika; meta_troika_t troika;
/* In write txns, array of cursors for each DB */ /* In write txns, array of cursors for each DB */
pgno_t *reclaimed_pglist; /* Reclaimed GC pages */ pgno_t *relist; /* Reclaimed GC pages */
txnid_t last_reclaimed; /* ID of last used record */ txnid_t last_reclaimed; /* ID of last used record */
#if MDBX_ENABLE_REFUND #if MDBX_ENABLE_REFUND
pgno_t loose_refund_wl /* FIXME: describe */; pgno_t loose_refund_wl /* FIXME: describe */;
#endif /* MDBX_ENABLE_REFUND */ #endif /* MDBX_ENABLE_REFUND */
@ -1100,9 +1139,7 @@ struct MDBX_cursor {
#define C_SUB 0x04 /* Cursor is a sub-cursor */ #define C_SUB 0x04 /* Cursor is a sub-cursor */
#define C_DEL 0x08 /* last op was a cursor_del */ #define C_DEL 0x08 /* last op was a cursor_del */
#define C_UNTRACK 0x10 /* Un-track cursor when closing */ #define C_UNTRACK 0x10 /* Un-track cursor when closing */
#define C_RECLAIMING 0x20 /* GC lookup is prohibited */ uint8_t mc_flags;
#define C_GCFREEZE 0x40 /* reclaimed_pglist must not be updated */
uint8_t mc_flags; /* see mdbx_cursor */
/* Cursor checking flags. */ /* Cursor checking flags. */
#define CC_BRANCH 0x01 /* same as P_BRANCH for CHECK_LEAF_TYPE() */ #define CC_BRANCH 0x01 /* same as P_BRANCH for CHECK_LEAF_TYPE() */
@ -1113,7 +1150,7 @@ struct MDBX_cursor {
#define CC_LEAF2 0x20 /* same as P_LEAF2 for CHECK_LEAF_TYPE() */ #define CC_LEAF2 0x20 /* same as P_LEAF2 for CHECK_LEAF_TYPE() */
#define CC_RETIRING 0x40 /* refs to child pages may be invalid */ #define CC_RETIRING 0x40 /* refs to child pages may be invalid */
#define CC_PAGECHECK 0x80 /* perform page checking, see MDBX_VALIDATION */ #define CC_PAGECHECK 0x80 /* perform page checking, see MDBX_VALIDATION */
uint8_t mc_checking; /* page checking level */ uint8_t mc_checking;
MDBX_page *mc_pg[CURSOR_STACK]; /* stack of pushed pages */ MDBX_page *mc_pg[CURSOR_STACK]; /* stack of pushed pages */
indx_t mc_ki[CURSOR_STACK]; /* stack of page indices */ indx_t mc_ki[CURSOR_STACK]; /* stack of page indices */

View File

@ -73,6 +73,13 @@
#error MDBX_ENABLE_REFUND must be defined as 0 or 1 #error MDBX_ENABLE_REFUND must be defined as 0 or 1
#endif /* MDBX_ENABLE_REFUND */ #endif /* MDBX_ENABLE_REFUND */
/** Controls profiling of GC search and updates. */
#ifndef MDBX_ENABLE_PROFGC
#define MDBX_ENABLE_PROFGC 0
#elif !(MDBX_ENABLE_PROFGC == 0 || MDBX_ENABLE_PROFGC == 1)
#error MDBX_ENABLE_PROFGC must be defined as 0 or 1
#endif /* MDBX_ENABLE_PROFGC */
/** Controls gathering statistics for page operations. */ /** Controls gathering statistics for page operations. */
#ifndef MDBX_ENABLE_PGOP_STAT #ifndef MDBX_ENABLE_PGOP_STAT
#define MDBX_ENABLE_PGOP_STAT 1 #define MDBX_ENABLE_PGOP_STAT 1

View File

@ -18,6 +18,7 @@
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
#include <psapi.h>
#include <winioctl.h> #include <winioctl.h>
#if !MDBX_WITHOUT_MSVC_CRT && defined(_DEBUG) #if !MDBX_WITHOUT_MSVC_CRT && defined(_DEBUG)
@ -2700,6 +2701,60 @@ MDBX_INTERNAL_FUNC uint64_t osal_monotime(void) {
return 0; return 0;
} }
MDBX_INTERNAL_FUNC uint64_t osal_cputime(size_t *optional_page_faults) {
#if defined(_WIN32) || defined(_WIN64)
if (optional_page_faults) {
PROCESS_MEMORY_COUNTERS pmc;
*optional_page_faults =
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))
? pmc.PageFaultCount
: 0;
}
FILETIME unused, usermode;
if (GetThreadTimes(GetCurrentThread(),
/* CreationTime */ &unused,
/* ExitTime */ &unused,
/* KernelTime */ &unused,
/* UserTime */ &usermode)) {
/* one second = 10_000_000 * 100ns = 78125 * (1 << 7) * 100ns;
* result = (h * f / 10_000_000) << 32) + l * f / 10_000_000 =
* = ((h * f) >> 7) / 78125) << 32) + ((l * f) >> 7) / 78125;
* 1) {h, l} *= f;
* 2) {h, l} >>= 7;
* 3) result = ((h / 78125) << 32) + l / 78125; */
uint64_t l = usermode.dwLowDateTime * performance_frequency.QuadPart;
uint64_t h = usermode.dwHighDateTime * performance_frequency.QuadPart;
l = h << (64 - 7) | l >> 7;
h = h >> 7;
return ((h / 78125) << 32) + l / 78125;
}
#elif defined(RUSAGE_THREAD) || defined(RUSAGE_LWP)
#ifndef RUSAGE_THREAD
#define RUSAGE_THREAD RUSAGE_LWP /* Solaris */
#endif
struct rusage usage;
if (getrusage(RUSAGE_THREAD, &usage) == 0) {
if (optional_page_faults)
*optional_page_faults = usage.ru_majflt;
return usage.ru_utime.tv_sec * UINT64_C(1000000000) +
usage.ru_utime.tv_usec * 1000u;
}
if (optional_page_faults)
*optional_page_faults = 0;
#elif defined(CLOCK_THREAD_CPUTIME_ID)
if (optional_page_faults)
*optional_page_faults = 0;
struct timespec ts;
if (likely(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0))
return ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec;
#else
/* FIXME */
if (optional_page_faults)
*optional_page_faults = 0;
#endif
return 0;
}
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
static void bootid_shake(bin128_t *p) { static void bootid_shake(bin128_t *p) {

View File

@ -620,6 +620,7 @@ osal_pthread_mutex_lock(pthread_mutex_t *mutex) {
#endif /* !Windows */ #endif /* !Windows */
MDBX_INTERNAL_FUNC uint64_t osal_monotime(void); MDBX_INTERNAL_FUNC uint64_t osal_monotime(void);
MDBX_INTERNAL_FUNC uint64_t osal_cputime(size_t *optional_page_faults);
MDBX_INTERNAL_FUNC uint64_t osal_16dot16_to_monotime(uint32_t seconds_16dot16); MDBX_INTERNAL_FUNC uint64_t osal_16dot16_to_monotime(uint32_t seconds_16dot16);
MDBX_INTERNAL_FUNC uint32_t osal_monotime_to_16dot16(uint64_t monotime); MDBX_INTERNAL_FUNC uint32_t osal_monotime_to_16dot16(uint64_t monotime);