Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use mimalloc to improve performance and reduce memory allocation lock contention #6

Merged
merged 8 commits into from
Apr 26, 2024

Conversation

apgrucza
Copy link
Contributor

@apgrucza apgrucza commented Mar 6, 2024

We have a multi-threaded Windows application that was experiencing delays due to high lock contention in memory allocations from the PostgreSQL ODBC driver. We tried modifying the driver to use mimalloc, which is a memory allocator with better performance characteristics. After deploying this change, the delays due to lock contention disappeared. It has been running on thousands of our production deployments for 9 months without issue.

I've created this pull request so that others can benefit from this change by building the driver with the _MIMALLOC_ symbol defined and linking to the mimalloc library. If building on Windows, BuildAll.ps1 accepts a -UseMimalloc argument that does this for you (requires a toolset of v141, v142 or later). Below is an example of how to build with mimalloc on Windows:

.\BuildAll.ps1 -Platform x64 -Toolset v141 -UseMimalloc

Currently the usage of mimalloc is off by default, but I'd like to get people's thoughts on whether it should be enabled by default.

@davecramer
Copy link
Contributor

I'm looking at enabling github actions on this repo. Once I get that working I can look at this PR

@davecramer
Copy link
Contributor

any chance you can test the installers from https://github.com/postgresql-interfaces/psqlodbc/actions/runs/8469691901

@apgrucza
Copy link
Contributor Author

Sorry I was off for a few weeks. Do you still need these installers tested?

@davecramer
Copy link
Contributor

@apgrucza yes please. I have been working on automating the release process.
Latest is https://github.com/postgresql-interfaces/psqlodbc/actions/runs/8723906820

@davecramer
Copy link
Contributor

Can you rebase and add your tests to the github actions ?

@apgrucza
Copy link
Contributor Author

I ran the psqlODBC x64 Installer and successfully used pyodbc to connect to and query a PostgreSQL database using the PostgreSQL Unicode(x64) driver.

Can you rebase and add your tests to the github actions ?

Not sure which tests you are referring to. This pull request does not add any tests.

@davecramer
Copy link
Contributor

@apgrucza right, but wouldn't we have to build with your option turned on, and then run all of the tests ?

@apgrucza apgrucza force-pushed the feature/mimalloc branch 3 times, most recently from 0df3899 to c602f0b Compare April 23, 2024 12:14
@davecramer
Copy link
Contributor

@apgrucza looks like the test is failing ?

@apgrucza
Copy link
Contributor Author

@apgrucza looks like the test is failing ?

@davecramer my changes are now complete. Please try again.

@davecramer davecramer merged commit 3869efe into postgresql-interfaces:main Apr 26, 2024
1 check passed
@davecramer
Copy link
Contributor

Thanks!

@apgrucza apgrucza deleted the feature/mimalloc branch April 29, 2024 06:06
@apgrucza
Copy link
Contributor Author

apgrucza commented May 1, 2024

any chance you can test the installers from https://github.com/postgresql-interfaces/psqlodbc/actions/runs/8469691901

@davecramer I tried running the 64-bit installer again (the linked artifact has expired but I still have the file), and I now get the following error:

Error installing ODBC driver: PostgreSQL ANSI(x64), ODBC error 13: The setup routines for the PostgreSQL ANSI(x64) ODBC driver could not be loaded due to system error code 126: The specified module could not be found. (C:\Program Files\psqlODBC\1600\bin\psqlodbc30a.dll).. Verify that the file PostgreSQL ANSI(x64) exists and that you can access it.

Not sure why I didn't get this the first time around. The same error occurs whether I install fresh or upgrade from an old version. I've tested on two different machines. The psqlodbc30a.dll file does exist, so I wonder whether it's failing due to a missing dependency?

@apgrucza
Copy link
Contributor Author

apgrucza commented May 1, 2024

The x86 installer works fine though.

@davecramer
Copy link
Contributor

@apgrucza
Copy link
Contributor Author

apgrucza commented May 1, 2024

@davecramer that link only contains the x86 installer. I did test that one (in addition to the build artifacts) and it installed without error. The problem seems to be just with the x64 installer.

I don't think it's related, but I'm also curious to know why the version of libpq.dll in these packages (both x86 and x64) is 17.0.0.0, but the latest version of PostgreSQL is 16.2.

@davecramer
Copy link
Contributor

Interesting. As for 17. it's because I build against head. Presumably the next release will be against 17
The fact that all the artifacts are not there is a problem though.
Thanks for testing!

@davecramer
Copy link
Contributor

The instructions on how to create the installers are not helpful here.

@davecramer
Copy link
Contributor

