Last active
February 8, 2025 13:34
-
-
Save GiuseppeChillemi/8bd833ba511af556ae135474088dda06 to your computer and use it in GitHub Desktop.
Context reuse speed test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Red [ | |
Title: "Speed comparison with context keeping" | |
Description: { | |
This test compares the same function with and without the | |
initialization phase | |
The purpose of this the is to test context reusing: | |
A function has ofen an check and init phase. In stict loops | |
many of these checks and inits could be bypassed after the first | |
one if part of the function context could be passed again | |
at each run. | |
APPLY could do this if modified but also | |
Rebol3 has the ability to use accept external context for | |
the called function | |
Lets see the difference: I have created an extract-keys | |
and extract-keys-fast fuctions. The latter one uses pre | |
calculated values in the system context. | |
Later, I have tested multiple optimization, | |
adding each optimization benchmarks | |
At the end you can find the results | |
} | |
] | |
contains?: func [ | |
target [block!] | |
data [block!] | |
] [ | |
either empty? exclude data target [true] [false] | |
] | |
extract-keys: func [ | |
data [map! object!] | |
keys [block!] | |
datatype [datatype!] | |
/couples "keys are couples" | |
/into "Extract inside a target" | |
target [map! block!] | |
/local | |
keys-to-pick | |
keys-to-write | |
data-keys | |
target-keys | |
go-on? | |
err | |
out-data | |
] [ | |
err: "Keys not valid" | |
go-on?: case [ | |
datatype = object! [parse keys [any [word!]]] | |
datatype = map! [parse keys [any [any-word! | any-string! | any-block!]]] | |
] | |
;--- Settings the keys to request and to write | |
if go-on? [ | |
either couples [ | |
keys-to-pick: extract keys 2 | |
keys-to-write: extract/index keys 2 2 | |
] [ | |
keys-to-pick: keys-to-write: keys | |
] | |
;--- Creating the target container or using the one provided by /INTO | |
either into [ | |
out-data: target | |
] [ | |
out-data: copy [] | |
forall keys-to-write [ | |
append out-data reduce [ | |
either datatype = object! [to-set-word :keys-to-write/1][:keys-to-write/1] | |
none | |
] | |
] | |
err: "Error creating datatype" | |
either attempt [out-data: make datatype out-data] [go-on?: true] [go-on?: false] | |
] | |
] | |
;Intead of the previous code, an empty container should used | |
if go-on? [ | |
target-keys: words-of out-data | |
data-keys: words-of data | |
go-on?: contains? data-keys keys-to-pick | |
] | |
;--- The CORE of the code | |
; | |
if go-on? [ | |
forall keys-to-pick [ | |
out-data/(pick keys-to-write index? keys-to-pick): :data/(:keys-to-pick/1) | |
] | |
] | |
either go-on? [out-data] [do make error! err] | |
] | |
map-types: make typeset! [any-word! any-string! any-block!] | |
extract-key-typeset: func [ | |
data [map! object!] | |
keys [block!] | |
datatype [datatype!] | |
/couples "keys are couples" | |
/into "Extract inside a target" | |
target [map! block!] | |
/local | |
keys-to-pick | |
keys-to-write | |
data-keys | |
target-keys | |
go-on? | |
err | |
out-data | |
] [ | |
err: "Keys not valid" | |
go-on?: case [ | |
datatype = object! [parse keys [any [word!]]] | |
datatype = map! [forall keys [find map-types :keys/1]] | |
] | |
;--- Settings the keys to request and to write | |
if go-on? [ | |
either couples [ | |
keys-to-pick: extract keys 2 | |
keys-to-write: extract/index keys 2 2 | |
] [ | |
keys-to-pick: keys-to-write: keys | |
] | |
;--- Creating the target container or using the one provided by /INTO | |
either into [ | |
out-data: target | |
] [ | |
out-data: copy [] | |
forall keys-to-write [ | |
append out-data reduce [ | |
either datatype = object! [to-set-word :keys-to-write/1][:keys-to-write/1] | |
none | |
] | |
] | |
err: "Error creating datatype" | |
either attempt [out-data: make datatype out-data] [go-on?: true] [go-on?: false] | |
] | |
] | |
;Intead of the previous code, an empty container should used | |
if go-on? [ | |
target-keys: words-of out-data | |
data-keys: words-of data | |
go-on?: contains? data-keys keys-to-pick | |
] | |
;--- The CORE of the code | |
; | |
if go-on? [ | |
forall keys-to-pick [ | |
out-data/(pick keys-to-write index? keys-to-pick): :data/(:keys-to-pick/1) | |
] | |
] | |
either go-on? [out-data] [do make error! err] | |
] | |
extract-key-typeset-type-forall: func [ | |
data [map! object!] | |
keys [block!] | |
datatype [datatype!] | |
/couples "keys are couples" | |
/into "Extract inside a target" | |
target [map! block!] | |
/local | |
keys-to-pick | |
keys-to-write | |
data-keys | |
target-keys | |
go-on? | |
err | |
out-data | |
] [ | |
err: "Keys not valid" | |
go-on?: case [ | |
datatype = object! [forall keys [type? :keys/1 = word!]] | |
datatype = map! [forall keys [find map-types :keys/1]] | |
] | |
;--- Settings the keys to request and to write | |
if go-on? [ | |
either couples [ | |
keys-to-pick: extract keys 2 | |
keys-to-write: extract/index keys 2 2 | |
] [ | |
keys-to-pick: keys-to-write: keys | |
] | |
;--- Creating the target container or using the one provided by /INTO | |
either into [ | |
out-data: target | |
] [ | |
out-data: copy [] | |
forall keys-to-write [ | |
append out-data reduce [ | |
either datatype = object! [to-set-word :keys-to-write/1][:keys-to-write/1] | |
none | |
] | |
] | |
err: "Error creating datatype" | |
either attempt [out-data: make datatype out-data] [go-on?: true] [go-on?: false] | |
] | |
] | |
;Intead of the previous code, an empty container should used | |
if go-on? [ | |
target-keys: words-of out-data | |
data-keys: words-of data | |
go-on?: contains? data-keys keys-to-pick | |
] | |
;--- The CORE of the code | |
; | |
if go-on? [ | |
forall keys-to-pick [ | |
out-data/(pick keys-to-write index? keys-to-pick): :data/(:keys-to-pick/1) | |
] | |
] | |
either go-on? [out-data] [do make error! err] | |
] | |
recycle/off | |
extract-keys-fast: func [ | |
data [map! object!] | |
keys [block!] | |
datatype [datatype!] | |
/couples "keys are couples" | |
/into "Extract inside a target" | |
target [map! block!] | |
/local | |
] [ | |
forall keys-to-pick [ | |
out-data/(pick keys-to-write index? keys-to-pick): :data/(:keys-to-pick/1) | |
] | |
out-data | |
] | |
extract-keys-passed-keys: func [ | |
data [map! object!] | |
keys [block!] | |
datatype [datatype!] | |
keys-to-pick | |
keys-to-write | |
/couples "keys are couples" | |
/into "Extract inside a target" | |
target [map! block!] | |
/local | |
data-keys | |
target-keys | |
go-on? | |
err | |
out-data | |
] [ | |
err: "Keys not valid" | |
go-on?: case [ | |
datatype = object! [parse keys [any [word!]]] | |
datatype = map! [parse keys [any [any-word! | any-string! | any-block!]]] | |
] | |
;--- Settings the keys to request and to write | |
if go-on? [ | |
;--- Creating the target container or using the one provided by /INTO | |
either into [ | |
out-data: target | |
] [ | |
out-data: copy [] | |
forall keys-to-write [ | |
append out-data reduce [ | |
either datatype = object! [to-set-word :keys-to-write/1][:keys-to-write/1] | |
none | |
] | |
] | |
err: "Error creating datatype" | |
either attempt [out-data: make datatype out-data] [go-on?: true] [go-on?: false] | |
] | |
] | |
;Intead of the previous code, an empty container should used | |
if go-on? [ | |
target-keys: words-of out-data | |
data-keys: words-of data | |
go-on?: contains? data-keys keys-to-pick | |
] | |
;--- The CORE of the code | |
; | |
if go-on? [ | |
forall keys-to-pick [ | |
out-data/(pick keys-to-write index? keys-to-pick): :data/(:keys-to-pick/1) | |
] | |
] | |
either go-on? [out-data] [do make error! err] | |
] | |
extract-keys-external-out-data-container: func [ | |
data [map! object!] | |
keys [block!] | |
datatype [datatype!] | |
out-data | |
/couples "keys are couples" | |
/into "Extract inside a target" | |
target [map! block!] | |
/local | |
keys-to-pick | |
keys-to-write | |
data-keys | |
target-keys | |
go-on? | |
err | |
] [ | |
err: "Keys not valid" | |
go-on?: case [ | |
datatype = object! [parse keys [any [word!]]] | |
datatype = map! [parse keys [any [any-word! | any-string! | any-block!]]] | |
] | |
;--- Settings the keys to request and to write | |
if go-on? [ | |
either couples [ | |
keys-to-pick: extract keys 2 | |
keys-to-write: extract/index keys 2 2 | |
] [ | |
keys-to-pick: keys-to-write: keys | |
] | |
;--- Creating the target container or using the one provided by /INTO | |
either into [ | |
out-data: target | |
] [ | |
] | |
] | |
;Intead of the previous code, an empty container should used | |
if go-on? [ | |
target-keys: words-of out-data | |
data-keys: words-of data | |
go-on?: contains? data-keys keys-to-pick | |
] | |
;--- The CORE of the code | |
; | |
if go-on? [ | |
forall keys-to-pick [ | |
out-data/(pick keys-to-write index? keys-to-pick): :data/(:keys-to-pick/1) | |
] | |
] | |
either go-on? [out-data] [do make error! err] | |
] | |
extract-keys-no-contains: func [ | |
data [map! object!] | |
keys [block!] | |
datatype [datatype!] | |
/couples "keys are couples" | |
/into "Extract inside a target" | |
target [map! block!] | |
/local | |
keys-to-pick | |
keys-to-write | |
data-keys | |
target-keys | |
go-on? | |
err | |
out-data | |
] [ | |
err: "Keys not valid" | |
go-on?: case [ | |
datatype = object! [parse keys [any [word!]]] | |
datatype = map! [parse keys [any [any-word! | any-string! | any-block!]]] | |
] | |
;--- Settings the keys to request and to write | |
if go-on? [ | |
either couples [ | |
keys-to-pick: extract keys 2 | |
keys-to-write: extract/index keys 2 2 | |
] [ | |
keys-to-pick: keys-to-write: keys | |
] | |
;--- Creating the target container or using the one provided by /INTO | |
either into [ | |
out-data: target | |
] [ | |
out-data: copy [] | |
forall keys-to-write [ | |
append out-data reduce [ | |
either datatype = object! [to-set-word :keys-to-write/1][:keys-to-write/1] | |
none | |
] | |
] | |
err: "Error creating datatype" | |
either attempt [out-data: make datatype out-data] [go-on?: true] [go-on?: false] | |
] | |
] | |
;Intead of the previous code, an empty container should used | |
; if go-on? [ | |
; target-keys: words-of out-data | |
; data-keys: words-of data | |
; | |
; go-on?: contains? data-keys keys-to-pick | |
; ] | |
;--- The CORE of the code | |
; | |
if go-on? [ | |
forall keys-to-pick [ | |
out-data/(pick keys-to-write index? keys-to-pick): :data/(:keys-to-pick/1) | |
] | |
] | |
either go-on? [out-data] [do make error! err] | |
] | |
;--- Repetitions for the test | |
times: 100000 | |
;--- Data to keep values from | |
m: #[a: 11 b: 22 c: 22] | |
;--- Running test with init inside | |
prin "Full: " probe dt [loop times [extract-keys m [a b] object!]] | |
;---- extract-key-typeset | |
prin "Typeset: " probe dt [loop times [extract-key-typeset m [a b] object!]] | |
;---- extract-key-typeset | |
prin "Typeset-type-forall: " probe dt [loop times [extract-key-typeset-type-forall m [a b] object!]] | |
;--- Passed keys | |
;either couples [ | |
; keys-to-pick: extract keys 2 | |
; keys-to-write: extract/index keys 2 2 | |
;] [ | |
; keys-to-pick: keys-to-write: keys | |
;] | |
prin "Typeset-type-passed-keys: " probe dt [loop times [extract-keys-passed-keys m [a b] object! [a b] [a b]]] | |
;---- extract-key-typeset | |
datatype: object! | |
out-data: copy [] | |
keys-to-write: [a b] | |
forall keys-to-write [ | |
append out-data reduce [ | |
either datatype = object! [to-set-word :keys-to-write/1][:keys-to-write/1] | |
none | |
] | |
] | |
either attempt [out-data: make datatype out-data] [go-on?: true] [go-on?: false] | |
prin "Typeset-type-external-out-data-container: " probe dt [loop times [copy extract-keys-external-out-data-container m [a b] object! out-data]] | |
;------ | |
prin "extract-keys-no-contains: " probe dt [loop times [extract-keys-no-contains m [a b] object!]] | |
;--- Running test without init but reusing containers | |
keys-to-pick: keys-to-write: [a b] | |
out-data: make object! [a: none b: none] | |
Prin "Fast: " probe dt [loop times [extract-keys-fast m [a b] object!]] | |
;--- Results (Note, they are not cumulative, the reference is the FULL version and not the previous one) | |
;Full: 0:00:01.37004 ;The base function | |
;Typeset: 0:00:01.34702 ;Changed parse of multiple datatypes with forall | |
;Typeset-type-forall: 0:00:01.276 ; As before + change of parse of for WORD! keys with a forall [word? key] | |
;Typeset-type-passed-keys: 0:00:01.28894 ;The keys are passed and not extracted | |
;Typeset-type-external-out-data-container: 0:00:01.04831 ;The target container is created externally and reused. On exit you copy it | |
;extract-keys-no-contains: 0:00:01.00221 ;No check if keys exists | |
;Fast: 0:00:00.174525 ;Just the loop and everything external |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment