Yazılım geliştirme: C++17’de ayırıcılarla optimizasyon

Adanali

Active member
Yazılım geliştirme: C++17’de ayırıcılarla optimizasyon


  1. Yazılım geliştirme: C++17’de ayırıcılarla optimizasyon

C++17’deki polimorfik ayırıcılar, hem performans hem de belleğin yeniden kullanımı için bellek tahsisinin optimize edilmesine yardımcı olur.

Duyuru








Rainer Grimm uzun yıllardır yazılım mimarı, ekip ve eğitim yöneticisi olarak çalışmaktadır. C++, Python ve Haskell programlama dilleri üzerine makaleler yazmaktan hoşlanıyor, aynı zamanda özel konferanslarda sık sık konuşmaktan da hoşlanıyor. Modern C++ adlı blogunda C++ tutkusunu yoğun bir şekilde ele alıyor.







verim


Aşağıdaki program cppreference.com/monotonic_buffer_resource adresinden gelmektedir. Clang ve MSVC derleyicisi için performans testini genişletip açıklayacağım.


// pmrPerformance.cpp
// https://en.cppreference.com/w/cpp/memory/monotonic_buffer_resource

#include <array>
#include <chrono>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <list>
#include <memory_resource>

template<typename Func>
auto benchmark(Func test_func, int iterations) // (1)
{
const auto start = std::chrono::system_clock::now();
while (iterations-- > 0)
test_func();
const auto stop = std::chrono::system_clock::now();
const auto secs = std::chrono::duration<double>(stop - start);
return secs.count();
}

int main()
{
constexpr int iterations{100};
constexpr int total_nodes{2'00'000};

auto default_std_alloc = [total_nodes] // (2)
{
std::list<int> list;
for (int i{}; i != total_nodes; ++i)
list.push_back(i);
};

auto default_pmr_alloc = [total_nodes] // (3)
{
std::pmr::list<int> list;
for (int i{}; i != total_nodes; ++i)
list.push_back(i);
};

auto pmr_alloc_no_buf = [total_nodes] // (4)
{
std::pmr::monotonic_buffer_resource mbr;
std::pmr::polymorphic_allocator<int> pa{&mbr};
std::pmr::list<int> list{pa};
for (int i{}; i != total_nodes; ++i)
list.push_back(i);
};

auto pmr_alloc_and_buf = [total_nodes] // (5)
{
// enough to fit in all nodes:
std::array<std::byte, total_nodes * 32> buffer;
std::pmr::monotonic_buffer_resource mbr{buffer.data(),
buffer.size()};
std::pmr::polymorphic_allocator<int> pa{&mbr};
std::pmr::list<int> list{pa};
for (int i{}; i != total_nodes; ++i)
list.push_back(i);
};

const double t1 = benchmark(default_std_alloc, iterations);
const double t2 = benchmark(default_pmr_alloc, iterations);
const double t3 = benchmark(pmr_alloc_no_buf , iterations);
const double t4 = benchmark(pmr_alloc_and_buf, iterations);

std::cout << std::fixed << std::setprecision(3)
<< "t1 (default std alloc): " << t1
<< " sec; t1/t1: " << t1/t1 << 'n'
<< "t2 (default pmr alloc): " << t2
<< " sec; t1/t2: " << t1/t2 << 'n'
<< "t3 (pmr alloc no buf): " << t3
<< " sec; t1/t3: " << t1/t3 << 'n'
<< "t4 (pmr alloc and buf): " << t4
<< " sec; t1/t4: " << t1/t4 << 'n';
}


(1)’deki bu performans testi, (2) – (5)’teki fonksiyonları yüz kez (constexpr int iterations{100}). Her işlev çağrısı bir tane oluşturur std::pmr::list<int> iki yüz bin knot ile (constexpr int total_nodes{2'00'000}). Bireysel listelerin düğümleri farklı şekillerde tahsis edilir:

  • (2): std::list<int> genel kullan operator new
  • (3): std::pmr::list<int> özel hafıza kaynağını kullanır std::pmr::new_delete_resource
  • (4): std::pmr::list<int> kullanılmış std::pmr::monotonic_buffer yığında önceden tahsis edilmiş bir arabellek olmadan
  • (5): std::pmr::list kullanılmış std::pmr::monotonic_buffer yığında önceden tahsis edilmiş bir arabellek ile
