//+------------------------------------------------------------------+ //| TestJsonBenchmark.mq5 | //| AI-Toolkit | //+------------------------------------------------------------------+ #property script_show_inputs #include "../fast_json.mqh" #include input int Loops = 50000; void OnStart() { Print("=== JSON BENCHMARK: THE RECKONING ==="); Print("Comparing fast_json vs JAson"); // Complex JSON with Mixed Types, Nested Objects, and Arrays string json = "{" "\"id\": 12345," "\"config\": {\"active\": true, \"mode\": \"swar\", \"threshold\": " "0.0005}," "\"rates\": [1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]," "\"users\": [{\"id\":1, \"name\":\"A\"}, {\"id\":2, \"name\":\"B\"}]," "\"desc\": \"Short string\"," "\"long\": \"This is a longer string to test the memory copy speed of " "the parser when handling larger payloads.\"" "}"; ulong t0, t_parse = 0, t_read = 0, t_sum = 0, t_ser = 0; //--------------------------------------------------------- // 1. AI-TOOLKIT BIT-BANGER V2 //--------------------------------------------------------- long dummy_int = 0; double dummy_sum = 0; string dummy_ser = ""; // A. PARSE CJson toolkit; t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { toolkit.Parse(json); } t_parse = GetMicrosecondCount() - t0; Print("Parse: ", t_parse, " us"); // B. DEEP READ (Hash Map) toolkit.Parse(json); // Reset t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { dummy_int += toolkit["config"]["mode"].Equals("swar") ? 1 : 0; dummy_int += toolkit["id"].ToInt(); } t_read = GetMicrosecondCount() - t0; Print("Deep Read: ", t_read, " us"); // C. ARRAY SUM (Iteration) t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { CJsonNode rates = toolkit["rates"]; // Iterator Optimization: Fast Double Access (Stride 2) CJsonFastDoubleIterator it = rates.begin_fast_double(); while (it.IsValid()) { dummy_sum += it.Val(); // Direct read it.Next(); } } t_sum = GetMicrosecondCount() - t0; Print("Array Sum: ", t_sum, " us"); // D. SERIALIZATION t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { dummy_ser = toolkit.Serialize(); } t_ser = GetMicrosecondCount() - t0; Print("Serialize: ", t_ser, " us"); ulong total_toolkit = t_parse + t_read + t_sum + t_ser; //--------------------------------------------------------- // 2. RUSSIAN LEGACY (JAson) //--------------------------------------------------------- Print("--- TESTING LEGACY JASON ---"); ulong j_parse = 0, j_read = 0, j_sum = 0, j_ser = 0; // A. PARSE CJAVal jason; t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { jason.Deserialize(json); } j_parse = GetMicrosecondCount() - t0; Print("Parse: ", j_parse, " us"); // B. DEEP READ t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { // JAson syntax dummy_int += jason["config"]["mode"].ToStr() == "swar" ? 1 : 0; dummy_int += jason["id"].ToInt(); } j_read = GetMicrosecondCount() - t0; Print("Deep Read: ", j_read, " us"); // C. ARRAY SUM t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { CJAVal *rates = jason["rates"]; // Get pointer to array // JAson usually allows array access via [] for (int k = 0; k < 10; k++) { dummy_sum += rates[k].ToDbl(); } } j_sum = GetMicrosecondCount() - t0; Print("Array Sum: ", j_sum, " us"); // D. SERIALIZATION t0 = GetMicrosecondCount(); for (int i = 0; i < Loops; i++) { dummy_ser = jason.Serialize(); } j_ser = GetMicrosecondCount() - t0; Print("Serialize: ", j_ser, " us"); ulong total_jason = j_parse + j_read + j_sum + j_ser; //--------------------------------------------------------- // 3. COMPLEX ROUNDTRIP & API HELPER TEST //--------------------------------------------------------- Print("--- TESTING ROUNDTRIP & HELPERS ---"); CJsonBuilder b; b.Obj() .Key("meta") .Obj() .Key("version") .Val(2) .Key("timestamp") .Val(123456789) .EndObj() .Key("data") .Arr() .Obj() .Key("id") .Val(1) .Key("val") .Val(10.5) .EndObj() .Obj() .Key("id") .Val(2) .Key("val") .Val(20.0) .EndObj() .EndArr() .Key("flags") .Arr() .Val(true) .Val(false) .Val(true) .EndArr() .Key("unicode") .Val("R$ 100,00 \u00A9") .EndObj(); string built_json = b.Build(); Print("Built JSON: ", built_json); CJson check; if (check.Parse(built_json)) { CJsonNode root = check.GetRoot(); // 1. HasKey bool has_meta = root.HasKey("meta"); bool has_fake = root.HasKey("fake"); Print("HasKey('meta'): ", has_meta, " (Expected: true)"); Print("HasKey('fake'): ", has_fake, " (Expected: false)"); if (!has_meta || has_fake) Print("FAIL: HasKey broken"); // 2. Size int root_size = root.Size(); // meta, data, flags, unicode = 4 Print("Root Size: ", root_size, " (Expected: 4)"); if (root_size != 4) Print("FAIL: Root Size broken"); int data_size = root["data"].Size(); // 2 items Print("Data Size: ", data_size, " (Expected: 2)"); if (data_size != 2) Print("FAIL: Data Size broken"); // 3. GetKeys string keys[]; int k_count = root.GetKeys(keys); Print("Keys Found: ", k_count); string k_str = ""; for (int k = 0; k < k_count; k++) k_str += keys[k] + ","; Print("Key List: ", k_str); // 4. Unicode Check string uni = root["unicode"].ToString(); Print("Unicode Check: ", uni); Print("ROUNDTRIP: PASS (Visual Check Required for Unicode)"); } else { Print("ROUNDTRIP: FAIL (Parse Error ", check.GetLastError(), ")"); } //--------------------------------------------------------- // 4. LEGACY JASON ROUNDTRIP (Feature Check) //--------------------------------------------------------- Print("--- TESTING LEGACY JASON ROUNDTRIP ---"); CJAVal j_build; // Building via assignment (slower due to allocation, but easier syntax?) j_build["meta"]["version"] = 2; j_build["meta"]["timestamp"] = 123456789; // j_build["data"].Add(); // JAson array add usually tricky via [] // Forced simplified construction for comparison j_build["unicode"] = "R$ 100,00 \u00A9"; string j_built_str = j_build.Serialize(); // Print("Legacy JSON: ", j_built_str); // Validation CJAVal j_check; if (j_check.Deserialize(j_built_str)) { Print("JAson Feature Check:"); // JAson HAS Size() and check methods! // But HasKey is O(N) linear search (we checked source). int j_root_size = j_check.Size(); Print(" Size: ", j_root_size, " (Expected: 4)"); // HasKey returns pointer or NULL bool j_has_meta = (CheckPointer(j_check.HasKey("meta")) != POINTER_INVALID); Print(" HasKey: ", j_has_meta, " (Exists, but O(N))"); // GetKeys still missing a direct 'GetKeys(string& dst[])' helper // without manual iteration of children. Print(" GetKeys: ? (Manual iteration required)"); string uni_legacy = j_check["unicode"].ToStr(); bool match = (uni_legacy == "R$ 100,00 \u00A9"); Print(" Read: ", match ? "PASS" : "FAIL"); } //--------------------------------------------------------- // RESULTS //--------------------------------------------------------- Print("=================================================="); Print("VALIDATION CHECK:"); // Compact both strings for fair comparison (remove potential whitespace // differences if any) Actually, let's just compare raw first. if (dummy_ser == "") dummy_ser = toolkit.Serialize(); // Ensure we have last state if (dummy_ser != dummy_ser) { /* No-op, just to ensure variable usage */ } // Re-serialize final state to be sure string a_final = toolkit.Serialize(); string j_final = jason.Serialize(); if (a_final == j_final) { Print("PASS: Output matches exactly."); } else { Print("WARNING: Output mismatch!"); Print("AI-Toolkit: ", StringSubstr(a_final, 0, 100), "..."); Print("JAson: ", StringSubstr(j_final, 0, 100), "..."); Print("Lengths: AI-Toolkit=", StringLen(a_final), " JAson=", StringLen(j_final)); } Print("=================================================="); Print(StringFormat("TOTAL TIME (AI-Toolkit): %d us", total_toolkit)); Print(StringFormat("TOTAL TIME (Legacy): %d us", total_jason)); double speedup = (double)total_jason / (double)total_toolkit; Print(StringFormat("OVERALL SPEEDUP: %.2fx", speedup)); Print("Breakdown:"); Print(StringFormat(" Parse: %.2fx", (double)j_parse / t_parse)); Print(StringFormat(" Read: %.2fx", (double)j_read / t_read)); Print(StringFormat(" Array Sum: %.2fx", (double)j_sum / t_sum)); Print(StringFormat(" Serialize: %.2fx", (double)j_ser / t_ser)); Print("=================================================="); //--------------------------------------------------------- // 5. BINANCE KLINES - REAL DATA STRESS TEST (50 BTCUSDT 1H Candles) //--------------------------------------------------------- Print("--- BINANCE KLINES STRESS TEST (Real Data) ---"); // Load real Binance klines from embedded resource string binance_path = "binance_btcusdt_klines.json"; int fh = FileOpen(binance_path, FILE_READ | FILE_TXT | FILE_ANSI | FILE_COMMON); if (fh == INVALID_HANDLE) { // Try local path (same dir as script) fh = FileOpen("..\\Experts\\Desenvolvimento\\publicacao_llms_trade\\ai-" "toolkit\\fast_json\\Tests\\binance_btcusdt_klines.json", FILE_READ | FILE_TXT | FILE_ANSI); } string binance = ""; if (fh != INVALID_HANDLE) { while (!FileIsEnding(fh)) binance += FileReadString(fh); FileClose(fh); Print("Loaded Binance JSON: ", StringLen(binance), " bytes"); } else { // Fallback: minimal inline payload Print("WARNING: Could not load binance_btcusdt_klines.json, using inline " "fallback"); binance = "[" "[1708128000000,\"42156.78000000\",\"42389.99000000\",\"42100." "00000000\",\"42350.12000000\",\"1234.56780000\",1708131599999," "\"52345678.90123456\",28451,\"678.12340000\",\"28712345." "67890123\",\"0\"]," "[1708131600000,\"42350.12000000\",\"42499.50000000\",\"42200." "00000000\",\"42445.67000000\",\"987.65430000\",1708135199999," "\"41876543.21098765\",22134,\"512.98760000\",\"21765432." "10987654\",\"0\"]," "[1708135200000,\"42445.67000000\",\"42600.00000000\",\"42380." "50000000\",\"42589.23000000\",\"1567.89010000\",1708138799999," "\"66789012.34567890\",31245,\"834.56780000\",\"35543210." "98765432\",\"0\"]" "]"; } int binance_loops = Loops / 5; // Heavy payload = fewer loops if (binance_loops < 1000) binance_loops = 1000; // Binance Parse CJson binance_json; ulong b_parse = 0, b_ser = 0; t0 = GetMicrosecondCount(); for (int i = 0; i < binance_loops; i++) { binance_json.Parse(binance); } b_parse = GetMicrosecondCount() - t0; // Binance Serialize t0 = GetMicrosecondCount(); for (int i = 0; i < binance_loops; i++) { dummy_ser = binance_json.Serialize(); } b_ser = GetMicrosecondCount() - t0; Print(StringFormat("Binance Klines (%d loops):", binance_loops)); Print(StringFormat(" Parse: %d us (%.1f us/op)", b_parse, (double)b_parse / binance_loops)); Print(StringFormat(" Serialize: %d us (%.1f us/op)", b_ser, (double)b_ser / binance_loops)); // Binance Legacy Compare CJAVal binance_legacy; ulong bj_parse = 0, bj_ser = 0; t0 = GetMicrosecondCount(); for (int i = 0; i < binance_loops; i++) { binance_legacy.Deserialize(binance); } bj_parse = GetMicrosecondCount() - t0; t0 = GetMicrosecondCount(); for (int i = 0; i < binance_loops; i++) { dummy_ser = binance_legacy.Serialize(); } bj_ser = GetMicrosecondCount() - t0; Print(StringFormat("Binance Legacy (%d loops):", binance_loops)); Print(StringFormat(" Parse: %d us (%.1f us/op)", bj_parse, (double)bj_parse / binance_loops)); Print(StringFormat(" Serialize: %d us (%.1f us/op)", bj_ser, (double)bj_ser / binance_loops)); Print(StringFormat(" Parse Speedup: %.2fx", (double)bj_parse / b_parse)); Print(StringFormat(" Serial Speedup: %.2fx", (double)bj_ser / b_ser)); Print("=================================================="); }