2026-01-25 23:16:07 -03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| TestJsonBenchmark.mq5 |
|
|
|
|
|
//| AI-Toolkit |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property script_show_inputs
|
2026-02-18 20:43:26 -03:00
|
|
|
#include "../fast_json.mqh"
|
2026-02-18 20:14:35 -03:00
|
|
|
#include <JAson.mqh>
|
2026-01-25 23:16:07 -03:00
|
|
|
|
|
|
|
|
input int Loops = 50000;
|
|
|
|
|
|
2026-01-25 23:39:19 -03:00
|
|
|
void OnStart() {
|
2026-02-17 12:02:14 -03:00
|
|
|
Print("=== JSON BENCHMARK: THE RECKONING ===");
|
2026-02-18 20:14:35 -03:00
|
|
|
Print("Comparing fast_json vs JAson");
|
2026-01-25 23:39:19 -03:00
|
|
|
|
|
|
|
|
// Complex JSON with Mixed Types, Nested Objects, and Arrays
|
|
|
|
|
string json =
|
|
|
|
|
"{"
|
2026-01-25 23:16:07 -03:00
|
|
|
"\"id\": 12345,"
|
2026-01-25 23:39:19 -03:00
|
|
|
"\"config\": {\"active\": true, \"mode\": \"swar\", \"threshold\": "
|
|
|
|
|
"0.0005},"
|
2026-01-25 23:16:07 -03:00
|
|
|
"\"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\","
|
2026-01-25 23:39:19 -03:00
|
|
|
"\"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)
|
2026-01-25 23:16:07 -03:00
|
|
|
.EndObj()
|
|
|
|
|
.EndArr()
|
2026-01-25 23:39:19 -03:00
|
|
|
.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:");
|
2026-02-17 12:02:14 -03:00
|
|
|
// 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)");
|
2026-02-18 20:43:26 -03:00
|
|
|
|
2026-02-17 12:02:14 -03:00
|
|
|
// 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))");
|
2026-02-18 20:43:26 -03:00
|
|
|
|
|
|
|
|
// GetKeys still missing a direct 'GetKeys(string& dst[])' helper
|
2026-02-17 12:02:14 -03:00
|
|
|
// without manual iteration of children.
|
|
|
|
|
Print(" GetKeys: ? (Manual iteration required)");
|
2026-01-25 23:39:19 -03:00
|
|
|
|
|
|
|
|
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("==================================================");
|
2026-02-18 20:43:26 -03:00
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
// 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("==================================================");
|
2026-01-25 23:16:07 -03:00
|
|
|
}
|