changeset 7073:c762f878e1f7 streaming

consistency in new configuration options
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 12 Aug 2026 12:36:31 +0200
parents 09528940289e
children a4a2eaf3401c
files NEWS OrthancFramework/Sources/Cache/SharedObjectCache.cpp OrthancFramework/Sources/Cache/SharedObjectCache.h OrthancFramework/Sources/DataSource/DataSourceReader.cpp OrthancFramework/Sources/DataSource/DataSourceReader.h OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp OrthancServer/Sources/ServerContext.cpp
diffstat 7 files changed, 104 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Aug 12 11:38:36 2026 +0200
+++ b/NEWS	Wed Aug 12 12:36:31 2026 +0200
@@ -6,7 +6,7 @@
 
 * Full rewrite of access to storage area and of DICOM parsing/transcoding
   to improve caching and to limit memory usage.  
-  This change brings updated configurations (TODO: review):
+  This change brings updated configurations (TODO-Streaming: review):
   - "StorageLoaderThreads"
   - "StorageMemoryCapacity"
   - "DicomParserThreads"
@@ -19,7 +19,7 @@
   - "SequentialDicomReaderWindowSize"
   - "SequentialDicomReaderWindowCapacity"
   And new metrics:
-  - TODO list
+  - TODO-Streaming list
 * Multiple paths to configuration files can now be provided, instead of a single file or a single folder
 * Split the configuration into a main file and a second file for advanced settings
 * Orthanc will now refuse to start if you have "AuthenticationEnabled" and "RemoteAccessAllowed"
@@ -66,9 +66,8 @@
   TODO: for the release, we may go backward to 31 but we need to change the conditional 
   tests - see this commit: https://orthanc.uclouvain.be/hg/orthanc-tests/rev/3a440ee47213)
 * New fields reported in the "/system" route:
-  - "OverwriteInstancesMode" (Boolean field "OverwriteInstances" is kept for backward
-    compatibility)
-  - "MaximumStorageCacheSize"
+  - "OverwriteInstancesMode" (Boolean field "OverwriteInstances" is kept for backward compatibility)
+  - "Performance" reports configuration options that may affect performance of Orthanc
   - "StoreMD5ForAttachments"
 * The "LocalAet" field of the payload to "/modalities/../move", "/modalities/../store", 
   "/modalities/../get", "queries/../answers/../retrieve" now always overwrites the "DicomAet"
--- a/OrthancFramework/Sources/Cache/SharedObjectCache.cpp	Wed Aug 12 11:38:36 2026 +0200
+++ b/OrthancFramework/Sources/Cache/SharedObjectCache.cpp	Wed Aug 12 12:36:31 2026 +0200
@@ -81,10 +81,14 @@
   }
 
 
-  SharedObjectCache::SharedObjectCache(size_t capacity) :
+  SharedObjectCache::SharedObjectCache(uint64_t capacity) :
     capacity_(capacity),
     currentSize_(0)
   {
+    if (static_cast<uint64_t>(static_cast<size_t>(capacity)) != capacity)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
   }
 
 
--- a/OrthancFramework/Sources/Cache/SharedObjectCache.h	Wed Aug 12 11:38:36 2026 +0200
+++ b/OrthancFramework/Sources/Cache/SharedObjectCache.h	Wed Aug 12 12:36:31 2026 +0200
@@ -49,7 +49,7 @@
     void MakeRoom(size_t newObjectSize);
 
   public:
-    explicit SharedObjectCache(size_t capacity);
+    explicit SharedObjectCache(uint64_t capacity);
 
     ~SharedObjectCache();
 
--- a/OrthancFramework/Sources/DataSource/DataSourceReader.cpp	Wed Aug 12 11:38:36 2026 +0200
+++ b/OrthancFramework/Sources/DataSource/DataSourceReader.cpp	Wed Aug 12 12:36:31 2026 +0200
@@ -284,7 +284,7 @@
   }
 
 
