From 3869efe76653ce9e56edf10478e30dcf1072123c Mon Sep 17 00:00:00 2001 From: Adrian Grucza <46910040+apgrucza@users.noreply.github.com> Date: Fri, 26 Apr 2024 23:00:13 +1000 Subject: [PATCH] Use mimalloc to improve performance and reduce memory allocation lock contention (#6) * Add mimalloc submodule and update build script * Add steps to build and test with UseMimalloc * Update mimalloc submodule * Change UseMimalloc parameter type to switch * Add ExpectMimalloc parameter * Fetch mimalloc submodule and use mimalloc parameters * Prevent MIMALLOC_VERBOSE aborting tests * Uninstall driver after tests; upload mimalloc artifacts --- .github/workflows/main.yml | 100 ++++++++++++++++++++++++++++++------- .gitmodules | 4 ++ inouealc.c | 26 +++++----- libs/mimalloc | 1 + psqlodbc.h | 32 ++++++++++++ winbuild/BuildAll.ps1 | 28 ++++++++++- winbuild/psqlodbc.vcxproj | 15 ++++-- winbuild/regress.ps1 | 19 ++++++- 8 files changed, 186 insertions(+), 39 deletions(-) create mode 100644 .gitmodules create mode 160000 libs/mimalloc diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4203264..bba823b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -184,54 +184,116 @@ jobs: sc config "postgresql-x64-14" start= auto - name: get psqlodbc uses: actions/checkout@v4 + with: + submodules: true - name: 'setup msvc for psqlodbc' uses: TheMrMilchmann/setup-msvc-dev@v3 with: arch: x86 - - name: build psqlodbc + + - name: build psqlodbc standard shell: powershell run: | copy .github\workflows\configuration.xml winbuild winbuild\BuildAll.ps1 installer\buildInstallers.ps1 - - name: test psqlodbc + env: + PSQLODBC_OBJBASE: ${{ github.workspace }}\winbuild\standard + - name: test psqlodbc standard shell: powershell run: | winbuild\regress.ps1 -DsnInfo "SERVER=localhost|DATABASE=contrib_regression|PORT=5432|UID=postgres|PWD=password" - - name: Upload x64 merge module + standard\test_x86\RegisterRegdsn.exe uninstall_driver postgres_devw + standard\test_x64\RegisterRegdsn.exe uninstall_driver postgres_devw + env: + PSQLODBC_OBJBASE: ${{ github.workspace }}\winbuild\standard + + - name: build psqlodbc mimalloc + shell: powershell + run: | + copy .github\workflows\configuration.xml winbuild + winbuild\BuildAll.ps1 -UseMimalloc + installer\buildInstallers.ps1 + env: + PSQLODBC_OBJBASE: ${{ github.workspace }}\winbuild\mimalloc + - name: test psqlodbc mimalloc + shell: powershell + run: | + winbuild\regress.ps1 -DsnInfo "SERVER=localhost|DATABASE=contrib_regression|PORT=5432|UID=postgres|PWD=password" -ExpectMimalloc + mimalloc\test_x86\RegisterRegdsn.exe uninstall_driver postgres_devw + mimalloc\test_x64\RegisterRegdsn.exe uninstall_driver postgres_devw + env: + PSQLODBC_OBJBASE: ${{ github.workspace }}\winbuild\mimalloc + + - name: Upload standard x64 merge module + uses: actions/upload-artifact@v4 + with: + name: psqlODBC Standard x64 Merge Module + path: winbuild/standard/installer/x64/*.msm + retention-days: 5 + if-no-files-found: error + - name: Upload standard x64 installer package + uses: actions/upload-artifact@v4 + with: + name: psqlODBC Standard x64 Installer + path: winbuild/standard/installer/x64/*.msi + retention-days: 5 + if-no-files-found: error + - name: Upload standard x86 merge module + uses: actions/upload-artifact@v4 + with: + name: psqlODBC Standard x86 Merge Module + path: winbuild/standard/installer/x86/*.msm + retention-days: 5 + if-no-files-found: error + - name: Upload standard x86 installer package + uses: actions/upload-artifact@v4 + with: + name: psqlODBC Standard x86 Installer + path: winbuild/standard/installer/x86/*.msi + retention-days: 5 + if-no-files-found: error + - name: Upload standard x64 setup + uses: actions/upload-artifact@v4 + with: + name: psqlODBC Standard x64 Setup + path: winbuild/standard/installer/psqlodbc-setup/bin/Release/psqlodbc-setup.exe + retention-days: 5 + if-no-files-found: error + + - name: Upload mimalloc x64 merge module uses: actions/upload-artifact@v4 with: - name: psqlODBC x64 Merge Module - path: ./installer/x64/*.msm + name: psqlODBC mimalloc x64 Merge Module + path: winbuild/mimalloc/installer/x64/*.msm retention-days: 5 if-no-files-found: error - - name: Upload x64 installer package + - name: Upload mimalloc x64 installer package uses: actions/upload-artifact@v4 with: - name: psqlODBC x64 Installer - path: ./installer/x64/*.msi + name: psqlODBC mimalloc x64 Installer + path: winbuild/mimalloc/installer/x64/*.msi retention-days: 5 if-no-files-found: error - - name: Upload x86 merge module + - name: Upload mimalloc x86 merge module uses: actions/upload-artifact@v4 with: - name: psqlODBC x86 Merge Module - path: ./installer/x86/*.msm + name: psqlODBC mimalloc x86 Merge Module + path: winbuild/mimalloc/installer/x86/*.msm retention-days: 5 if-no-files-found: error - - name: Upload x86 installer package + - name: Upload mimalloc x86 installer package uses: actions/upload-artifact@v4 with: - name: psqlODBC x86 Installer - path: ./installer/x86/*.msi + name: psqlODBC mimalloc x86 Installer + path: winbuild/mimalloc/installer/x86/*.msi retention-days: 5 if-no-files-found: error - - name: Upload x64 setup - id: x64_setup + - name: Upload mimalloc x64 setup uses: actions/upload-artifact@v4 with: - name: psqlODBC x64 Setup - path: ./installer/psqlodbc-setup/bin/Release/psqlodbc-setup.exe + name: psqlODBC mimalloc x64 Setup + path: winbuild/mimalloc/installer/psqlodbc-setup/bin/Release/psqlodbc-setup.exe retention-days: 5 if-no-files-found: error @@ -243,4 +305,4 @@ jobs: draft: false prerelease: false token: ${{secrets.RELEASE_TOKEN}} - artifacts: "./installer/x86/*.msi,./installer/x86/*.msm,./installer/psqlodbc-setup/bin/Release/psqlodbc-setup.exe" + artifacts: "winbuild/standard/installer/x86/*.msi,winbuild/standard/installer/x86/*.msm,winbuild/standard/installer/psqlodbc-setup/bin/Release/psqlodbc-setup.exe" diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0b66fd3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "libs/mimalloc"] + path = libs/mimalloc + url = https://github.com/microsoft/mimalloc + branch = master diff --git a/inouealc.c b/inouealc.c index 977990a..a7d2191 100644 --- a/inouealc.c +++ b/inouealc.c @@ -1,6 +1,7 @@ #undef _MEMORY_DEBUG_ #include "psqlodbc.h" +#ifndef _MIMALLOC_ #ifdef WIN32 #ifdef _DEBUG /* #include */ @@ -10,6 +11,7 @@ #include #endif /* _DEBUG */ #endif /* WIN32 */ +#endif /* _MIMALLOC_ */ #include #include "misc.h" @@ -26,20 +28,20 @@ CSTR ALCERR = "alcerr"; void * pgdebug_alloc(size_t size) { void * alloced; - alloced = malloc(size); + alloced = pg_malloc(size); MYLOG(2, " alloced=%p(" FORMAT_SIZE_T ")\n", alloced, size); if (alloced) { if (!alsize) { alsize = 100; - altbl = (ALADR *) malloc(alsize * sizeof(ALADR)); + altbl = (ALADR *) pg_malloc(alsize * sizeof(ALADR)); } else if (tbsize >= alsize) { ALADR *al; alsize *= 2; - if (al = (ALADR *) realloc(altbl, alsize * sizeof(ALADR)), NULL == al) + if (al = (ALADR *) pg_realloc(altbl, alsize * sizeof(ALADR)), NULL == al) return alloced; altbl = al; } @@ -53,20 +55,20 @@ MYLOG(2, " alloced=%p(" FORMAT_SIZE_T ")\n", alloced, size); } void * pgdebug_calloc(size_t n, size_t size) { - void * alloced = calloc(n, size); + void * alloced = pg_calloc(n, size); if (alloced) { if (!alsize) { alsize = 100; - altbl = (ALADR *) malloc(alsize * sizeof(ALADR)); + altbl = (ALADR *) pg_malloc(alsize * sizeof(ALADR)); } else if (tbsize >= alsize) { ALADR *al; alsize *= 2; - if (al = (ALADR *) realloc(altbl, alsize * sizeof(ALADR)), NULL == al) + if (al = (ALADR *) pg_realloc(altbl, alsize * sizeof(ALADR)), NULL == al) return alloced; altbl = al; } @@ -85,7 +87,7 @@ void * pgdebug_realloc(void * ptr, size_t size) if (!ptr) return pgdebug_alloc(size); - alloced = realloc(ptr, size); + alloced = pg_realloc(ptr, size); if (!alloced) { MYLOG(0, "%s %p error\n", ALCERR, ptr); @@ -109,7 +111,7 @@ void * pgdebug_realloc(void * ptr, size_t size) } char * pgdebug_strdup(const char * ptr) { - char * alloced = strdup(ptr); + char * alloced = pg_strdup(ptr); if (!alloced) { MYLOG(0, "%s %p error\n", ALCERR, ptr); @@ -119,13 +121,13 @@ char * pgdebug_strdup(const char * ptr) if (!alsize) { alsize = 100; - altbl = (ALADR *) malloc(alsize * sizeof(ALADR)); + altbl = (ALADR *) pg_malloc(alsize * sizeof(ALADR)); } else if (tbsize >= alsize) { ALADR *al; alsize *= 2; - if (al = (ALADR *) realloc(altbl, alsize * sizeof(ALADR)), NULL == al) + if (al = (ALADR *) pg_realloc(altbl, alsize * sizeof(ALADR)), NULL == al) return alloced; altbl = al; } @@ -168,7 +170,7 @@ void pgdebug_free(void * ptr) } else MYLOG(2, "ptr=%p\n", ptr); - free(ptr); + pg_free(ptr); } static BOOL out_check(void *out, size_t len, const char *name) @@ -253,7 +255,7 @@ void debug_memory_check(void) if (0 == tbsize) { MYLOG(0, "no memry leak found and max count allocated so far is %d\n", alsize); - free(altbl); + pg_free(altbl); alsize = 0; } else diff --git a/libs/mimalloc b/libs/mimalloc new file mode 160000 index 0000000..229ec9c --- /dev/null +++ b/libs/mimalloc @@ -0,0 +1 @@ +Subproject commit 229ec9cbdc81cf1ffb705cbd8a07503589847426 diff --git a/psqlodbc.h b/psqlodbc.h index bf25960..5b40b58 100644 --- a/psqlodbc.h +++ b/psqlodbc.h @@ -24,6 +24,9 @@ #endif #include "version.h" +#ifdef _MIMALLOC_ +#include +#else /* _MIMALLOC_ */ #ifdef WIN32 #ifdef _DEBUG #ifndef _MEMORY_DEBUG_ @@ -41,6 +44,11 @@ #else /* WIN32 */ #include #endif /* WIN32 */ +#endif /* _MIMALLOC_ */ + +#ifdef WIN32 +#include +#endif /* WIN32 */ #ifdef __INCLUDE_POSTGRES_FE_H__ /* currently not defined */ /* @@ -61,6 +69,21 @@ #endif /* __GNUC__ || __IBMC__ */ #endif /* __INCLUDE_POSTGRES_FE_H__ */ +#ifdef _MIMALLOC_ +#include +#define pg_malloc mi_malloc +#define pg_realloc mi_realloc +#define pg_calloc mi_calloc +#define pg_strdup mi_strdup +#define pg_free mi_free +#else /* _MIMALLOC_ */ +#define pg_malloc malloc +#define pg_realloc realloc +#define pg_calloc calloc +#define pg_strdup _strdup +#define pg_free free +#endif /* _MIMALLOC_ */ + #ifdef _MEMORY_DEBUG_ void *pgdebug_alloc(size_t); void *pgdebug_calloc(size_t, size_t); @@ -87,6 +110,15 @@ void debug_memory_check(void); /* #define strncpy_null pgdebug_strncpy_null */ #define memcpy pgdebug_memcpy #define memset pgdebug_memset +#else /* _MEMORY_DEBUG_ */ +#ifdef WIN32 +#undef strdup +#endif /* WIN32 */ +#define malloc pg_malloc +#define realloc pg_realloc +#define calloc pg_calloc +#define strdup pg_strdup +#define free pg_free #endif /* _MEMORY_DEBUG_ */ #ifdef WIN32 diff --git a/winbuild/BuildAll.ps1 b/winbuild/BuildAll.ps1 index 3deed98..ac8bc70 100755 --- a/winbuild/BuildAll.ps1 +++ b/winbuild/BuildAll.ps1 @@ -30,6 +30,9 @@ Specify the configuration xml file name if you want to use the configuration file other than standard one. The relative path is relative to the current directory. +.PARAMETER UseMimalloc + Specify whether to use the mimalloc allocator for improved performance. + Requires a toolset of v141, v142 or later. .EXAMPLE > .\BuildAll Build with default or automatically selected parameters. @@ -65,7 +68,8 @@ Param( [ValidateSet("Debug", "Release")] [String]$Configuration="Release", [string]$BuildConfigPath, -[switch]$AlongWithInstallers +[switch]$AlongWithInstallers, +[switch]$UseMimalloc ) function buildPlatform([xml]$configInfo, [string]$Platform) @@ -100,7 +104,27 @@ function buildPlatform([xml]$configInfo, [string]$Platform) $BUILD_MACROS = $BUILD_MACROS -replace '"', '`"' $macroList = iex "write-output $BUILD_MACROS" } - & ${msbuildexe} ./platformbuild.vcxproj /tv:$MSToolsV "/p:Platform=$Platform;Configuration=$Configuration;PlatformToolset=${Toolset}" /t:$target /p:VisualStudioVersion=${VCVersion} /p:DRIVERVERSION=$DRIVERVERSION /p:PG_INC=$PG_INC /p:PG_LIB=$PG_LIB /p:PG_BIN=$PG_BIN $macroList + + if ($UseMimalloc) { + $mimallocProperty = "yes" + + switch ($VCVersion) { + "10.0" { $mimallocIdeDir = "vs2017" } + "11.0" { $mimallocIdeDir = "vs2017" } + "12.0" { $mimallocIdeDir = "vs2017" } + "14.0" { $mimallocIdeDir = "vs2017" } + "15.0" { $mimallocIdeDir = "vs2017" } + "16.0" { $mimallocIdeDir = "vs2019" } + "17.0" { $mimallocIdeDir = "vs2022" } + default { throw "Unable to resolve mimalloc IDE directory for VC ${VCVersion}."} + } + + # build mimalloc dependency + & ${msbuildexe} ..\libs\mimalloc\ide\$mimallocIdeDir\mimalloc.vcxproj /tv:$MSToolsV "/p:Platform=$Platform;Configuration=$Configuration;PlatformToolset=${Toolset}" /t:$target /p:VisualStudioVersion=${VCVersion} + } + + # build psqlodbc + & ${msbuildexe} ./platformbuild.vcxproj /tv:$MSToolsV "/p:Platform=$Platform;Configuration=$Configuration;PlatformToolset=${Toolset}" /t:$target /p:VisualStudioVersion=${VCVersion} /p:DRIVERVERSION=$DRIVERVERSION /p:PG_INC=$PG_INC /p:PG_LIB=$PG_LIB /p:PG_BIN=$PG_BIN /p:MIMALLOC=$mimallocProperty $macroList } $scriptPath = (Split-Path $MyInvocation.MyCommand.Path) diff --git a/winbuild/psqlodbc.vcxproj b/winbuild/psqlodbc.vcxproj index 5d1d9e8..9eeeeab 100755 --- a/winbuild/psqlodbc.vcxproj +++ b/winbuild/psqlodbc.vcxproj @@ -131,6 +131,13 @@ $(ADD_DEFINES);_MEMORY_DEBUG_ + + + $(ADD_DEFINES);_MIMALLOC_ + $(ADD_INC);..\libs\mimalloc\include + $(ADD_LIBPATH);..\libs\mimalloc\out\msvc-$(Platform)\$(Configuration) + $(CALL_LIB);mimalloc-static.lib + true @@ -158,7 +165,7 @@ $(DELAY_LOAD_DLLS);%(DelayLoadDLLs) $(ADD_LIBPATH);$(OutDir);%(AdditionalLibraryDirectories) - libpq.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;ws2_32.lib;secur32.lib;winmm.lib;%(AdditionalDependencies) + $(CALL_LIB);libpq.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;ws2_32.lib;secur32.lib;winmm.lib;%(AdditionalDependencies) $(MAINDEF) true @@ -177,7 +184,7 @@ $(DELAY_LOAD_DLLS);%(DelayLoadDLLs) $(ADD_LIBPATH);$(OutDir);%(AdditionalLibraryDirectories) - libpq.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;ws2_32.lib;secur32.lib;winmm.lib;%(AdditionalDependencies) + $(CALL_LIB);libpq.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;ws2_32.lib;secur32.lib;winmm.lib;%(AdditionalDependencies) $(MAINDEF) true @@ -196,7 +203,7 @@ $(DELAY_LOAD_DLLS);%(DelayLoadDLLs) $(ADD_LIBPATH);$(OutDir);%(AdditionalLibraryDirectories) - libpq.lib;winmm.lib;wsock32.lib;ws2_32.lib;secur32.lib;%(AdditionalDependencies) + $(CALL_LIB);libpq.lib;winmm.lib;wsock32.lib;ws2_32.lib;secur32.lib;%(AdditionalDependencies) $(MAINDEF) true @@ -215,7 +222,7 @@ $(DELAY_LOAD_DLLS);%(DelayLoadDLLs) $(ADD_LIBPATH);$(OutDir);%(AdditionalLibraryDirectories) - libpq.lib;winmm.lib;wsock32.lib;ws2_32.lib;secur32.lib;%(AdditionalDependencies) + $(CALL_LIB);libpq.lib;winmm.lib;wsock32.lib;ws2_32.lib;secur32.lib;%(AdditionalDependencies) $(MAINDEF) true diff --git a/winbuild/regress.ps1 b/winbuild/regress.ps1 index bff0b7e..be33df0 100644 --- a/winbuild/regress.ps1 +++ b/winbuild/regress.ps1 @@ -38,6 +38,8 @@ The relative path is relative to the current directory. .PARAMETER ReinstallDriver Reinstall the driver in any case. +.PARAMETER ExpectMimalloc + Specify whether usage of the mimalloc allocator is expected. .EXAMPLE > .\regress Build with default or automatically selected parameters @@ -85,7 +87,8 @@ Param( [string]$DeclareFetch="on", [string]$DsnInfo, [string]$SpecificDsn, -[switch]$ReinstallDriver +[switch]$ReinstallDriver, +[switch]$ExpectMimalloc ) @@ -207,6 +210,8 @@ function vcxfile_make($testnames, $dirnames, $vcxfile) function RunTest($scriptPath, $Platform, $testexes) { + $originalErrorActionPreference = $ErrorActionPreference + # Run regression tests if ($Platform -eq "x64") { $targetdir="test_x64" @@ -237,6 +242,9 @@ function RunTest($scriptPath, $Platform, $testexes) if ($cnstr.length -eq 0) { $cnstr += $null } + # Temporarily set $ErrorActionPreference to "Continue" because MIMALLOC_VERBOSE writes to stderr + $ErrorActionPreference = "Continue" + $env:MIMALLOC_VERBOSE = 1 for ($i = 0; $i -lt $cnstr.length; $i++) { $env:COMMON_CONNECTION_STRING_FOR_REGRESSION_TEST = $cnstr[$i] @@ -244,12 +252,19 @@ function RunTest($scriptPath, $Platform, $testexes) $env:COMMON_CONNECTION_STRING_FOR_REGRESSION_TEST += ";Database=contrib_regression;ConnSettings={set lc_messages='C'}" } write-host "`n`tSetting by env variable:$env:COMMON_CONNECTION_STRING_FOR_REGRESSION_TEST" - .\runsuite $testexes --inputdir=$origdir + .\runsuite $testexes --inputdir=$origdir 2>&1 | Tee-Object -Variable runsuiteOutput + + # Check whether mimalloc ran by searching for a verbose message from mimalloc + if ($ExpectMimalloc -xor ($runsuiteOutput -match "mimalloc: process done")) { + throw "`tmimalloc usage was expected to be $ExpectMimalloc" + } } } catch [Exception] { throw $error[0] } finally { $env:COMMON_CONNECTION_STRING_FOR_REGRESSION_TEST = $null + $env:MIMALLOC_VERBOSE = $null + $ErrorActionPreference = $originalErrorActionPreference } } -- 2.39.5