> You're building it, not changing it. Does it make a difference?
I am not sure I understand what you mean.
We are not building it, we are using it.
In the AutoCreateFields() we don't build anything, we just cache the RTTI and its published properties classes the first time we use this class.
Then fClassNewInstance() is a very efficient way of creating each needed class instance, with the proper virtual constructor if needed.
The FPC internal layouts are used to bypass the RTL when it makes a difference.
See mormot.core.rtti.pas about how we use the official typinfo unit as source, but encapsulate it into a Delphi/FPC compatible wrapper, and also introduce some RTTI cache as TRttiCustom/TRttiJson classes, with ready-to-use methods and settings.
mORMot users don't need to deal into those details. They just use the high level methods like JSON, ORM or SOA, letting the low level framework do its work.
Most of the low level code is deeply optimized, with a lot of pointer arithmetic for sure, sometimes with huge amount of asm (up to AVX2/BMI SIMD), but it is transparent to the user, and cross-platform.
If you look at the AutoCreateFields() function generated, once inlined into the class constructor, you will see:
MORMOT.CORE.JSON$_$TSYNAUTOCREATEFIELDS_$__$$_CREATE$$TSYNAUTOCREATEFIELDS PROC
push rbx ; 0000 _ 53
.....
mov rax, qword ptr [rsp+8H] ; 0072 _ 48: 8B. 44 24, 08
mov rax, qword ptr [rax] ; 0077 _ 48: 8B. 00
mov rbx, qword ptr [rax+48H] ; 007A _ 48: 8B. 58, 48
test rbx, rbx ; 007E _ 48: 85. DB
jz ?_2462 ; 0081 _ 74, 09
test dword ptr [rbx+3CH], 4000H ; 0083 _ F7. 43, 3C, 00004000
jnz ?_2463 ; 008A _ 75, 0D
?_2462: mov rdi, qword ptr [rsp+8H] ; 008C _ 48: 8B. 7C 24, 08
call MORMOT.CORE.JSON_$$_DOREGISTERAUTOCREATEFIELDS$TOBJECT$$TRTTIJSON; 0091 _ E8, 00000000(PLT r)
mov rbx, rax ; 0096 _ 48: 89. C3
?_2463: mov r12, qword ptr [rbx+0DCH] ; 0099 _ 4C: 8B. A3, 000000DC
test r12, r12 ; 00A0 _ 4D: 85. E4
jz ?_2465 ; 00A3 _ 74, 35
mov rax, qword ptr [r12-8H] ; 00A5 _ 49: 8B. 44 24, F8
lea rbx, ptr [rax+1H] ; 00AA _ 48: 8D. 58, 01
ALIGN 8
?_2464: mov r13, qword ptr [r12] ; 00B0 _ 4D: 8B. 2C 24
mov rdi, qword ptr [r13] ; 00B4 _ 49: 8B. 7D, 00
mov rax, qword ptr [r13] ; 00B8 _ 49: 8B. 45, 00
call qword ptr [rax+0D4H] ; 00BC _ FF. 90, 000000D4
mov rcx, qword ptr [rsp+8H] ; 00C2 _ 48: 8B. 4C 24, 08
mov rdx, qword ptr [r13+8H] ; 00C7 _ 49: 8B. 55, 08
add rdx, rcx ; 00CB _ 48: 01. CA
mov qword ptr [rdx], rax ; 00CE _ 48: 89. 02
add r12, 8 ; 00D1 _ 49: 83. C4, 08
sub ebx, 1 ; 00D5 _ 83. EB, 01
jnz ?_2464 ; 00D8 _ 75, D6
?_2465: mov qword ptr [rsp+10H], 1 ; 00DA _ 48: C7. 44 24, 10, 00000001
.....
The resulting asm is really optimized, as fast as it could be with manually written asm, even if it was written in plain pascal.
It may be confusing to read, but it is how we achieve best performance.
But it is still real cross-platform pascal, and the very same code works on ARM32 or AARCH64 with no problem, and good performance.
In the mORMot core, we use the pascal language as a "portable assembler", as C is used in the Linux kernel or SQlite3 library for instance.
It may be confusing, but it is similar to what is done is the lowest part of the FPC RTL.
This is how we achieved our JSON parsing to be magnitude times faster than FPC/Delphi alternatives, in plain pascal code: by looking deeply at the generated assembly and aggressively profiling the code, following
https://www.agner.org/optimize reference material.