Author: rinrab
Date: Sun Dec 28 23:00:09 2025
New Revision: 1930949

Log:
Add OpenSSL backend for checksum computation.

After this change, every component that relies on checksuming would improve in
performance by a lot in thanks to OpenSSL optimised hash algorithms. My
benchmarks showed the following improvements:

- The benchmarking test was showing 0.16 GB/s raw speed with APR backend and
  1.38 seconds GB/s with OpenSSL. All of those are SHA1. This is 8.62 times 
jump!

- Status of a working copy with a single file (with size of 370 MB) simply
  touched was taking 1.94 seconds of real time to execute with APR backend and
  0.30 seconds with OpenSSL (6.46 times improvement).

- The same test with a file of around 3 GB had a jump from 15.73 seconds to
  2.23 seconds (7.05 times improvement).

- Checkout of the whole repository with those two files over file:// protocol
  took 40 seconds with APR and 20 seconds with OpenSSL (2 times improvement).

Currenly, only CMake build system implements support for this feature. As far
as I checked, it should not at least break other build systems, but I didn't
implement support for them either.

This implementation relies on deprecated API of OpenSSL which (I think) is
objectively better than the approach they currenly consider recommended (which
is to use EVP interface).

1) it's just simpler
2) doesn't perform vtable calls
3) and OpenSSL uses it anyway in EVP

