-1

In my application I using SKPoinI as a key to store some small objects in Dictionary. SkiaSharp already used in this project for drawing and not need to be referred to only to allow SKPointI to be used as a key.

At first look, it's a good and simple solution, but I not sure because as I presume that the SkiaSharp library primarily optimized for graphics.

Maybe better create a structure that has two Int32 as coordinates and implementation of IEqitable, or create combined Int64 via BitConverter to be used as the key

key = BitConverter.ToInt64(BitConverter.GetBytes(x).Concat(BitConverter.GetBytes(y)).ToArray)

and get x and y from a key with code like:

With BitConverter.GetBytes(key)
    x = BitConverter.ToInt32(.Take(4).ToArray)
    y = BitConverter.ToInt32(.Skip(4).ToArray)
End With

What way is better if the application will add objects only once on the initial stage and then accesses them frequently?

I already using the key for 3D-storage:

Public Structure Adr3D
    Implements IEquatable(Of Adr3D)

#Region "Public Constructors"

    Public Sub New(x As Integer, y As Integer, z As Integer)
        Me.X = x
        Me.Y = y
        Me.Z = z
    End Sub

#End Region

#Region "Public Properties"

    Public ReadOnly Property X As Integer

    Public ReadOnly Property Y As Integer

    Public ReadOnly Property Z As Integer

#End Region

#Region "Public Methods"

    Public Shared Operator <>(left As Adr3D, right As Adr3D) As Boolean
        Return Not left.Equals(right)
    End Operator

    Public Shared Operator =(left As Adr3D, right As Adr3D) As Boolean
        Return left.Equals(right)
    End Operator

    Public Overrides Function Equals(obj As Object) As Boolean
        Return TypeOf obj Is Adr3D AndAlso Equals(DirectCast(obj, Adr3D))
    End Function

    Public Overloads Function Equals(other As Adr3D) As Boolean Implements IEquatable(Of Adr3D).Equals
        Return X = other.X AndAlso Y = other.Y AndAlso Z = other.Z
    End Function

    Public Overrides Function GetHashCode() As Integer
        Return HashCode.Combine(X, Y, Z)
    End Function

    Public Overrides Function ToString() As String
        Return $"{{X = {X}, Y = {Y}, Z = {Z}}}"
    End Function

#End Region

End Structure

In fact, it is not a question of "How to implement?", but "What is more efficient?"

