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:
mr::list<int> list;
for (int i{}; i != total_nodes; ++i)
list.push_back(i);
};
auto pmr_alloc_no_buf = [total_nodes] // (4)
{
std:
mr::monotonic_buffer_resource mbr;
std:
mr:
olymorphic_allocator<int> pa{&mbr};
std:
mr::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:
mr::monotonic_buffer_resource mbr{buffer.data(),
buffer.size()};
std:
mr:
olymorphic_allocator<int> pa{&mbr};
std:
mr::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:
mr::list<int> iki yüz bin knot ile (constexpr int total_nodes{2'00'000}). Bireysel listelerin düğümleri farklı şekillerde tahsis edilir:
İş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:
mr::new_delete_resource her zaman en yavaş olanıdır. Tam tersine ortaya çıkıyor std:
mr::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:
mr::new_delete_resource daha da fazla optimizasyon sunar.
Belleğin yeniden kullanımı
std:
mr::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:
mr::monotonic_buffer_resource pool{buf.data(),
buf.size(), // (2)
std:
mr::null_memory_resource()};
std:
mr::vector<std:
mr::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:
mr::vector<std:
rm::string> kullan std:
mr::monotonic_buffer_resource yukarı akış depolama kaynağıyla std:
mr::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
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:
for (int i{}; i != total_nodes; ++i)
list.push_back(i);
};
auto pmr_alloc_no_buf = [total_nodes] // (4)
{
std:
std:
std:
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:
buffer.size()};
std:
std:
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:
- (2): std::list<int> genel kullan operator new
- (3): std:
mr::list<int> özel hafıza kaynağını kullanır std:
mr::new_delete_resource
- (4): std:
mr::list<int> kullanılmış std:
mr::monotonic_buffer yığında önceden tahsis edilmiş bir arabellek olmadan
- (5): std:
mr::list kullanılmış std:
mr::monotonic_buffer yığında önceden tahsis edilmiş bir arabellek ile

İş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:

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:
Belleğin yeniden kullanımı
std:
// 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:
buf.size(), // (2)
std:
std:
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:
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