(personally, i don't get the reason why it was deprecated in the first place)

This is a completelly optional feature which is disabled by default. If one
doesn't want to use it, we wont force you to. There are many disadvantages of
loading such heavy libraries when we don't need all the stuff it gives us. This
optimisation comes with its own cost. But, our client already loads OpenSSL
indirectly through libserf so this is not an issue in some cases. Let's just
try it. Why not?

* CMakeLists.txt
  (options): Add SVN_CHECKSUM_USE_OPENSSL.
  (SVN_CHECKSUM_USE_OPENSSL): Check for option to decide whether to look for
   the OpenSSL library and setup definitions or not.
  (configsummary): Log value of SVN_CHECKSUM_USE_OPENSSL.

* build.conf
  (libsvn_subr): Add dependency on openssl.

* subversion/libsvn_subr/checksum_apr.c
  (): Put the whole file into an ifndef condition depending on
   SVN_CHECKSUM_USE_OPENSSL.

* subversion/libsvn_subr/checksum_openssl.c
  (svn_checksum__md5,
   svn_checksum__md5_ctx_t,
   svn_checksum__md5_ctx_create,
   svn_checksum__md5_ctx_reset,
   svn_checksum__md5_ctx_update,
   svn_checksum__md5_ctx_final,
   svn_checksum__sha1,
   svn_checksum__sha1_ctx_t,
   svn_checksum__sha1_ctx_create,
   svn_checksum__sha1_ctx_reset,
   svn_checksum__sha1_ctx_update,
   svn_checksum__sha1_ctx_final): Implement through OpenSSL (under an ifdef).

Added:
   subversion/trunk/subversion/libsvn_subr/checksum_openssl.c   (contents, 
props changed)
Modified:
   subversion/trunk/CMakeLists.txt
   subversion/trunk/build.conf
   subversion/trunk/subversion/libsvn_subr/checksum_apr.c

Modified: subversion/trunk/CMakeLists.txt
==============================================================================
--- subversion/trunk/CMakeLists.txt     Sun Dec 28 20:22:21 2025        
(r1930948)
+++ subversion/trunk/CMakeLists.txt     Sun Dec 28 23:00:09 2025        
(r1930949)
@@ -111,6 +111,7 @@ cmake_dependent_option(SVN_BUILD_SHARED_
 option(SVN_DEBUG "Enables specific features for developer builds" OFF)
 cmake_dependent_option(SVN_USE_WIN32_CRASHHANDLER "Enables WIN32 crash 
handler." ON "WIN32" OFF)
 option(SVN_USE_DSO "Defined if svn should try to load DSOs" OFF)
+option(SVN_CHECKSUM_USE_OPENSSL "Defined if svn should use OpenSSL to compute 
checksums." OFF)
 set(SVN_SOVERSION "0" CACHE STRING "Subversion library ABI version")
 mark_as_advanced(SVN_SOVERSION)
 
@@ -383,6 +384,17 @@ if (SVN_ENABLE_RA_SERF)
   endif()
 endif()
 
+if (SVN_CHECKSUM_USE_OPENSSL)
+  find_package(OpenSSL REQUIRED)
+  add_library(external-openssl ALIAS OpenSSL::Crypto)
+  add_private_config_definition(
+    "Defined if svn should use OpenSSL to compute checksums."
+    "SVN_CHECKSUM_USE_OPENSSL" "1"
+  )
+else()
+  add_library(external-openssl INTERFACE)
+endif()
+
 ### Python
 
 if(SVN_ENABLE_SWIG_PYTHON)
@@ -952,6 +964,7 @@ message(STATUS "    Build shared librari
 message(STATUS "    Build shared FS Modues ........ : ${SVN_BUILD_SHARED_FS}")
 message(STATUS "    Build shared RA Modues ........ : ${SVN_BUILD_SHARED_RA}")
 message(STATUS "    Use pkg-config dependencies ... : ${SVN_USE_PKG_CONFIG}")
+message(STATUS "    Use OpenSSL for checksum ...... : 
${SVN_CHECKSUM_USE_OPENSSL}")
 message(STATUS "  FS modules:")
 message(STATUS "    Enable FSFS ................... : ${SVN_ENABLE_FS_FS}")
 message(STATUS "    Enable FSX .................... : ${SVN_ENABLE_FS_X}")

Modified: subversion/trunk/build.conf
==============================================================================
--- subversion/trunk/build.conf Sun Dec 28 20:22:21 2025        (r1930948)
+++ subversion/trunk/build.conf Sun Dec 28 23:00:09 2025        (r1930949)
@@ -394,7 +394,7 @@ install = fsmod-lib
 path = subversion/libsvn_subr
 sources = *.c lz4/*.c
 libs = aprutil apriconv apr xml zlib apr_memcache
-       sqlite magic intl lz4 utf8proc macos-plist macos-keychain
+       sqlite magic intl lz4 utf8proc macos-plist macos-keychain openssl
 msvc-libs = kernel32.lib advapi32.lib shfolder.lib ole32.lib
             crypt32.lib version.lib ws2_32.lib
 msvc-export = 

Modified: subversion/trunk/subversion/libsvn_subr/checksum_apr.c
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/checksum_apr.c      Sun Dec 28 
20:22:21 2025        (r1930948)
+++ subversion/trunk/subversion/libsvn_subr/checksum_apr.c      Sun Dec 28 
23:00:09 2025        (r1930949)
@@ -22,6 +22,7 @@
  */
 
 #include "svn_private_config.h"
+#ifndef SVN_CHECKSUM_USE_OPENSSL
 
 #define APR_WANT_BYTEFUNC
 
@@ -130,3 +131,5 @@ svn_checksum__sha1_ctx_final(unsigned ch
   return SVN_NO_ERROR;
 }
 
+#endif /* SVN_CHECKSUM_USE_OPENSSL */
+

Added: subversion/trunk/subversion/libsvn_subr/checksum_openssl.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ subversion/trunk/subversion/libsvn_subr/checksum_openssl.c  Sun Dec 28 
23:00:09 2025        (r1930949)
@@ -0,0 +1,159 @@
+/*
+ * checksum_openssl.c:   OpenSSL backed checksums
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include "svn_private_config.h"
+#ifdef SVN_CHECKSUM_USE_OPENSSL
+
+#define APR_WANT_BYTEFUNC
+
+#include <apr_md5.h>
+#include <apr_sha1.h>
+
+#include "svn_error.h"
+#include "checksum.h"
+
+/* There is an alternative way to compute checksums in OpenSSL which is to use
+ * their EVP interface. Here are the arguments why we are sticking with this
+ * one even though it's deprecated in OpenSSL v3.0:
+ *
+ * - EVP provides much more complicated interface.
+ *
+ * - We don't need the most of the features it gives us.
+ *
+ * - It might affect performance because there are vtable calls involved.
+ *
+ * - The default implementation used in EVP actually relies the exact same set
+ *   of functions under the hood.
+ */
+
+#define OPENSSL_SUPPRESS_DEPRECATED
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+
+
+/*** MD5 checksum ***/
+svn_error_t *
+svn_checksum__md5(unsigned char *digest,
+                  const void *data,
+                  apr_size_t len)
+{
+  MD5_CTX ctx;
+
+  MD5_Init(&ctx);
+  MD5_Update(&ctx, data, len);
+  MD5_Final(digest, &ctx);
+
+  return SVN_NO_ERROR;
+}
+
+svn_checksum__md5_ctx_t *
+svn_checksum__md5_ctx_create(apr_pool_t *pool)
+{
+  MD5_CTX *result = apr_palloc(pool, sizeof(*result));
+  MD5_Init(result);
+  return (svn_checksum__md5_ctx_t *)result;
+}
+
+svn_error_t *
+svn_checksum__md5_ctx_reset(svn_checksum__md5_ctx_t *ctx)
+{
+  MD5_CTX *md5_ctx = (MD5_CTX *)ctx;
+  memset(md5_ctx, 0, sizeof(*md5_ctx));
+  MD5_Init(md5_ctx);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_checksum__md5_ctx_update(svn_checksum__md5_ctx_t *ctx,
+                             const void *data,
+                             apr_size_t len)
+{
+  MD5_CTX *md5_ctx = (MD5_CTX *)ctx;
+  MD5_Update(md5_ctx, data, len);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_checksum__md5_ctx_final(unsigned char *digest,
+                            const svn_checksum__md5_ctx_t *ctx)
+{
+  MD5_CTX *md5_ctx = (MD5_CTX *)ctx;
+  MD5_Final(digest, md5_ctx);
+  return SVN_NO_ERROR;
+}
+
+
+/*** SHA1 checksum ***/
+svn_error_t *
+svn_checksum__sha1(unsigned char *digest,
+                   const void *data,
+                   apr_size_t len)
+{
+  SHA_CTX ctx;
+
+  SHA1_Init(&ctx);
+  SHA1_Update(&ctx, data, len);
+  SHA1_Final(digest, &ctx);
+
+  return SVN_NO_ERROR;
+}
+
+svn_checksum__sha1_ctx_t *
+svn_checksum__sha1_ctx_create(apr_pool_t *pool)
+{
+  SHA_CTX *result = apr_palloc(pool, sizeof(*result));
+  SHA1_Init(result);
+  return (svn_checksum__sha1_ctx_t *)result;
+}
+
+svn_error_t *
+svn_checksum__sha1_ctx_reset(svn_checksum__sha1_ctx_t *ctx)
+{
+  SHA_CTX *sha_ctx = (SHA_CTX *)ctx;
+  memset(sha_ctx, 0, sizeof(*sha_ctx));
+  SHA1_Init(sha_ctx);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_checksum__sha1_ctx_update(svn_checksum__sha1_ctx_t *ctx,
+                             const void *data,
+                             apr_size_t len)
+{
+  SHA_CTX *sha_ctx = (SHA_CTX *)ctx;
+  SHA1_Update(sha_ctx, data, len);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_checksum__sha1_ctx_final(unsigned char *digest,
+                            const svn_checksum__sha1_ctx_t *ctx)
+{
+
+  SHA_CTX *sha_ctx = (SHA_CTX *)ctx;
+  SHA1_Final(digest, sha_ctx);
+  return SVN_NO_ERROR;
+}
+
+#endif /* SVN_CHECKSUM_USE_OPENSSL */
+

Reply via email to