I found the problem. I deleted

	echo Building Installer Merge Module
        $(CANDLE) -nologo -dLIBPQMEM0="libssl-3-x64.dll" -dLIBPQMEM1="libcrypto-3-x64.dll" -dLIBPQMEM2="libintl-9.dll" -dLIBPQMEM3="libwinpthread-1.dll" -dLIBPQMEM4="libiconv-2.dll" -dLIBPQMEM5="" -dLIBPQMEM6="" -dLIBPQMEM7="" -dLIBPQMEM8="" -dLIBPQMEM9="" -dPlatform="$(TARGET_CPU)" -dVERSION=$(PG_VER) -dLIBPQBINDIR=$(PG_BIN) -dLIBPQMSVCDLL="" -dLIBPQMSVCSYS="" -dPODBCMSVCDLL=$(VCRT_DLL) -dPODBCMSVPDLL=$(MSVCP_DLL) -dPODBCMSVCSYS="" -dPODBCMSVPSYS="" -dNoPDB="False" -dBINBASE=".." -o .\x64\psqlodbcm.wixobj psqlodbcm_cpu.wxs
	$(LIGHT) -nologo -o $(TARGET_CPU)\psqlodbc_$(TARGET_CPU).msm $(TARGET_CPU)\psqlodbcm.wixobj

$(TARGET_CPU)\psqlodbc_$(TARGET_CPU).msi: psqlodbc_cpu.wxs $(DRIVER_FILES)
	echo Building Installer
	$(CANDLE) -nologo -dPlatform="$(TARGET_CPU)" -dVERSION=$(POSTGRESDRIVERVERSION) -dSUBLOC=$(SUBLOC) -dPRODUCTCODE=$(PRODUCTCODE) -o $(TARGET_CPU)\psqlodbc.wixobj psqlodbc_cpu.wxs
	$(LIGHT) -nologo -ext WixUIExtension -cultures:en-us -o $(TARGET_CPU)\psqlodbc_$(TARGET_CPU).msi $(TARGET_CPU)\psqlodbc.wixobj
	cscript modify_msi.vbs $(TARGET_CPU)\psqlodbc_$(TARGET_CPU).msi

out of installer.mak. This is where the extra files are added

@apgrucza
Copy link
Contributor Author

apgrucza commented May 4, 2024

When was that? installer.mak hasn't been changed for 10 years.

@davecramer
Copy link
Contributor

Not sure, all I know is that it is in the original code, and that is where the additional dll's are added

@davecramer
Copy link
Contributor

So looking at this some more,

<?if "$(var.LIBPQMEM0)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM0)" />
<?endif ?>
<?if "$(var.LIBPQMEM1)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM1)" />
<?endif ?>
<?if "$(var.LIBPQMEM2)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM2)" />
<?endif ?>
<?if "$(var.LIBPQMEM3)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM3)" />
<?endif ?>
<?if "$(var.LIBPQMEM4)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM4)" />
<?endif ?>
<?if "$(var.LIBPQMEM5)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM5)" />
<?endif ?>
<?if "$(var.LIBPQMEM6)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM6)" />
<?endif ?>
<?if "$(var.LIBPQMEM7)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM7)" />
<?endif ?>
<?if "$(var.LIBPQMEM8)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM8)" />
<?endif ?>
<?if "$(var.LIBPQMEM9)" != "" ?>
<File Source="$(var.LIBPQBINDIR)\$(var.LIBPQMEM9)" />
<?endif ?>
is where those variables are used.
It's becoming clear to me that the actual release steps are not documented anywhere.

You are correct installer.mak hasn't changed and I found the code in a private repo that I have access to.

Either way we need to fix the release code to include those libraries.

Also need to figure out how to get the 32bit versions of those libraries.

@apgrucza
Copy link
Contributor Author

apgrucza commented May 6, 2024

I just took a deeper look too.

installer.mak is not even used by buildInstallers.ps1, so changing that file won't help. In buildInstallers.ps1 there is a call to Get-RelatedDlls which uses dumpbin.exe to inspect libpq.dll and figure out its dependencies. As long as the bin directory referenced in configuration.xml contains these libraries, dumpbin.exe will find them and they will be included in the installer. That's why it worked when I changed configuration.xml to reference the EDB installation (Get-RelatedDlls returned libssl-3-x64.dll libcrypto-3-x64.dll libintl-9.dll libwinpthread-1.dll libiconv-2.dll). So, there should be no need to change the release code.

As you say, we still need to figure out how to get the 32-bit versions of those libraries. @winpg was the one who always prepared and announced psqlODBC releases. Perhaps he could provide some insight here, or is he also unavailable?

@apgrucza
Copy link
Contributor Author

apgrucza commented May 6, 2024