Thanks for any advice!

  • The more efficient solution is the one which proves itself to be the faster one in **your** measurements. Are you asking us to do this profiling for you? – Doc Brown Jan 30 '21 at 07:34
  • @DocBrown No, I'll make benchmarks after implements part of the app's functionality required for getting mindful tests. For now, I'm asking recommendation on what variant of keys you and/or other developers consider as the most perspective based on experience (I think that Int64 has most memory and maybe CPU efficiency, but not sure about "bug-safety" and complexity of this implementation) – Bogdan Samchuk Jan 30 '21 at 08:41
  • 1
    My experience says one needs to make measurements, any advice without them will be a guessing game. You are asking about some [micro optimization](https://softwareengineering.stackexchange.com/questions/99445/is-micro-optimisation-important-when-coding). – Doc Brown Jan 30 '21 at 09:13

1 Answers1

0

Finally, I created a benchmark-program and find out the most efficient (fastest) implementation:

Module Program
    Const Length = 1024 * 16
    Const IterLim = 1024 * 1024 * 2
    ReadOnly Rand As New Random
    Structure Adr2D
        Implements IEquatable(Of Adr2D)
        Public Sub New(x As Integer, y As Integer)
            Me.X = x
            Me.Y = y
        End Sub
        Public ReadOnly Property X As Integer
        Public ReadOnly Property Y As Integer
        Public Shared Operator <>(left As Adr2D, right As Adr2D) As Boolean
            Return Not left.Equals(right)
        End Operator
        Public Shared Operator =(left As Adr2D, right As Adr2D) As Boolean
            Return left.Equals(right)
        End Operator
        Public Overrides Function Equals(obj As Object) As Boolean
            Return TypeOf obj Is Adr2D AndAlso Equals(DirectCast(obj, Adr2D))
        End Function
        Public Overloads Function Equals(other As Adr2D) As Boolean Implements IEquatable(Of Adr2D).Equals
            Return X = other.X AndAlso Y = other.Y
        End Function
        Public Overrides Function GetHashCode() As Integer
            Return HashCode.Combine(X, Y)
        End Function
        Public Overrides Function ToString() As String
            Return $"{{X = {X}, Y = {Y}}}"
        End Function
    End Structure
    Sub Main(args As String())
        Console.WriteLine($"    SKPointI: {FormatNumber(BenchSKPointI, 3)} s.")
        Console.WriteLine($"       Adr2D: {FormatNumber(BenchAdr2D, 3)} s.")
        Console.WriteLine($"Long (Int64): {FormatNumber(BenchLong, 3)} s.")
        Do
        Loop Until Console.KeyAvailable
        Console.ReadKey()
    End Sub
    Function BenchSKPointI() As Double
        Dim objSW = Stopwatch.StartNew
        Dim dicTest = New Dictionary(Of SkiaSharp.SKPointI, Double)
        For i = 1 To Length
            Dim stKey As SkiaSharp.SKPointI
            Do
                stKey = New SkiaSharp.SKPointI(Rand.Next, Rand.Next)
            Loop While dicTest.ContainsKey(stKey)
            dicTest.Add(stKey, Rand.NextDouble)
        Next
        Dim dSum As Double
        For i = 1 To IterLim
            dSum += dicTest(dicTest.Keys(Rand.Next(Length)))
        Next
        Return objSW.Elapsed.TotalSeconds
    End Function
    Function BenchAdr2D() As Double
        Dim objSW = Stopwatch.StartNew
        Dim dicTest = New Dictionary(Of Adr2D, Double)
        For i = 1 To Length
            Dim stKey As Adr2D
            Do
                stKey = New Adr2D(Rand.Next, Rand.Next)
            Loop While dicTest.ContainsKey(stKey)
            dicTest.Add(stKey, Rand.NextDouble)
        Next
        Dim dSum As Double
        For i = 1 To IterLim
            dSum += dicTest(dicTest.Keys(Rand.Next(Length)))
        Next
        Return objSW.Elapsed.TotalSeconds
    End Function
    Function BenchLong() As Double
        Dim objSW = Stopwatch.StartNew
        Dim dicTest = New Dictionary(Of Long, Double)
        For i = 1 To Length
            Dim lKey As Long
            Do
                lKey = BitConverter.ToInt64(BitConverter.GetBytes(Rand.Next).Concat(BitConverter.GetBytes(Rand.Next)).ToArray)
            Loop While dicTest.ContainsKey(lKey)
            dicTest.Add(lKey, Rand.NextDouble)
        Next
        Dim dSum As Double
        For i = 1 To IterLim
            dSum += dicTest(dicTest.Keys(Rand.Next(Length)))
        Next
        Return objSW.Elapsed.TotalSeconds
    End Function
End Module

The program gives the next output:

    SKPointI: 62,400 s.
       Adr2D: 56,954 s.
Long (Int64): 60,531 s.

According to program output, the fastest are own Structure that implements IEquatable (Adr2D).

  • To me, it looks like this micro benchmark shows that the differences are negligible for most real world cases. If I were in your shoes, with these numbers at hand, I would probably pick the most simple (and thus most maintainable) approach - which is to reuse the existing point class instead of implementing something by myself. – Doc Brown Jan 30 '21 at 17:07
  • @DocBrown Yes, in real runtime, it is nearly nothing, but it allows split logics into data processing without third party libraries and drawing with Skiasharp or maybe another library in the future, also I already have tested structure Adr3D (Skiasharp does not contain 3D point with Integers as coordinates), I need just cut Z-coordinate. Thanks for the help in the fight with my procrastination :) – Bogdan Samchuk Jan 30 '21 at 18:04