Skip to content

Commit

Permalink
Various performance improvements (#61)
Browse files Browse the repository at this point in the history
* Minor performance improvements to MetaModelica.jl

* Fixing formatting of dangerous

* Updates to MetaModelica.jl

* Updated versioning

* Changed Jenkinsfile to use 1.10 bookworm from 1.1

* Added test as an explicit dependency
  • Loading branch information
JKRT authored Jan 10, 2024
1 parent 8afe308 commit e27149f
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .CI/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pipeline {
docker {
// Large image with full OpenModelica build dependencies; lacks omc and OMPython
label 'linux'
image 'julia:1.1-buster'
image 'julia:1.10.0-bookworm'
alwaysPull true
args '--privileged'
}
Expand Down
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
name = "MetaModelica"
uuid = "9d7f2a79-07b5-5542-8b19-c0100dda6b06"
authors = ["Martin Sjölund <[email protected]>", "John Tinnerholm <[email protected]>"]
version = "0.0.3"
version = "0.0.4"

[deps]
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
ExportAll = "ad2082ca-a69e-11e9-38fa-e96309a31fe4"
FastClosures = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a"
ImmutableList = "4a558cac-c1ed-11e9-20da-3584bcd8709a"
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
julia = "1.1"
julia = "1.7"
2 changes: 1 addition & 1 deletion src/MetaModelica.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export @match, @matchcontinue, MatchFailure, ModelicaReal, ModelicaInteger
export @Uniontype, @Record, @UniontypeDecl, @ExtendedFunction, @ExtendedAnonFunction
export List, list, Nil, nil, Cons, cons, =>, Option, SOME, NONE, SourceInfo, SOURCEINFO
export @do_threaded_for, <|, @shouldFail, sourceInfo, _cons, @importDBG
export @assign
export @assign, @Mutable_Uniontype, @closure

include("exportmetaRuntime.jl")
include("dangerous.jl")
Expand Down
153 changes: 134 additions & 19 deletions src/dangerous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,63 +9,178 @@ import ExportAll
using ..MetaModelica

""" O(1) """
function arrayGetNoBoundsChecking(arr::Array, index::ModelicaInteger)
function arrayGetNoBoundsChecking(arr::Vector{A}, index::ModelicaInteger) where {A}
@inbounds arr[index]
end

""" O(1) """
function arrayUpdateNoBoundsChecking(arr::Array{T}, index::ModelicaInteger, newValue::T) where {T}
function arrayUpdateNoBoundsChecking(arr::Vector{A},
index::ModelicaInteger,
newValue::A) where {A}
local newArray = arr
if index < 0
println("arrayUpdateNoBoundsChecking: index < 0!")
end
@inbounds newArray[index] = newValue
newArray
return newArray
end

""" Creates a new array where the elements are *not* initialized!. Any attempt to
access an uninitialized elements may cause segmentation faults if you're
lucky, and pretty much anything else if you're not. Do not use unless you will
immediately fill the whole array with data. The dummy variable is used to fix
the type of the array. """
function arrayCreateNoInit(size::ModelicaInteger, dummy::T) where {T}
local arr = fill(dummy, size)
the type of the array.
"""
function arrayCreateNoInit(size::ModelicaInteger, dummy::A)::Array{A} where {A}
local arr::Array{A} = fill(dummy, size)
arr
end

""" O(1) """
function stringGetNoBoundsChecking(str::String, index::ModelicaInteger)::ModelicaInteger
function stringGetNoBoundsChecking(str::String, index::ModelicaInteger)
local ch::ModelicaInteger
if index < 0
println("stringGetNoBoundsChecking: index < 0!")
end
ch = @inbounds str[index]
end

""" Not possible unless we write a C list impl for Julia """
function listReverseInPlace(inList::List{T}) where {T}
function listReverseInPlace(inList::List{T})::List{T} where {T}
MetaModelica.listReverse(inList)
end

function listReverseInPlace2(inList::Nil)
return inList#MetaModelica.listReverse(inList)
end

"""
Unsafe implementation of list reverse in place.
Instead of creating new cons cells we swap pointers...
"""
function listReverseInPlace2(lst::Cons{T}) where {T}
local prev = nil
#= Declare an unsafe pointer to the list =#
local oldCdrPtr::Ptr{List{T}}
GC.@preserve while (!(lst isa Nil))
println("prev at the iteration:")
println(prev)
println("lst at the iteration:")
println(lst)
println("before oldCdr = $(lst.tail)")
oldCdr = deepcopy(lst.tail)
println("Before listSetRest($lst, $prev)")
listSetRest(lst, prev)
println("Before prev = $lst")
prev = lst
println("Before lst = $(oldCdr) //oldCdr")
lst = oldCdr
end
println("After loop")
return prev
end


# """
# O(1). A destructive operation changing the \"first\" part of a cons-cell.
# TODO: Not implemented
# """
# function listSetFirst(inConsCell::Cons{A}, inNewContent::A) where {A} #= A non-empty list =#
# firstPtr::Ptr{A} = unsafe_getListAsPtr(inConsCell)
# #local newHead = Cons{T}(inNewContent, inConsCell.tail)
# # unsafe_store!(firstPtr, inNewContent)
# end

""" O(1). A destructive operation changing the rest part of a cons-cell """
#= NOTE: Make sure you do NOT create cycles as infinite lists are not handled well in the compiler. =#
function listSetRest(inConsCell::Cons{A}, inNewRest::Cons{A}) where {A} #= A non-empty list =#
newTailPtr::Ptr{Cons{A}} = unsafe_getListAsPtr(inNewRest)
inConsCellTailPtr::Ptr{Cons{A}} = unsafe_getListTailAsPtr(inConsCell)
inConsCellTailPtr2::Ptr{Cons{A}} = unsafe_getListAsPtr(inConsCell)
GC.@preserve(unsafe_store!(inConsCellTailPtr, unsafe_load(newTailPtr)))
return inConsCell
end

"""
We create one cons cell when the tail we are setting is a nil...
"""
function listSetRest(inConsCell::Cons{A}, inNewRest::Nil) where {A} #= A non-empty list =#
local lstPtr::Ptr{Cons{A}} = unsafe_getListAsPtr(inConsCell)
local val = inConsCell.head
GC.@preserve unsafe_store!(lstPtr, Cons{A}(inConsCell.head, inNewRest))
return inConsCell
end

""" O(1). A destructive operation changing the \"first\" part of a cons-cell. """
function listSetFirst(inConsCell::Cons{A}, inNewContent::A) where {A} #= A non-empty list =#
@assign inConsCell.head = inNewConent
end

"""
O(1). A destructive operation changing the rest part of a cons-cell
NOTE: Make sure you do NOT create cycles as infinite lists are not handled well in the compiler.
"""
O(1). A destructive operation changing the rest part of a cons-cell
NOTE: Make sure you do NOT create cycles as infinite lists are not handled well in the compiler.
"""
function listSetRest(inConsCell::Cons{T}, inNewRest::List{T}) where {T} #= A non-empty list =#
@assign inConsCell.tail = inNewRest
end


""" O(n) """
function listArrayLiteral(lst::List{T}) where {T}
local arr = listArray(lst)
function listArrayLiteral(lst::List{A})::Array{A} where {A}
local arr::Array{A} = listArray(lst)
arr
end

"""
```
listGetFirstAsPtr(lst::Cons{T})::Ptr{T}
```
Dangerous function.
Gets the first element of the list as a pointer of type T.
Unless it is nil then we get a NULL pointer
"""
function unsafe_getListHeadAsPtr(lst::Cons{T}) where{T}
convert(Ptr{T}, unsafe_pointer_from_objref(lst.head))
end

"""
``` listGetFirstAsPtr(nil)::Ptr{Nothing}```
Returns a null pointer
"""
function unsafe_getListHeadAsPtr(lst::Nil)
unsafe_pointer_from_objref(nil)
end

"""
Fetches the pointer to the tail of the list
```
unsafe_listGetTailAsPtr{lst::List{T}}::Ptr{Cons{T}}
```
"""
function unsafe_getListTailAsPtr(lst::List{T}) where {T}
if lst.tail === nil
return unsafe_pointer_from_objref(nil)
else
convert(Ptr{Cons{T}}, unsafe_pointer_from_objref(lst.tail))
end
end

"""
Unsafley get a pointer to a list.
"""
function unsafe_getListAsPtr(lst::List{T}) where {T}
if lst === nil
ptrToNil::Ptr{Nil{Any}} = unsafe_pointer_from_objref(nil)
return ptrToNil
else
convert(Ptr{Cons{T}}, unsafe_pointer_from_objref(lst))
end
end

"""
Unsafe function to get pointers from immutable struct.
Use with !care!
"""
function unsafe_pointer_from_objref(@nospecialize(x))
ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x)
end


ExportAll.@exportAll()

end #=End dangerous =#
4 changes: 2 additions & 2 deletions src/matchcontinue.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
The code is based on https://github.com/RelationalAI-oss/Rematch.jl with
changes to allow keyword argument matching on structs along with
The code is originally based on https://github.com/RelationalAI-oss/Rematch.jl with
changes to allow keyword argument matching on structs along with
matching on the immutable list construct accompanying MetaModelica + some other improvements and bug fixes.
It also provides @matchcontinue macro (try the next case when any exception is thrown).
"""
Expand Down
58 changes: 42 additions & 16 deletions src/metaRuntime.jl
Original file line number Diff line number Diff line change
Expand Up @@ -369,18 +369,20 @@ end
Example: stringDelimitList({\"x\",\"y\",\"z\"}, \", \") => \"x, y, z\"
"""
function stringDelimitList(strs::List{String}, delimiter::String)::String
local str::String = ""
for n in strs
if isempty(str)
str = n
buffer = IOBuffer()
for (i,n) in enumerate(strs)
if i == 1
print(buffer, n)
else
str = str + delimiter + n
print(buffer, delimiter)
print(buffer, n)
#str = str + delimiter + n
end
end
str
return String(take!(buffer))#str
end

function stringDelimitList(strs::List{Any}, delimiter::String)::String
function stringDelimitList(strs::List, delimiter::String)::String
local str::String = ""
for n in strs
if isempty(str)
Expand Down Expand Up @@ -528,18 +530,42 @@ end

""" O(n) """
function listArray(lst::Cons{T}) where {T}
local arr::Vector{T} = T[]
for i in lst
push!(arr, i)
local N = length(lst)
local arr::Vector{T} = Vector{T}(undef, N)
i = 1
while lst !== nil
arr[i] = lst.head
i += 1
lst = lst.tail
end
arr
return arr
end

""" O(1) """
function listArray(lst::Nil)
[]
end

"""
O(n)
Same as listArray but with a dummy argument to specify the type.
"""
function listArray(lst::Cons{T}, ty) where {T}
local arr = Vector{ty}(undef, length(lst))
for i in lst
arr[i]
end
return arr
end

"""
Same as listArray but with a dummy argument to specify the type.
"""
function listArray(lst::Nil, ty)
ty[]
end

""" O(1) """
function arrayUpdate(arr::Array{A}, index::ModelicaInteger,
newValue::B)::Array{A} where {A,B}
Expand All @@ -558,9 +584,8 @@ end
Note that this operation is *not* destructive, i.e. a new array is created. """
function arrayAppend(arr1::Array{A}, arr2::Array{A})::Array{A} where {A}
local arr::Array{A}

#= Defined in the runtime =#
arr
@error "Defined in the runtime"
fail()
end

""" Returns the string representation of any value.
Expand Down Expand Up @@ -605,7 +630,7 @@ end
This is a global mutable value and should be used sparingly.
You are recommended not to use "missing" the runtime system treats this values as uninitialized and fail getGlobalRoot later on.
"""
global globalRoots = Array{Any,1}(missing, 1024)
const global globalRoots::Vector{Any} = Vector{Any}(missing, 1024)

function setGlobalRoot(index::ModelicaInteger, value::T) where {T}
if index > 1023 || index < 0
Expand Down Expand Up @@ -735,7 +760,8 @@ function referenceDebugString(functionSymbol::A)::String where {A}
name
end

""" TODO: I am far from sure that this will fly.. in Julia. The code generated from the transpiler is correct however"""
""" TODO: I am far from sure that this will fly.. in Julia.
The code generated from the transpiler is correct however"""
function isPresent(ident::T)::Bool where {T}
local b::Bool
b = true
Expand Down
Loading

0 comments on commit e27149f

Please sign in to comment.