Информация об изменениях

Сообщение Re[8]: [performance] ладно, заинтриговал от 05.07.2022 7:26

Изменено 05.07.2022 7:28 rg45

Re[8]: [performance] ладно, заинтриговал
Здравствуйте, Videoman, Вы писали:

V>Опциональна, но во всех современных С++ библиотеках она используется.

V>P.S. Относительно вопроса скорости примера: похоже, что С# 6.0 компилятор тупо сгенерировал более быстрый код, чем MSVC. Либо действительно числа не помещаются в буфера SSO и ты, по сути, меряешь скорости разных структур в памяти.

В текушем коде примера случайные числа генерируются в диапазоне [0 .. std::numeric_limits<int32_t>::max()]. То есть, максимальная длина строки не должна превышайть 9-ти символов и SSO должна быть эффективна, если только она используется. Я, не мудрствуя лукаво, добавил функцию, которая выводит в конце минимальный и максимальный интервалы между буферами соседних элементов входной последовательности. И вот, выходит, что не используется SSO:

Hash = 41910796
Processing time: 0.83061 sec
[Distribution in memory]: Min Interval: -3218203360, Max Interval: 1680855496


  Полный текст примера
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <random>

using Int = int32_t;

std::vector<std::wstring> MakeIntSequence(size_t size)
{
   std::random_device rd;  //Will be used to obtain a seed for the random number engine
   std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
   std::uniform_int_distribution<> distrib(0, std::numeric_limits<int32_t>::max());
   std::vector<std::wstring> v;
   v.reserve(size);
   for (size_t i = 0; i < size; ++i)
   {
      v.push_back(std::to_wstring(distrib(gen)));
   }
   return v;
}

Int ParseInt(const std::wstring& wstr)
{
   Int res{};
   for (auto&& d : wstr)
   {
      if ('0' <= d && d <= '9')
      {
         res = res * 10 + d - '0';
      }
      else
      {
         throw std::out_of_range("'" + std::to_string(d) + "': Symbol is out of range");
      }
   }
   return res;
}

void testDistributionInMemory(const std::vector<std::wstring>& v)
{
   if (v.size() >= 2)
   {
      std::intptr_t minInterval = std::numeric_limits<std::intptr_t>::max();
      std::intptr_t maxInterval{};
      const wchar_t* prev = v[0].data();

      for (size_t i = 1; i < v.size(); ++i)
      {
         const wchar_t* next = v[i].data();
         std::intptr_t nextInterval = next - prev;
         if (minInterval > nextInterval)
         {
            minInterval = nextInterval;
         }
         if (maxInterval < nextInterval)
         {
            maxInterval = nextInterval;
         }
         prev = next;
      }
      std::cout << "[Distribution in memory]: Min Interval: " << minInterval << ", Max Interval: " << maxInterval << std::endl;
   }
}

int main()
try
{
   namespace tm = std::chrono;

   const auto vals = MakeIntSequence(0x4000000);

   const auto t0 = tm::steady_clock::now();

   Int hash{};
   for (const auto& val : vals)
   {
      hash ^= ParseInt(val);
   }
   const tm::duration<double> dt = tm::steady_clock::now() - t0;

   std::cout << "Hash = " << std::hex << hash << std::dec << std::endl;
   std::cout << "Processing time: " << dt.count() << " sec" << std::endl;
   testDistributionInMemory(vals);
}
catch (const std::exception& ex)
{
   std::cerr << "[Unhandled Exception]: " << ex.what() << std::endl;
}
Re[8]: [performance] ладно, заинтриговал
Здравствуйте, Videoman, Вы писали:

V>Опциональна, но во всех современных С++ библиотеках она используется.

V>P.S. Относительно вопроса скорости примера: похоже, что С# 6.0 компилятор тупо сгенерировал более быстрый код, чем MSVC. Либо действительно числа не помещаются в буфера SSO и ты, по сути, меряешь скорости разных структур в памяти.

В текушем коде примера случайные числа генерируются в диапазоне [0 .. std::numeric_limits<int32_t>::max()]. То есть, максимальная длина строки не должна превышайть 9-ти символов и SSO должна быть эффективна, если только она используется. Я, не мудрствуя лукаво, добавил функцию, которая выводит в конце минимальный и максимальный интервалы между буферами соседних элементов входной последовательности. И вот, выходит, что не используется SSO, строки здорово разбросаны по памяти:

Hash = 41910796
Processing time: 0.83061 sec
[Distribution in memory]: Min Interval: -3218203360, Max Interval: 1680855496


  Полный текст примера
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <random>

using Int = int32_t;

std::vector<std::wstring> MakeIntSequence(size_t size)
{
   std::random_device rd;  //Will be used to obtain a seed for the random number engine
   std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
   std::uniform_int_distribution<> distrib(0, std::numeric_limits<int32_t>::max());
   std::vector<std::wstring> v;
   v.reserve(size);
   for (size_t i = 0; i < size; ++i)
   {
      v.push_back(std::to_wstring(distrib(gen)));
   }
   return v;
}

Int ParseInt(const std::wstring& wstr)
{
   Int res{};
   for (auto&& d : wstr)
   {
      if ('0' <= d && d <= '9')
      {
         res = res * 10 + d - '0';
      }
      else
      {
         throw std::out_of_range("'" + std::to_string(d) + "': Symbol is out of range");
      }
   }
   return res;
}

void testDistributionInMemory(const std::vector<std::wstring>& v)
{
   if (v.size() >= 2)
   {
      std::intptr_t minInterval = std::numeric_limits<std::intptr_t>::max();
      std::intptr_t maxInterval{};
      const wchar_t* prev = v[0].data();

      for (size_t i = 1; i < v.size(); ++i)
      {
         const wchar_t* next = v[i].data();
         std::intptr_t nextInterval = next - prev;
         if (minInterval > nextInterval)
         {
            minInterval = nextInterval;
         }
         if (maxInterval < nextInterval)
         {
            maxInterval = nextInterval;
         }
         prev = next;
      }
      std::cout << "[Distribution in memory]: Min Interval: " << minInterval << ", Max Interval: " << maxInterval << std::endl;
   }
}

int main()
try
{
   namespace tm = std::chrono;

   const auto vals = MakeIntSequence(0x4000000);

   const auto t0 = tm::steady_clock::now();

   Int hash{};
   for (const auto& val : vals)
   {
      hash ^= ParseInt(val);
   }
   const tm::duration<double> dt = tm::steady_clock::now() - t0;

   std::cout << "Hash = " << std::hex << hash << std::dec << std::endl;
   std::cout << "Processing time: " << dt.count() << " sec" << std::endl;
   testDistributionInMemory(vals);
}
catch (const std::exception& ex)
{
   std::cerr << "[Unhandled Exception]: " << ex.what() << std::endl;
}