The EDB installer must include those extra DLLs because they are required by PostgreSQL. So I checked the Windows build instructions for PostgreSQL and it lists the requirements. One of them is OpenSSL, and it provides a link to download binaries. I downloaded OpenSSL v3.3.0 Light and found that it includes both the libssl-3 and libcrypto-3 DLLs. So perhaps all we need to do is get those into d:\postgresql86\bin.

@davecramer
Copy link
Contributor

Yes, I am in the process of doing that now!. Thanks

@davecramer
Copy link
Contributor

can you check the installers from https://github.com/postgresql-interfaces/psqlodbc/actions/runs/8992393964

@apgrucza
Copy link
Contributor Author

apgrucza commented May 8, 2024

Both installers now work without errors. However, the x86 installer still does not contain the libssl and libcrypto DLLs.

@apgrucza
Copy link
Contributor Author

apgrucza commented May 8, 2024

One problem I can see is that this:

cp c:\openssl32\*.dll d:\postgresql86\bin

needs to be changed to this:

cp c:\openssl32\bin\*.dll d:\postgresql86\bin

@apgrucza
Copy link
Contributor Author

apgrucza commented May 8, 2024

BTW the usage of actions/cache is causing certain steps to be skipped when they shouldn't be. For example, the "build postgresx86" step in this run was skipped even though the commit that triggered it changed that step. Not sure what the best solution to this is, but one option is to include in the cache key a hash of all files that affect the contents of d:\postgresql86, including .github/workflows/main.yml itself.

@davecramer
Copy link
Contributor

One problem I can see is that this:

cp c:\openssl32\*.dll d:\postgresql86\bin

needs to be changed to this:

cp c:\openssl32\bin\*.dll d:\postgresql86\bin

The dll's are installed in both places so it is currently correct.

@davecramer
Copy link
Contributor

BTW the usage of actions/cache is causing certain steps to be skipped when they shouldn't be. For example, the "build postgresx86" step in this run was skipped even though the commit that triggered it changed that step. Not sure what the best solution to this is, but one option is to include in the cache key a hash of all files that affect the contents of d:\postgresql86, including .github/workflows/main.yml itself.

I'm not sure how to handle that either. I guess we need to figure out how to invalidate the cache when the version of postgres changes as well.

Thanks for testing this

davecramer pushed a commit that referenced this pull request May 8, 2024
… 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
@apgrucza
Copy link
Contributor Author

apgrucza commented May 9, 2024

One problem I can see is that this:

cp c:\openssl32\*.dll d:\postgresql86\bin

needs to be changed to this:

cp c:\openssl32\bin\*.dll d:\postgresql86\bin

The dll's are installed in both places so it is currently correct.

Yes you are correct. So I guess the installers you built weren't working due to the caching issue.

@apgrucza
Copy link
Contributor Author

apgrucza commented May 9, 2024

I disabled the caching in my fork so that I could test your changes. The x86 installer does now install the libssl and libcrypto DLLs. However, the DLL versions are not consistent between x86 and x64.

  • x86: libpq.dll (17.0.0.0), libssl-3.dll (3.3.0.0)
  • x64: libpq.dll (16.0.2.0), libssl-3-x64.dll (3.0.13.0)

Is it possible to build the x64 driver the same way as the x86 driver so that we can have some consistency? And to get the PostgreSQL code from a stable release, from https://www.postgresql.org/ftp/source/?

@davecramer
Copy link
Contributor

Ah, good point. Yes, it is possible to build the driver the same way

@davecramer
Copy link
Contributor

@apgrucza
Copy link
Contributor Author

apgrucza commented May 11, 2024

I have a fix for the caching issue here: #13

@apgrucza
Copy link
Contributor Author

can you check these https://github.com/davecramer/psqlodbc/actions/runs/9036264469 ?

The x86 artifact still contains libpq 17, and looking at the GitHub Actions log reveals that it's because the "build postgresx86" step was again skipped (due to the caching problem). If you want to merge your changes then I can rebase #13 and the problem should go away.

@apgrucza
Copy link
Contributor Author

The EDB installer takes 2m 30s to install. With your recent change, is the EDB installer now only used to run PostgreSQL (so that the psqlodbc tests can run)? If so, we could reduce the workflow duration by running the PostgreSQL Docker image instead.

@davecramer
Copy link
Contributor

The EDB installer takes 2m 30s to install. With your recent change, is the EDB installer now only used to run PostgreSQL (so that the psqlodbc tests can run)? If so, we could reduce the workflow duration by running the PostgreSQL Docker image instead.

I didn't think docker ran on github windows images ?

@apgrucza
Copy link
Contributor Author

I didn't think docker ran on github windows images ?

Oh, that surprises me. I haven't used GitHub Actions much before.

@davecramer
Copy link
Contributor

I think you can run testcontainers which does run docker but I don't think you can specify the version of postgres. It seemed simpler to use EDB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants