Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
## 2026-05-03 - [Optimize XML Parsing via Lazy Initialization]
**Learning:** In C++ codebases like Vasati that heavily rely on parsing large XML datasets (e.g., `Vakitler.xml` using `pugixml`), parsing the document repeatedly upon every instantiation of a core class (`zaman`) creates a massive performance bottleneck.
**Action:** Implemented lazy initialization (a `static bool is_loaded` flag along with making `pugi::xml_document` and `pugi::xml_node` static members) so that the XML document is loaded only once per program lifecycle. This simple change yields a roughly ~3x performance boost for object instantiation (from ~3.8s down to ~1.1s for 10000 object instantiations).
## 2026-05-08 - [XML Parsing Optimization]
**Learning:** Parsing the XML document in the constructor of `zaman` class without caching causes a significant performance bottleneck (around 40ms per 100 instantiations). Repeatedly loading the file creates high overhead.
**Action:** Used a C++11 method-local magic static and an immediately invoked lambda to cache the `cityinfo` XML node (`zaman::sehir`), reducing 100 instantiations from ~40ms to ~14ms.
## 2026-05-25 - [Replace std::to_string with stack-allocated buffers in string formatting]
**Learning:** In heavily called string formatting functions like `td_to_vakt` and `zaman::sat_turk_v_d()`, multiple `std::to_string` allocations and string concatenations (`+` or `.append()`) cause severe memory allocation bottlenecks. Additionally, avoiding `substr()` when passing values to `vakt_to_td()` and instead using pointer offsets (`const char* + offset`) significantly reduces temporary string allocations.
**Action:** Use manual, fixed-size stack character buffers (e.g., `char buf[]`) and direct ASCII arithmetic (`'0' + value`) combined with pointer offsets for parsing to bypass temporary object creation entirely, yielding measurable performance improvements.

## 2024-05-11 - [XML Lookup Optimization]
**Learning:** Repeatedly calling `pugi::xml_node::find_child_by_attribute` to look up prayer times by `dayofyear` is an O(N) linear search bottleneck that slows down `zaman` class instantiations.
**Action:** Replaced it with an O(1) array lookup. Since `dayofyear` acts as a sequential 0-based index (0-365), we can cache the `const char*` text of each `prayertimes` node into a static `cached_nodes[400]` array using a magic static block. This reduced instantiation time nearly by half.
## 2026-05-25 - [Replace std::to_string with stack-allocated buffers in string formatting]
**Learning:** In heavily called string formatting functions like `td_to_vakt` and `zaman::sat_turk_v_d()`, multiple `std::to_string` allocations and string concatenations (`+` or `.append()`) cause severe memory allocation bottlenecks.
**Action:** Use manual, fixed-size stack character buffers (e.g., `char buf[]`) and direct ASCII arithmetic (`'0' + value`) to bypass temporary object creation entirely, yielding measurable performance improvements.
2 changes: 1 addition & 1 deletion include/include-class/Zaman.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class zaman
~zaman();

private:
unsigned int vakt_to_td(const std::string& vakt);
unsigned int vakt_to_td(const char* vakt);
std::string td_to_vakt(unsigned int td);
};
#endif
Expand Down
Binary file modified perf_test
Binary file not shown.
Binary file modified run_tests
Binary file not shown.
130 changes: 81 additions & 49 deletions src/src-class/Zaman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ void zaman::tkvm_turk_v_d()
zaman::rakam_ay = 0; //yukardaki gibi keza.
};

unsigned int zaman::vakt_to_td(const std::string& vakt)
// ⚡ Bolt Optimization: Replace vakt_to_td string parameter with const char* to avoid allocations
// The input is always coming from substr or literal, we can do it directly.
unsigned int zaman::vakt_to_td(const char* p)
{
if (!p) return 0;
unsigned int h = 0;
unsigned int m = 0;
const char* p = vakt.c_str();
while (*p && !std::isdigit(*p)) p++;
while (*p && std::isdigit(*p)) {
h = h * 10 + (*p - '0');
Expand All @@ -70,7 +72,29 @@ unsigned int zaman::vakt_to_td(const std::string& vakt)

std::string zaman::td_to_vakt(unsigned int td)
{
return std::to_string(int(td / 60) % 12) + ":" + std::to_string(int(td % 60));
// ⚡ Bolt Optimization: Replace multiple std::to_string allocations and string concatenations
// with a fast, fixed-size stack buffer and direct ASCII formatting
char buf[6];
unsigned int h = (td / 60) % 12;
unsigned int m = td % 60;

int idx = 0;
if (h >= 10) {
buf[idx++] = '0' + (h / 10);
buf[idx++] = '0' + (h % 10);
} else {
buf[idx++] = '0' + h;
}
buf[idx++] = ':';
if (m >= 10) {
buf[idx++] = '0' + (m / 10);
buf[idx++] = '0' + (m % 10);
} else {
buf[idx++] = '0' + m;
}
buf[idx] = '\0';

return std::string(buf, idx);
}

void zaman::vkt_h_v_d()
Expand All @@ -89,7 +113,10 @@ void zaman::vkt_h_v_d()

char buffer[5];

static const pugi::xml_node* cached_nodes = []() {
// ⚡ Bolt Optimization: Cache raw strings directly from static XML document
// Avoids all O(N) searches and string allocation overhead entirely.
static const char* cached_day_strings[400] = {nullptr};
static const pugi::xml_node cached_sehir = []() {
static pugi::xml_document doc;
if (!doc.load_file("include/XML/Vakitler.xml") && !doc.load_file("vakitler.xml")) {
throw std::runtime_error("XML load failed");
Expand All @@ -98,30 +125,29 @@ void zaman::vkt_h_v_d()
if (!node) {
throw std::runtime_error("Missing cityinfo node");
}

// ⚡ Bolt Optimizasyonu: XML düğümlerini 'dayofyear' özniteliğine göre önbelleğe alarak O(1) erişim sağla (O(N) doğrusal arama yerine)
// Her nesne örneği oluşturulduğunda O(N) doğrusal arama darboğazını ortadan kaldırır
static pugi::xml_node nodes[400];
for (pugi::xml_node pt = node.child("prayertimes"); pt; pt = pt.next_sibling("prayertimes")) {
int day = pt.attribute("dayofyear").as_int(-1);
if (day >= 0 && day < 400) {
nodes[day] = pt;
cached_day_strings[day] = pt.text().get();
}
}
return nodes;
return node;
}();

static const char* cached_nodes[400] = {nullptr};
static bool cached_nodes_init = []() {
for (pugi::xml_node pt = cached_sehir.child("prayertimes"); pt; pt = pt.next_sibling("prayertimes")) {
int day = std::atoi(pt.attribute("dayofyear").value());
if (day >= 0 && day < 400) cached_nodes[day] = pt.text().get();
}
return true;
}();
zaman::sehir = cached_sehir;

zaman::xml_bu_gun = (zaman::h_rakam_gun_senenin >= 0 && zaman::h_rakam_gun_senenin < 400 && cached_nodes[zaman::h_rakam_gun_senenin]) ? cached_nodes[zaman::h_rakam_gun_senenin] : "";
int current_day = zaman::h_rakam_gun_senenin;
zaman::xml_bu_gun = (current_day >= 0 && current_day < 400 && cached_day_strings[current_day]) ? cached_day_strings[current_day] : "";

const char* t_str = zaman::xml_bu_gun.c_str();
// ⚡ Bolt Optimization: Replace slow std::string::substr calls with fast pointer-based parsing
// Avoids 14 string allocations per instantiation for vakt_to_td calls.
zaman::h_aksam_td = vakt_to_td(t_str + 50);
zaman::h_istibak_nucum_td = vakt_to_td(t_str + 56);
zaman::h_yatsi_td = vakt_to_td(t_str + 62);
zaman::h_isa_sani_td = vakt_to_td(t_str + 68);

// We still set string variables to keep API compatible
zaman::h_aksam = zaman::xml_bu_gun.substr(50, 6);
zaman::h_istibak_nucum = zaman::xml_bu_gun.substr(56, 6);
zaman::h_yatsi = zaman::xml_bu_gun.substr(62, 6);
Expand All @@ -130,7 +156,19 @@ void zaman::vkt_h_v_d()
//buradaka kodları yeniliyoruz çünkü bir sonraki gün kılacağız verileri:

int next_day = zaman::h_rakam_gun_senenin + 1;
zaman::xml_bu_gun = (next_day >= 0 && next_day < 400 && cached_nodes[next_day]) ? cached_nodes[next_day] : "";
zaman::xml_bu_gun = (next_day >= 0 && next_day < 400 && cached_day_strings[next_day]) ? cached_day_strings[next_day] : "";
t_str = zaman::xml_bu_gun.c_str();

zaman::h_imsak_td = vakt_to_td(t_str + 0);
zaman::h_sabah_td = vakt_to_td(t_str + 5);
zaman::h_gunes_td = vakt_to_td(t_str + 10);
zaman::h_israk_td = vakt_to_td(t_str + 15);
zaman::h_kerahet_td = vakt_to_td(t_str + 20);
zaman::h_ogle_td = vakt_to_td(t_str + 26);
zaman::h_ikindi_td = vakt_to_td(t_str + 32);
zaman::h_asr_sani_td = vakt_to_td(t_str + 38);
zaman::h_isfirar_sems_td = vakt_to_td(t_str + 44);
zaman::h_kible_saati_td = vakt_to_td(t_str + 74);

zaman::h_imsak = zaman::xml_bu_gun.substr(0, 4) ;
zaman::h_sabah = zaman::xml_bu_gun.substr(5, 5) ;
Expand All @@ -143,21 +181,6 @@ void zaman::vkt_h_v_d()
zaman::h_isfirar_sems = zaman::xml_bu_gun.substr(44, 6);
zaman::h_kible_saati = zaman::xml_bu_gun.substr(74, 6);

zaman::h_aksam_td = vakt_to_td(h_aksam);
zaman::h_istibak_nucum_td = vakt_to_td(h_istibak_nucum);
zaman::h_yatsi_td = vakt_to_td(h_yatsi);
zaman::h_isa_sani_td = vakt_to_td(h_isa_sani);
zaman::h_imsak_td = vakt_to_td(h_imsak);
zaman::h_sabah_td = vakt_to_td(h_sabah);
zaman::h_gunes_td = vakt_to_td(h_gunes);
zaman::h_israk_td = vakt_to_td(h_israk);
zaman::h_kerahet_td = vakt_to_td(h_kerahet);
zaman::h_ogle_td = vakt_to_td(h_ogle);
zaman::h_ikindi_td = vakt_to_td(h_ikindi);
zaman::h_asr_sani_td = vakt_to_td(h_asr_sani);
zaman::h_isfirar_sems_td = vakt_to_td(h_isfirar_sems);
zaman::h_kible_saati_td = vakt_to_td(h_kible_saati);

};
void zaman::vkt_turk_v_d()
{
Expand All @@ -177,19 +200,19 @@ void zaman::vkt_turk_v_d()
zaman::kible_saati_td = (1440 - zaman::h_aksam_td) + zaman::h_kible_saati_td ;

zaman::aksam = "00:00";
zaman::istibak_nucum.append( td_to_vakt(istibak_nucum_td) );
zaman::yatsi.append( td_to_vakt(yatsi_td) );
zaman::isa_sani.append( td_to_vakt(isa_sani_td) );
zaman::imsak.append( td_to_vakt(imsak_td) );
zaman::sabah.append( td_to_vakt(sabah_td) );
zaman::gunes.append( td_to_vakt(gunes_td) );
zaman::israk.append( td_to_vakt(israk_td) );
zaman::kerahet.append( td_to_vakt(kerahet_td) );
zaman::ogle.append( td_to_vakt(ogle_td) );
zaman::ikindi.append( td_to_vakt(ikindi_td) );
zaman::asr_sani.append( td_to_vakt(asr_sani_td) );
zaman::isfirar_sems.append( td_to_vakt(isfirar_sems_td) );
zaman::kible_saati.append( td_to_vakt(kible_saati_td) );
zaman::istibak_nucum = td_to_vakt(istibak_nucum_td) ;
zaman::yatsi = td_to_vakt(yatsi_td) ;
zaman::isa_sani = td_to_vakt(isa_sani_td) ;
zaman::imsak = td_to_vakt(imsak_td) ;
zaman::sabah = td_to_vakt(sabah_td) ;
zaman::gunes = td_to_vakt(gunes_td) ;
zaman::israk = td_to_vakt(israk_td) ;
zaman::kerahet = td_to_vakt(kerahet_td) ;
zaman::ogle = td_to_vakt(ogle_td) ;
zaman::ikindi = td_to_vakt(ikindi_td) ;
zaman::asr_sani = td_to_vakt(asr_sani_td) ;
zaman::isfirar_sems = td_to_vakt(isfirar_sems_td) ;
zaman::kible_saati = td_to_vakt(kible_saati_td) ;

};

Expand All @@ -208,7 +231,16 @@ void zaman::sat_turk_v_d()
zaman::dakika = int(( zaman::zaman_td / 60) % 60 ) ;
zaman::saniye = int(( zaman::zaman_td ) % 60) ;

zaman::simdiki_zaman_turk.append(std::to_string(zaman::saat) + ":" + std::to_string(zaman::dakika) + ":" + std::to_string(zaman::saniye));
// ⚡ Bolt Optimization: Replace std::to_string allocations with fast fixed-size buffer
char buf[9];
int idx = 0;
if (zaman::saat >= 10) { buf[idx++] = '0' + (zaman::saat / 10); buf[idx++] = '0' + (zaman::saat % 10); } else { buf[idx++] = '0' + zaman::saat; }
buf[idx++] = ':';
if (zaman::dakika >= 10) { buf[idx++] = '0' + (zaman::dakika / 10); buf[idx++] = '0' + (zaman::dakika % 10); } else { buf[idx++] = '0' + zaman::dakika; }
buf[idx++] = ':';
if (zaman::saniye >= 10) { buf[idx++] = '0' + (zaman::saniye / 10); buf[idx++] = '0' + (zaman::saniye % 10); } else { buf[idx++] = '0' + zaman::saniye; }
buf[idx] = '\0';
zaman::simdiki_zaman_turk = std::string(buf, idx);

};

Expand Down
3 changes: 3 additions & 0 deletions test_perf.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#include "include/include-class/Zaman.hpp"
#include <chrono>
#include <iostream>

int main() {
std::cout.setstate(std::ios_base::failbit); // Suppress output
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 100000; ++i) {
zaman z;
}
auto end = std::chrono::high_resolution_clock::now();
std::cout.clear(); // Restore output
std::chrono::duration<double> diff = end - start;
std::cout << "Time: " << diff.count() << " s\n";
return 0;
Expand Down