Son fonksiyona (5) ilişkin yorum, yığında tüm düğümleri barındıracak yeterli alanın bulunduğunu belirtir: “tüm düğümlere uyacak kadar“. Bu, Linux bilgisayarımda doğruydu, ancak Windows bilgisayarımda değildi. Linux altında yığının standart boyutu 8 MB’tır, ancak Windows altında yalnızca 1 MB’dir. Bu, programımı Windows altında çalıştırmanın MSVC derleyicisini kullandığı anlamına geliyordu. ve clang derleyici sessizce başarısız oldu. Sorunu şunu kullanarak çözdüm: editbin.exe MSVC ve Clang yürütülebilir dosyalarımın yığın boyutunu değiştirdim:








İşte nihayet rakamlar. Referans değeri şu atamadır: std::list<int> (satır 2). Mutlak sayıları değil göreceli sayıları karşılaştırın çünkü sanallaştırılmış bir Linux bilgisayar ve sanal olmayan bir Windows bilgisayar kullandım. Açıkçası maksimum optimizasyonu etkinleştirdim. Bu şu anlama gelir (/Ox) MSVC derleyicisi için ve (-Ox) GCC ve Clang derleyicileri için.




















İlginç bir şekilde, hafıza tahsisi hafıza kaynağıyla ilgiliydi std::pmr::new_delete_resource her zaman en yavaş olanıdır. Tam tersine ortaya çıkıyor std::pmr::monotonic_buffer özellikle yığında önceden tahsis edilmiş bir arabellek kullanıldığında en hızlı bellek tahsisini temsil eder. Windows’ta bu, bellek tahsisinin yaklaşık on kat daha hızlı olmasını sağlar.






C++20 ile tanıtılan kavramlar, Ranges kitaplığı, modüller ve eşyordamlar ile birlikte modern C++ uygulamaları oluşturmanın yolunu yeniden tanımladı. 7 – 9 Kasım arası 2023’te Rainer Grimm, yoğun C++20 atölyesinde sizi bilgilendirecek: yeni kavramlar kapsamlı bir şekilde açıklanacak ve C++20’nin sunduğu birçok yararlı işlevi derinlemesine inceleyecek.







Depolama kaynağı std::pmr::new_delete_resource daha da fazla optimizasyon sunar.

Belleğin yeniden kullanımı


std::pmr::monotonic_buffer belleği yeniden kullanmanıza olanak tanır, böylece bellekte yer açmanıza gerek kalmaz.


// reuseMemory.cpp

#include <array>
#include <cstddef>
#include <iostream>
#include <memory_resource>
#include <string>
#include <vector>

int main() {

std::array<std::byte, 2000> buf;

for (int i = 0; i < 100; ++i) { // (1)
std::pmr::monotonic_buffer_resource pool{buf.data(),
buf.size(), // (2)
std::pmr::null_memory_resource()};
std::pmr::vector<std::pmr::string> myVec{&pool};
for (int j = 0; j < 16; ++j) { // (3)
myVec.emplace_back("A short string");
}
}
}


Bu program şunları atar: std::array 2000 bayt ile: std::array<std::byte, 2000>. Bu yığına ayrılmış bellek yüzlerce kez yeniden kullanılır (1). THE std::pmr::vector<std::prm::string> kullan std::pmr::monotonic_buffer_resource yukarı akış depolama kaynağıyla std::pmr::null_memory_resource (2). Son olarak vektöre 16 dize eklenir.

Sıradaki ne?


Bu makale, C++17’deki polimorfik bellek kaynakları hakkındaki mini dizimi sonlandırıyor. Bir sonraki yazımda üç yıl ileriye atlayıp C++20 yolculuğuma devam edeceğim.


(kendim)



Haberin Sonu
 
Üst