changeset 7093:fc7b11559381 streaming

clarifying computation of missing performance options
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 13 Aug 2026 11:45:02 +0000
parents 8bdb4ff3b484
children 1159a299587c
files OrthancFramework/Sources/DataSource/DicomSequentialReader.h OrthancServer/Resources/AdvancedConfiguration.json OrthancServer/Sources/OrthancConfiguration.cpp OrthancServer/Sources/OrthancConfiguration.h OrthancServer/Sources/ServerContext.cpp
diffstat 5 files changed, 82 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/DataSource/DicomSequentialReader.h	Thu Aug 13 10:53:03 2026 +0000
+++ b/OrthancFramework/Sources/DataSource/DicomSequentialReader.h	Thu Aug 13 11:45:02 2026 +0000
@@ -120,6 +120,11 @@
               unsigned int windowSize,
               uint64_t windowCapacity);
 
+      const boost::shared_ptr<IExecutorService>& GetExecutorService() const
+      {
+        return executor_;
+      }
+
       /**
        * Methods below prioritize the downloading of the raw DICOM
        * files (and avoid DICOM parsing if possible).
--- a/OrthancServer/Resources/AdvancedConfiguration.json	Thu Aug 13 10:53:03 2026 +0000
+++ b/OrthancServer/Resources/AdvancedConfiguration.json	Thu Aug 13 11:45:02 2026 +0000
@@ -404,7 +404,7 @@
   // Together with "SequentialDicomReaderWindowCapacity", this also defines the amount of memory that is
   // used by each job that required a SequentialDicomReader.
   // By default, the value of this configuration is the same as "LoaderThreadsCount". (new in Orthanc 1.13.0)
-  // "SequentialDicomReaderWindowSize" : 4,
+  "SequentialDicomReaderWindowSize" : 4,
 
   // Peak amount of RAM (in MB) that can be allocated by each
   // sequential DICOM reader. The sequential DICOM reader pauses
--- a/OrthancServer/Sources/OrthancConfiguration.cpp	Thu Aug 13 10:53:03 2026 +0000
+++ b/OrthancServer/Sources/OrthancConfiguration.cpp	Thu Aug 13 11:45:02 2026 +0000
@@ -48,6 +48,10 @@
 static const char* const JOBS_ENGINE_THREADS_COUNT = "JobsEngineThreadsCount";
 static const char* const DICOM_LOSSY_TRANSCODING_QUALITY = "DicomLossyTranscodingQuality";
 
+static const char* const ORTHANC_CONFIG_LOADER_THREADS = "LoaderThreads";         // for backward compatibility only
+static const char* const ORTHANC_CONFIG_ZIP_LOADER_THREADS = "ZipLoaderThreads";  // for backward compatibility only
+
+
 namespace Orthanc
 {
   static void ReadConfigurationFromString(Json::Value& target,
@@ -1482,23 +1486,44 @@
     }
   }
 
-  unsigned int OrthancConfiguration::GetLoaderThreads() const
+
+  bool OrthancConfiguration::LookupCompatibilityLoaderThreads(unsigned int& threadsCount,
+                                                              std::string& fromOption) const
   {
-    // from 1.10.0 to 1.12.10, only CONFIG_ZIP_LOADER_THREADS was available -> read from it if CONFIG_LOADER_THREADS is not specified.
-    unsigned int loaderThreads = 1; // old ZipLoaderThreads default value
+    // This method corresponds to OrthancConfiguration::GetLoaderThreads() in Orthanc <= 1.12.11
+    bool found;
 
-    if (!LookupUnsignedIntegerParameter(loaderThreads, ORTHANC_CONFIG_LOADER_THREADS))
+    if (LookupUnsignedIntegerParameter(threadsCount, ORTHANC_CONFIG_LOADER_THREADS))
     {
-      LookupUnsignedIntegerParameter(loaderThreads, ORTHANC_CONFIG_ZIP_LOADER_THREADS); // we cannot use GetUnsignedIntegerParameter() because there is no default value for this old configuration
+      found = true;
+      fromOption = ORTHANC_CONFIG_LOADER_THREADS;
     }
-
-    if (loaderThreads <= 1)
+    else if (LookupUnsignedIntegerParameter(threadsCount, ORTHANC_CONFIG_ZIP_LOADER_THREADS))
     {
-      return 1; // 0 is not a valid internal value anymore
+      // from 1.10.0 to 1.12.10, only CONFIG_ZIP_LOADER_THREADS was available
+      // => read from it if CONFIG_LOADER_THREADS is not specified.
+      found = true;
+      fromOption = ORTHANC_CONFIG_ZIP_LOADER_THREADS;
     }
     else
     {
-      return loaderThreads;
+      // In Orthanc <= 1.12.11, the returned number of threads was 1 in this case,
+      // corresponding to the old ZipLoaderThreads default value
+      found = false;
+    }
+
+    if (found)
+    {
+      if (threadsCount < 1)
+      {
+        threadsCount = 1;  // 0 is not a valid internal value anymore
+      }
+
+      return true;
+    }
+    else
+    {
+      return false;
     }
   }
 }
--- a/OrthancServer/Sources/OrthancConfiguration.h	Thu Aug 13 10:53:03 2026 +0000
+++ b/OrthancServer/Sources/OrthancConfiguration.h	Thu Aug 13 11:45:02 2026 +0000
@@ -58,9 +58,6 @@
 #define ORTHANC_CONFIG_DICOM_THREADS_COUNT "DicomThreadsCount"
 #define ORTHANC_CONFIG_STORAGE_DIRECTORY "StorageDirectory"
 
-#define ORTHANC_CONFIG_LOADER_THREADS "LoaderThreads"
-#define ORTHANC_CONFIG_ZIP_LOADER_THREADS "ZipLoaderThreads"   // for backward compatibility only
-
 
 
 namespace Orthanc
@@ -269,8 +266,6 @@
 
     void RemovePeer(const std::string& symbolicName);
 
-    unsigned int GetLoaderThreads() const;
-
     unsigned int GetConcurrentJobs() const
     {
       return GetUnsignedIntegerParameter(ORTHANC_CONFIG_CONCURRENT_JOBS);
@@ -385,5 +380,9 @@
 
     static void ParseAcceptedTransferSyntaxes(std::set<DicomTransferSyntax>& target,
                                               const Json::Value& source);
+
+    // This method provides backward compatibility against old configuration options
+    bool LookupCompatibilityLoaderThreads(unsigned int& threadsCount,
+                                          std::string& fromOption) const;
   };
 }
--- a/OrthancServer/Sources/ServerContext.cpp	Thu Aug 13 10:53:03 2026 +0000
+++ b/OrthancServer/Sources/ServerContext.cpp	Thu Aug 13 11:45:02 2026 +0000
@@ -728,27 +728,35 @@
           LOG(WARNING) << "Storage cache is disabled";
         }
 
+        unsigned int compatibilityLoaderThreadsCount;
+        std::string compatibilityLoaderThreadsOption;
+        bool hasCompatibilityLoaderThreads = lock.GetConfiguration().LookupCompatibilityLoaderThreads(
+          compatibilityLoaderThreadsCount, compatibilityLoaderThreadsOption);
+
         if (!lock.GetConfiguration().LookupUnsignedIntegerParameter(storageLoaderThreads, ORTHANC_CONFIG_STORAGE_LOADER_THREADS_COUNT))
         {
-          const unsigned int loaderThreads = lock.GetConfiguration().GetLoaderThreads();
           const unsigned int concurrentJobs = lock.GetConfiguration().GetConcurrentJobs();
 
-          storageLoaderThreads = loaderThreads * concurrentJobs;
-          if (storageLoaderThreads < 4)
+          if (hasCompatibilityLoaderThreads)
           {
-            storageLoaderThreads = 4;
-            LOG(WARNING) << "Performance option \"" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS_COUNT
-                         << "\" is not defined in your configuration, setting it to " << storageLoaderThreads;
+            storageLoaderThreads = concurrentJobs * compatibilityLoaderThreadsCount;
           }
           else
           {
-            static const unsigned int CAP = 50;
-            storageLoaderThreads = std::min(CAP, storageLoaderThreads);
-            LOG(WARNING) << "Performance option \"" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS_COUNT
-                         << "\" is not defined in your configuration, setting it to " << storageLoaderThreads
-                         << ", based on the \"" << ORTHANC_CONFIG_CONCURRENT_JOBS << "\" and the \""
-                         << ORTHANC_CONFIG_LOADER_THREADS << "\" options capped at " << CAP;
+            storageLoaderThreads = concurrentJobs;
           }
+
+          static const unsigned int CAP_LOW = 4;
+          static const unsigned int CAP_HIGH = 50;
+
+          storageLoaderThreads = std::max(storageLoaderThreads, CAP_LOW);
+          storageLoaderThreads = std::min(storageLoaderThreads, CAP_HIGH);
+
+          LOG(WARNING) << "Performance option \"" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS_COUNT
+                       << "\" is not defined in your configuration, setting it to " << storageLoaderThreads
+                       << ", based on the \"" << ORTHANC_CONFIG_CONCURRENT_JOBS << "\" "
+                       << (hasCompatibilityLoaderThreads ? "and the \"" + compatibilityLoaderThreadsOption + "\" options" : "option")
+                       << " capped to the range [" << CAP_LOW << "," << CAP_HIGH << "]";
         }
         else
         {
@@ -783,11 +791,19 @@
 
         if (!lock.GetConfiguration().LookupUnsignedIntegerParameter(sequentialReaderWindowSize, ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE))
         {
-          const unsigned int loaderThreads = lock.GetConfiguration().GetLoaderThreads();
-          LOG(WARNING) << "Performance option \"" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE
-                       << "\" is not defined in your configuration, setting it to the same value as \""
-                       << ORTHANC_CONFIG_LOADER_THREADS << "\": " << loaderThreads;
-          sequentialReaderWindowSize = loaderThreads;
+          if (hasCompatibilityLoaderThreads)
+          {
+            LOG(WARNING) << "Performance option \"" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE
+                         << "\" is not defined in your configuration, setting it to the same value as \""
+                         << compatibilityLoaderThreadsOption << "\": " << compatibilityLoaderThreadsCount;
+            sequentialReaderWindowSize = compatibilityLoaderThreadsCount;
+          }
+          else
+          {
+            sequentialReaderWindowSize = lock.GetConfiguration().GetUnsignedIntegerParameter(ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE);
+            LOG(WARNING) << "Performance option \"" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE
+                         << "\" is not defined in your configuration, setting it to " << sequentialReaderWindowSize;
+          }
         }
         else
         {
@@ -2498,6 +2514,11 @@
     }
 
     {
+      boost::shared_ptr<IExecutorService> service = dicomSequentialReaderFactory_->GetExecutorService();
+      target[ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_THREADS_COUNT] = dynamic_cast<ThreadPool&>(*service).GetThreadsCount();
+    }
+
+    {
       OrthancConfiguration::ReaderLock lock;
       target[ORTHANC_CONFIG_HTTP_THREADS_COUNT] = lock.GetConfiguration().GetHttpThreadsCount();
       target[ORTHANC_CONFIG_DICOM_THREADS_COUNT] = lock.GetConfiguration().GetDicomThreadsCount();