-  void DataSourceReader::CreateCache(size_t capacity)
+  void DataSourceReader::CreateCache(uint64_t capacity)
   {
     cache_ = boost::make_shared<SharedObjectCache>(capacity);
   }
@@ -294,11 +294,12 @@
     metricsConfiguration_ = configuration;
     if (budget_.get())
     {
-      budget_->SetMetricsConfiguration(Internals::DataSourceMemoryBudget::MetricsConfiguration(configuration.metrics_,
-                                                                                               configuration.capacityMaxSizeMegabytesName_,
-                                                                                               configuration.capacityCurrentSizeMegabytesName_,
-                                                                                               configuration.capacityCountName_,
-                                                                                               configuration.capacityMaxUsageSinceStartMegabytesName_));
+      budget_->SetMetricsConfiguration(Internals::DataSourceMemoryBudget::MetricsConfiguration(
+                                         configuration.metrics_,
+                                         configuration.capacityMaxSizeMegabytesName_,
+                                         configuration.capacityCurrentSizeMegabytesName_,
+                                         configuration.capacityCountName_,
+                                         configuration.capacityMaxUsageSinceStartMegabytesName_));
     }
   }
 
@@ -306,11 +307,12 @@
   void DataSourceReader::SetCapacity(uint64_t maximumMemory)
   {
     budget_ = boost::make_shared<Internals::DataSourceMemoryBudget>(maximumMemory);
-    budget_->SetMetricsConfiguration(Internals::DataSourceMemoryBudget::MetricsConfiguration(metricsConfiguration_.metrics_,
-                                                                                             metricsConfiguration_.capacityMaxSizeMegabytesName_,
-                                                                                             metricsConfiguration_.capacityCurrentSizeMegabytesName_,
-                                                                                             metricsConfiguration_.capacityCountName_,
-                                                                                             metricsConfiguration_.capacityMaxUsageSinceStartMegabytesName_));
+    budget_->SetMetricsConfiguration(Internals::DataSourceMemoryBudget::MetricsConfiguration(
+                                       metricsConfiguration_.metrics_,
+                                       metricsConfiguration_.capacityMaxSizeMegabytesName_,
+                                       metricsConfiguration_.capacityCurrentSizeMegabytesName_,
+                                       metricsConfiguration_.capacityCountName_,
+                                       metricsConfiguration_.capacityMaxUsageSinceStartMegabytesName_));
   }
 
   uint64_t DataSourceReader::GetCapacity() const
--- a/OrthancFramework/Sources/DataSource/DataSourceReader.h	Wed Aug 12 11:38:36 2026 +0200
+++ b/OrthancFramework/Sources/DataSource/DataSourceReader.h	Wed Aug 12 12:36:31 2026 +0200
@@ -105,7 +105,7 @@
 
     void SetMetricsConfiguration(const MetricsConfiguration& configuration);
 
