terminal/src/propslib/RegistrySerialization.hpp
Austin Lamb 539a5dc0af
Greatly reduce allocations in the conhost/OpenConsole startup path (#8489)
I was looking at conhost/OpenConsole and noticed it was being pretty
inefficient with allocations due to some usages of std::deque and
std::vector that didn't need to be done quite that way.

So this uses std::vector for the TextBuffer's storage of ROW objects,
which allows one allocation to contiguously reserve space for all the
ROWs - on Desktop this is 9001 ROW objects which means it saves 9000
allocations that the std::deque would have done.  Plus it has the
benefit of increasing locality of the ROW objects since deque is going
to chase pointers more often with its data structure.

Then, within each ROW there are CharRow and ATTR_ROW objects that use
std::vector today.  This changes them to use Boost's small_vector, which
is a variation of vector that allows for the so-called "small string
optimization."  Since we know the typical size of these vectors, we can
pre-reserve the right number of elements directly in the
CharRow/ATTR_ROW instances, avoiding any heap allocations at all for
constructing these objects.

There are a ton of variations on this "small_vector" concept out there
in the world - this one in Boost, LLVM has one called SmallVector,
Electronic Arts' STL has a small_vector, Facebook's folly library has
one...there are a silly number of these out there.  But Boost seems like
it's by far the easiest to consume in terms of integration into this
repo, the CI/CD pipeline, licensing, and stuff like that, so I went with
the boost version.

In terms of numbers, I measured the startup path of OpenConsole.exe on
my dev box for Release x64 configuration.  My box is an i7-6700k @ 4
Ghz, with 32 GB RAM, not that I think machine config matters much here:

|        | Allocation count    | Allocated bytes    | CPU usage (ms) |
| ------ | ------------------- | ------------------ | -------------- |
| Before | 29,461              | 4,984,640          | 103            |
| After  | 2,459 (-91%)        | 4,853,931 (-2.6%)  | 96 (-7%)       |

Along the way, I also fixed a dynamic initializer I happened to spot in
the registry code, and updated some docs.

## Validation Steps Performed
- Ran "runut", "runft" and "runuia" locally and confirmed results are
  the same as the main branch
- Profiled the before/after numbers in the Visual Studio profiler, for
  the numbers shown in the table

Co-authored-by: Austin Lamb <austinl@microsoft.com>
2020-12-16 10:40:30 -08:00

100 lines
4.2 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- RegistrySerialization.hpp
Abstract:
- This module is used for reading/writing registry operations
Author(s):
- Michael Niksa (MiNiksa) 23-Jul-2014
- Paul Campbell (PaulCam) 23-Jul-2014
- Mike Griese (MiGrie) 04-Aug-2015
Revision History:
- From components of srvinit.c
- From miniksa, paulcam's Registry.cpp
--*/
#pragma once
class Settings;
class RegistrySerialization
{
public:
// The following registry methods remain public for DBCS and EUDC lookups.
[[nodiscard]] static NTSTATUS s_OpenKey(_In_opt_ HKEY const hKey, _In_ PCWSTR const pwszSubKey, _Out_ HKEY* const phResult);
[[nodiscard]] static NTSTATUS s_QueryValue(const HKEY hKey,
_In_ PCWSTR const pwszValueName,
const DWORD cbValueLength,
const DWORD regType,
_Out_writes_bytes_(cbValueLength) BYTE* const pbData,
_Out_opt_ _Out_range_(0, cbValueLength) DWORD* const pcbDataLength);
[[nodiscard]] static NTSTATUS s_EnumValue(const HKEY hKey,
const DWORD dwIndex,
const DWORD cbValueLength,
_Out_writes_bytes_(cbValueLength) PWSTR const pwszValueName,
const DWORD cbDataLength,
_Out_writes_bytes_(cbDataLength) BYTE* const pbData);
[[nodiscard]] static NTSTATUS s_OpenConsoleKey(_Out_ HKEY* phCurrentUserKey, _Out_ HKEY* phConsoleKey);
[[nodiscard]] static NTSTATUS s_CreateKey(const HKEY hKey, _In_ PCWSTR const pwszSubKey, _Out_ HKEY* const phResult);
[[nodiscard]] static NTSTATUS s_DeleteValue(const HKEY hKey, _In_ PCWSTR const pwszValueName);
[[nodiscard]] static NTSTATUS s_SetValue(const HKEY hKey,
_In_ PCWSTR const pwszValueName,
const DWORD dwType,
_In_reads_bytes_(cbDataLength) BYTE* const pbData,
const DWORD cbDataLength);
[[nodiscard]] static NTSTATUS s_UpdateValue(const HKEY hConsoleKey,
const HKEY hKey,
_In_ PCWSTR const pwszValueName,
const DWORD dwType,
_In_reads_bytes_(dwDataLength) BYTE* pbData,
const DWORD dwDataLength);
[[nodiscard]] static NTSTATUS s_OpenCurrentUserConsoleTitleKey(_In_ PCWSTR const title,
_Out_ HKEY* phCurrentUserKey,
_Out_ HKEY* phConsoleKey,
_Out_ HKEY* phTitleKey);
enum _RegPropertyType
{
Boolean,
Dword,
Word,
Byte,
Coordinate,
String,
};
static DWORD ToWin32RegistryType(const _RegPropertyType type);
typedef struct _RegPropertyMap
{
_RegPropertyType propertyType;
PCWSTR pwszValueName;
DWORD dwFieldOffset;
size_t cbFieldSize;
_RegPropertyMap& operator=(const _RegPropertyMap&) { return *this; }
} RegPropertyMap;
static const RegPropertyMap s_PropertyMappings[];
static const size_t s_PropertyMappingsSize;
static const RegPropertyMap s_GlobalPropMappings[];
static const size_t s_GlobalPropMappingsSize;
[[nodiscard]] static NTSTATUS s_LoadRegDword(const HKEY hKey, const _RegPropertyMap* const pPropMap, _In_ Settings* const pSettings);
[[nodiscard]] static NTSTATUS s_LoadRegString(const HKEY hKey, const _RegPropertyMap* const pPropMap, _In_ Settings* const pSettings);
};