Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct Element with limited number or Values

Tags:

julia

I want to create a struct

mutable struct myStruct
    id::string
end

I do want id to be a string, but I want it to take only 1 of 3 values (e.g. "a", "b" or "c"). My idea is that could theoretically speed up operations.

  1. Am I / would I be right about the speed increase?
  2. Is this possible and how?
like image 590
Georgery Avatar asked Jan 01 '23 05:01

Georgery


1 Answers

I don't know of any way to let id be a String, while constraining it to only have only one of 3 possible values in such a way that the compiler could take advantage of it.

However, if leaving the String domain is an option, you have a few possibilities. Let's try and benchmark some of them here. For the sake of the example, let's assume that this id field is used to test the equality between two MyStruct instances.

A possible (very minimalistic) generic implementation of such a type would be the following, where parameter T determines the type used for the id.

mutable struct MyType{T}
    id :: T
end

# Two MyType instances are equal if their ids are equal
import Base.==
==(a::MyType{T}, b::MyType{T}) where {T} = (a.id == b.id)

Now let's benchmark the equality operator in various cases. First, the base case using strings as identifiers:

julia> using BenchmarkTools

julia> a = MyType("apple")
MyType{String}("apple")

julia> b = MyType("banana")
MyType{String}("banana")

julia> @btime $a == $b
  4.326 ns (0 allocations: 0 bytes)
false

We would expect integers to be faster, and this is indeed the case:

julia> a = MyType(1)
MyType{Int64}(1)

julia> b = MyType(2)
MyType{Int64}(2)

julia> @btime $a == $b
  1.882 ns (0 allocations: 0 bytes)
false

But perhaps the API is less convenient that way. Using an enumerated type might more readable (users don't have to remember which value conrresponds to "apple"). It also prevents using any value that was not predefined, while keeping the same performance:

julia> @enum Fruit begin
           apple
           banana
       end

julia> a = MyType(apple)
MyType{Fruit}(apple)

julia> b = MyType(banana)
MyType{Fruit}(banana)

julia> @btime $a == $b
  1.816 ns (0 allocations: 0 bytes)
false

A final option that might be worth considering is to use symbols for keys, which preserves (IMO) the useability as well as the performance:

julia> a = MyType(:apple)
MyType{Symbol}(:apple)

julia> b = MyType(:banana)
MyType{Symbol}(:banana)

julia> @btime $a == $b
  1.883 ns (0 allocations: 0 bytes)
false

Nothing prevents anyone from building an illegal value such as MyStruct(:cheddar), but it fits nicely in a context where strings are manipulated. For example, if you get the id as a string somewhere in your workflow, you can easily convert it to a symbol before constructing the MyStruct instance:

julia> id = "apple"
"apple"

julia> a = MyType(Symbol(id))
MyType{Symbol}(:apple)
like image 108
François Févotte Avatar answered Feb 19 '23 08:02

François Févotte