-    void CreateCache(size_t capacity);
+    void CreateCache(uint64_t capacity);
 
     /**
      * Apply backpressure by limiting memory for pending read
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Wed Aug 12 11:38:36 2026 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Wed Aug 12 12:36:31 2026 +0200
@@ -88,6 +88,7 @@
     static const char* const HAS_QUEUES = "HasQueues";
     static const char* const HAS_EXTENDED_FIND = "HasExtendedFind";
     static const char* const HAS_RESERVE_QUEUE_VALUE = "HasReserveQueueValue";
+    static const char* const PERFORMANCE = "Performance";
 
     if (call.IsDocumentation())
     {
@@ -122,13 +123,11 @@
         .SetAnswerField(ORTHANC_CONFIG_STORAGE_COMPRESSION, RestApiCallDocumentation::Type_Boolean,
                         "Whether storage compression is enabled (new in Orthanc 1.11.0)")
         .SetAnswerField(ORTHANC_CONFIG_OVERWRITE_INSTANCES, RestApiCallDocumentation::Type_Boolean,
-                        "Whether instances are overwritten when re-ingested (new in Orthanc 1.11.0 and kept as a bool for backward compatibility)")
+                        "Whether instances are overwritten when re-ingested (new in Orthanc 1.11.0 and kept as a Boolean for backward compatibility)")
         .SetAnswerField(OVERWRITE_INSTANCES_MODE, RestApiCallDocumentation::Type_String,
-                        "Whether instances are overwritten when re-ingested (new in Orthanc 1.13.0)")
+                        "Overwriting mode governing how instances are re-ingested (new in Orthanc 1.13.0)")
         .SetAnswerField(ORTHANC_CONFIG_INGEST_TRANSCODING, RestApiCallDocumentation::Type_String,
                         "Whether instances are transcoded when ingested into Orthanc (`""` if no transcoding is performed) (new in Orthanc 1.11.0)")
-        .SetAnswerField(ORTHANC_CONFIG_MAXIMUM_STORAGE_CACHE_SIZE, RestApiCallDocumentation::Type_Number,
-                        std::string("The configured ") + ORTHANC_CONFIG_MAXIMUM_STORAGE_CACHE_SIZE + " in MB (new in Orthanc 1.13.0)")
         .SetAnswerField(ORTHANC_CONFIG_STORE_MD5_FOR_ATTACHMENTS, RestApiCallDocumentation::Type_Boolean,
                         std::string("The configured ") + ORTHANC_CONFIG_STORE_MD5_FOR_ATTACHMENTS + " (new in Orthanc 1.13.0)")
         .SetAnswerField(ORTHANC_CONFIG_MAXIMUM_STORAGE_SIZE, RestApiCallDocumentation::Type_Number,
@@ -149,6 +148,8 @@
                         "Whether Orthanc is running in read only mode (new in Orthanc 1.12.5)")
         .SetAnswerField(ORTHANC_CONFIG_PATIENT_LEVEL_ENABLED, RestApiCallDocumentation::Type_Boolean,
                         "Whether Patient level routes and sanity checks are enabled (new in Orthanc 1.12.11)")
+        .SetAnswerField(PERFORMANCE, RestApiCallDocumentation::Type_JsonObject,
+                        "The performance options from the configuration file, with sizes expressed in MB (new in Orthanc 1.13.0)")
         .SetHttpGetSample("https://orthanc.uclouvain.be/demo/system", true);
       return;
     }
@@ -156,6 +157,7 @@
     ServerContext& context = OrthancRestApi::GetContext(call);
 
     Json::Value result = Json::objectValue;
+    Json::Value performance = Json::objectValue;
 
     result[API_VERSION] = ORTHANC_API_VERSION;
     result[VERSION] = ORTHANC_VERSION;
@@ -164,12 +166,12 @@
 
     {
       OrthancConfiguration::ReaderLock lock;
+
       result[ORTHANC_CONFIG_NAME] = lock.GetConfiguration().GetOrthancName();
       result[ORTHANC_CONFIG_DICOM_AET] = lock.GetConfiguration().GetOrthancAET();
       result[ORTHANC_CONFIG_DICOM_PORT] = lock.GetConfiguration().GetDicomPort();
       result[ORTHANC_CONFIG_HTTP_PORT] = lock.GetConfiguration().GetHttpPort();
       result[ORTHANC_CONFIG_CHECK_REVISIONS] = lock.GetConfiguration().HasCheckRevisions();  // New in Orthanc 1.9.2
-      result[ORTHANC_CONFIG_MAXIMUM_STORAGE_CACHE_SIZE] = lock.GetConfiguration().GetMaximumStorageCacheSize(); // New in Orthanc 1.13.0
       result[ORTHANC_CONFIG_STORE_MD5_FOR_ATTACHMENTS] = lock.GetConfiguration().HasStoreMD5ForAttachments(); // New in Orthanc 1.13.0
       result[ORTHANC_CONFIG_STORAGE_COMPRESSION] = lock.GetConfiguration().HasStorageCompression(); // New in Orthanc 1.11.0
       result[ORTHANC_CONFIG_DATABASE_SERVER_IDENTIFIER] = lock.GetConfiguration().GetDatabaseServerIdentifier();
@@ -177,12 +179,15 @@
       result[ORTHANC_CONFIG_MAXIMUM_PATIENT_COUNT] = lock.GetConfiguration().GetMaximumPatientCount(); // New in Orthanc 1.12.4
       result[ORTHANC_CONFIG_MAXIMUM_STORAGE_MODE] = lock.GetConfiguration().GetMaximumStorageMode(); // New in Orthanc 1.11.3
       result[ORTHANC_CONFIG_DICOM_DEFAULT_RETRIEVE_METHOD] = lock.GetConfiguration().GetDicomDefaultRetrieveMethod();
-      result[ORTHANC_CONFIG_CONCURRENT_JOBS] = lock.GetConfiguration().GetConcurrentJobs();
-      result[ORTHANC_CONFIG_HTTP_THREADS_COUNT] = lock.GetConfiguration().GetHttpThreadsCount();
-      result[ORTHANC_CONFIG_DICOM_THREADS_COUNT] = lock.GetConfiguration().GetDicomThreadsCount();
+
+      performance[ORTHANC_CONFIG_HTTP_THREADS_COUNT] = lock.GetConfiguration().GetHttpThreadsCount();
+      performance[ORTHANC_CONFIG_DICOM_THREADS_COUNT] = lock.GetConfiguration().GetDicomThreadsCount();
+      performance[ORTHANC_CONFIG_CONCURRENT_JOBS] = lock.GetConfiguration().GetConcurrentJobs();
     }
 
     {
+      // New in Orthanc 1.13.0
+
       uint64_t storageMemoryCapacity, transcoderMemoryCapacity, dicomParserMemoryCapacity;
       size_t storageMemoryCache, transcoderMemoryCache, dicomParserMemoryCache;
       unsigned int storageReaderThreadsCount, transcoderReaderThreadsCount, dicomParserThreadsCount;
@@ -191,17 +196,19 @@
                                           transcoderMemoryCapacity, transcoderMemoryCache, transcoderReaderThreadsCount,
                                           dicomParserMemoryCapacity, dicomParserMemoryCache, dicomParserThreadsCount);
 
-      result[ORTHANC_CONFIG_STORAGE_MEMORY_CAPACITY] = BytesToMegabytes(storageMemoryCapacity);
-      result[ORTHANC_CONFIG_MAXIMUM_STORAGE_CACHE_SIZE] = BytesToMegabytes(storageMemoryCache);
-      result[ORTHANC_CONFIG_STORAGE_LOADER_THREADS] = storageReaderThreadsCount;
-      result[ORTHANC_CONFIG_TRANSCODER_MEMORY_CAPACITY] = BytesToMegabytes(transcoderMemoryCapacity);
-      result[ORTHANC_CONFIG_TRANSCODER_CACHE_SIZE] = BytesToMegabytes(transcoderMemoryCache);
-      result[ORTHANC_CONFIG_TRANSCODER_THREADS] = transcoderReaderThreadsCount;
-      result[ORTHANC_CONFIG_DICOM_PARSER_MEMORY_CAPACITY] = BytesToMegabytes(dicomParserMemoryCapacity);
-      result[ORTHANC_CONFIG_DICOM_PARSER_CACHE_SIZE] = BytesToMegabytes(dicomParserMemoryCache);
-      result[ORTHANC_CONFIG_DICOM_PARSER_SOURCE_THREADS] = dicomParserThreadsCount;
+      performance[ORTHANC_CONFIG_DICOM_PARSER_CACHE_SIZE] = BytesToMegabytes(dicomParserMemoryCache);
+      performance[ORTHANC_CONFIG_DICOM_PARSER_MEMORY_CAPACITY] = BytesToMegabytes(dicomParserMemoryCapacity);
+      performance[ORTHANC_CONFIG_DICOM_PARSER_SOURCE_THREADS] = dicomParserThreadsCount;
+      performance[ORTHANC_CONFIG_MAXIMUM_STORAGE_CACHE_SIZE] = BytesToMegabytes(storageMemoryCache);
+      performance[ORTHANC_CONFIG_STORAGE_LOADER_THREADS] = storageReaderThreadsCount;
+      performance[ORTHANC_CONFIG_STORAGE_MEMORY_CAPACITY] = BytesToMegabytes(storageMemoryCapacity);
+      performance[ORTHANC_CONFIG_TRANSCODER_CACHE_SIZE] = BytesToMegabytes(transcoderMemoryCache);
+      performance[ORTHANC_CONFIG_TRANSCODER_MEMORY_CAPACITY] = BytesToMegabytes(transcoderMemoryCapacity);
+      performance[ORTHANC_CONFIG_TRANSCODER_THREADS] = transcoderReaderThreadsCount;
     }
 
+    result[PERFORMANCE] = performance;
+
     DicomTransferSyntax ingestTransferSyntax;
     if (context.LookupIngestTranscoding(ingestTransferSyntax))
     {
--- a/OrthancServer/Sources/ServerContext.cpp	Wed Aug 12 11:38:36 2026 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Wed Aug 12 12:36:31 2026 +0200
@@ -498,20 +498,23 @@
   }
 
 
-  static void GetMemoryConfiguration(unsigned int& resultMb, 
-                                     OrthancConfiguration::ReaderLock& lock,
-                                     const char* configurationName)
+  static void GetMemorySizeConfiguration(uint64_t& result,
+                                         OrthancConfiguration::ReaderLock& lock,
+                                         const char* parameter)
   {
-    if (!lock.GetConfiguration().LookupUnsignedIntegerParameter(resultMb, configurationName))
+    unsigned int mb;
+    if (!lock.GetConfiguration().LookupUnsignedIntegerParameter(mb, parameter))
     {
-      resultMb = lock.GetConfiguration().GetUnsignedIntegerParameter(configurationName);
-      LOG(WARNING) << "====> '" << configurationName << "' is not defined in your configuration, setting it to "
-                   << resultMb << "MB. Depending on the available memory on the system, you may want to adapt this value.";
+      mb = lock.GetConfiguration().GetUnsignedIntegerParameter(parameter);
+      LOG(WARNING) << "Performance option '" << parameter << "' is not defined in your configuration, setting it to "
+                   << mb << "MB. Depending on the available memory on the system, you may want to adapt this value.";
     }
     else
     {
-      LOG(WARNING) << "'" << configurationName << "' is set to " << resultMb;
+      LOG(WARNING) << "Performance option '" << parameter << "' is set to " << mb << "MB";
     }
+
+    result = static_cast<uint64_t>(mb) * MEGABYTE;
   }
 
 
@@ -695,15 +698,22 @@
 #endif
 
       unsigned int storageLoaderThreads, transcoderThreads, dicomParserThreads;
-      unsigned int storageCacheSizeMb, transcoderCacheSizeMb, dicomParserCacheSizeMb;
-      unsigned int storageMemoryCapacityMb, transcoderMemoryCapacityMb, dicomParserMemoryCapacityMb;
-      unsigned int sequentialReaderThreads, sequentialReaderWindowSize, sequentialReaderWindowCapacityMb;
+      unsigned int sequentialReaderThreads, sequentialReaderWindowSize;
+
+      uint64_t storageCacheSize, storageMemoryCapacity, dicomParserMemoryCapacity;
+      uint64_t dicomParserCacheSize, transcoderMemoryCapacity, transcoderCacheSize, sequentialReaderWindowCapacity;
       
       {
         OrthancConfiguration::ReaderLock lock;
         unsigned int loaderThreads = lock.GetConfiguration().GetLoaderThreads();
         unsigned int concurrentJobs = lock.GetConfiguration().GetConcurrentJobs();
-        storageCacheSizeMb = lock.GetConfiguration().GetMaximumStorageCacheSize();
+
+        GetMemorySizeConfiguration(storageCacheSize, lock, ORTHANC_CONFIG_MAXIMUM_STORAGE_CACHE_SIZE);
+
+        if (storageCacheSize == 0)
+        {
+          LOG(WARNING) << "Storage cache is disabled";
+        }
 
         if (!lock.GetConfiguration().LookupUnsignedIntegerParameter(storageLoaderThreads, ORTHANC_CONFIG_STORAGE_LOADER_THREADS))
         {
@@ -711,52 +721,62 @@
           if (storageLoaderThreads < 4)
           {
             storageLoaderThreads = 4;
-            LOG(WARNING) << "'" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS << "' is not defined in your configuration, setting it to " << storageLoaderThreads;
+            LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS
+                         << "' is not defined in your configuration, setting it to " << storageLoaderThreads;
           }
           else
           {
-            storageLoaderThreads = std::min(50u, storageLoaderThreads);
-            LOG(WARNING) << "'" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS << "' is not defined in your configuration, setting it to " << storageLoaderThreads << ", based on the '" << ORTHANC_CONFIG_CONCURRENT_JOBS << "' and the '" << ORTHANC_CONFIG_LOADER_THREADS << "' configurations capped at 50.";
+            static const unsigned int CAP = 50;
+            storageLoaderThreads = std::min(CAP, storageLoaderThreads);
+            LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS
+                         << "' 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;
           }
         }
         else
         {
-          LOG(WARNING) << "'" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS << "' is set to " << storageLoaderThreads;
+          LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS << "' is set to " << storageLoaderThreads;
         }
 
         transcoderThreads = lock.GetConfiguration().GetUnsignedIntegerParameter(ORTHANC_CONFIG_TRANSCODER_THREADS);
-        LOG(WARNING) << "'" << ORTHANC_CONFIG_TRANSCODER_THREADS << "' is set to " << transcoderThreads;
+        LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_TRANSCODER_THREADS << "' is set to " << transcoderThreads;
 
         dicomParserThreads = lock.GetConfiguration().GetUnsignedIntegerParameter(ORTHANC_CONFIG_DICOM_PARSER_SOURCE_THREADS);
-        LOG(WARNING) << "'" << ORTHANC_CONFIG_DICOM_PARSER_SOURCE_THREADS << "' is set to " << dicomParserThreads;
+        LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_DICOM_PARSER_SOURCE_THREADS << "' is set to " << dicomParserThreads;
 
-        GetMemoryConfiguration(storageMemoryCapacityMb, lock, ORTHANC_CONFIG_STORAGE_MEMORY_CAPACITY);
-        GetMemoryConfiguration(dicomParserMemoryCapacityMb, lock, ORTHANC_CONFIG_DICOM_PARSER_MEMORY_CAPACITY);
-        GetMemoryConfiguration(dicomParserCacheSizeMb, lock, ORTHANC_CONFIG_DICOM_PARSER_CACHE_SIZE);
-        GetMemoryConfiguration(transcoderMemoryCapacityMb, lock, ORTHANC_CONFIG_TRANSCODER_MEMORY_CAPACITY);
-        GetMemoryConfiguration(transcoderCacheSizeMb, lock, ORTHANC_CONFIG_TRANSCODER_CACHE_SIZE);
+        GetMemorySizeConfiguration(storageMemoryCapacity, lock, ORTHANC_CONFIG_STORAGE_MEMORY_CAPACITY);
+        GetMemorySizeConfiguration(dicomParserMemoryCapacity, lock, ORTHANC_CONFIG_DICOM_PARSER_MEMORY_CAPACITY);
+        GetMemorySizeConfiguration(dicomParserCacheSize, lock, ORTHANC_CONFIG_DICOM_PARSER_CACHE_SIZE);
+        GetMemorySizeConfiguration(transcoderMemoryCapacity, lock, ORTHANC_CONFIG_TRANSCODER_MEMORY_CAPACITY);
+        GetMemorySizeConfiguration(transcoderCacheSize, lock, ORTHANC_CONFIG_TRANSCODER_CACHE_SIZE);
+        GetMemorySizeConfiguration(sequentialReaderWindowCapacity, lock, ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_CAPACITY);
 
         if (!lock.GetConfiguration().LookupUnsignedIntegerParameter(sequentialReaderThreads, ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_THREADS))
         {
-          LOG(WARNING) << "'" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_THREADS << "' is not defined in your configuration, setting it to the same value as '" << ORTHANC_CONFIG_STORAGE_LOADER_THREADS << "': " << storageLoaderThreads;
+          LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_THREADS
+                       << "' is not defined in your configuration, setting it to the same value as '"
+                       << ORTHANC_CONFIG_STORAGE_LOADER_THREADS << "': " << storageLoaderThreads;
           sequentialReaderThreads = storageLoaderThreads;
         }
         else
         {
-          LOG(WARNING) << "'" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_THREADS << "' is set to " << sequentialReaderThreads;
+          LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_THREADS
+                       << "' is set to " << sequentialReaderThreads;
         }
 
         if (!lock.GetConfiguration().LookupUnsignedIntegerParameter(sequentialReaderWindowSize, ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE))
         {
-          LOG(WARNING) << "'" << 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;
+          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;
         }
         else
         {
-          LOG(WARNING) << "'" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE << "' is set to " << sequentialReaderWindowSize;
+          LOG(WARNING) << "Performance option '" << ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_SIZE
+                       << "' is set to " << sequentialReaderWindowSize;
         }
-
-        GetMemoryConfiguration(sequentialReaderWindowCapacityMb, lock, ORTHANC_CONFIG_SEQUENTIAL_DICOM_READER_WINDOW_CAPACITY);
       }
 
       // For streaming
@@ -784,17 +804,8 @@
                                                  METRICS_STORAGE_AREA_MEMORY_COUNT,
                                                  METRICS_STORAGE_AREA_MEMORY_MAX_USAGE_MB));
 
-        storageAreaReader_->SetCapacity(storageMemoryCapacityMb * MEGABYTE);
-        storageAreaReader_->CreateCache(storageCacheSizeMb * MEGABYTE);
-        
-        if (storageCacheSizeMb == 0)
-        {
-          LOG(WARNING) << "Storage cache is disabled";
-        }
-        else
-        {
-          LOG(WARNING) << "Storage cache size is " << storageCacheSizeMb << " MB";
-        }
+        storageAreaReader_->SetCapacity(storageMemoryCapacity);
+        storageAreaReader_->CreateCache(storageCacheSize);
       }
 
       {
@@ -817,8 +828,8 @@
                                                  METRICS_DICOM_PARSER_MEMORY_COUNT,
                                                  METRICS_DICOM_PARSER_MEMORY_MAX_USAGE_MB));
 
-        dicomReader_->SetCapacity(dicomParserMemoryCapacityMb * MEGABYTE);
-        dicomReader_->CreateCache(dicomParserCacheSizeMb * MEGABYTE);
+        dicomReader_->SetCapacity(dicomParserMemoryCapacity);
+        dicomReader_->CreateCache(dicomParserCacheSize);
       }
 
       if (transcoder_.get() != NULL)
@@ -842,8 +853,8 @@
                                                  METRICS_TRANSCODER_MEMORY_COUNT,
                                                  METRICS_TRANSCODER_MEMORY_MAX_USAGE_MB));
 
-        transcoderReader_->SetCapacity(transcoderMemoryCapacityMb * MEGABYTE);
-        transcoderReader_->CreateCache(transcoderCacheSizeMb * MEGABYTE);
+        transcoderReader_->SetCapacity(transcoderMemoryCapacity);
+        transcoderReader_->CreateCache(transcoderCacheSize);
       }
 
       {
@@ -858,7 +869,7 @@
         dicomSequentialReaderFactory_.reset(new DicomSequentialReader::Factory(
                                               executor, storageAreaReader_, dicomReader_, transcoderReader_,
                                               sequentialReaderWindowSize,
-                                              sequentialReaderWindowCapacityMb * MEGABYTE));
+                                              sequentialReaderWindowCapacity));
       }
     }
     catch (OrthancException&)