Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jule: new trait implementation #113

Merged
merged 6 commits into from
Sep 1, 2024
Merged

jule: new trait implementation #113

merged 6 commits into from
Sep 1, 2024

Conversation

mertcandav
Copy link
Member

This PR implements a new trait logic. New approach to handle traits.

This new approach focuses on increasing memory efficiency and improving performance. It achieves this with some important differences compared to the current trait implementation.

About Current Implementation

The current implementation uses a relatively efficient method of handling traits. But here we are.
Some improvements were possible.

According to the approach of the current implementation, each trait instance should have 4 different data;

  • Allocation
    Allocation is a pointer to the data itself that the trait stores. Managed by GC. The current implementation handles this well. If a pointer that is already traced by the GC is passed to the trait, for example a smart pointer, the trait uses it by directly referencing that smart pointer rather than making a new allocation. This helps reduce memory allocations and increases efficiency.
  • Pointer State
    Traits may take both a smart pointer or a normal instance for supported types. Accordingly, the deallocation method and type comparison also vary. If separate code was generated for both forms with and without smart pointers, this could significantly increase the size and compilation time of the executable. Since it does not contribute much to the runtime cost, it stores whether the stored data is a smart pointer or not with a simple boolean flag. In this way, it is sufficient to generate a single handler for each form of trait type.
  • Deallocator Function
    Allocation pointer is stored without any explicit type as required by dynamic programming. Therefore, when it comes to deallocation, it needs to be handled in a special function. The deallocator function is not handled by the compiler. C++'s generic types and compiler are relied upon. This is clearly bad. Because the generated code requires the trait data type to be transferred to C++ code as a generic type. This leads to the creation of a new instance of the trait container class for each trait. It's clearly something that negatively impacts executable size, and this is all to enable a single generic type to create a deallocator function. It should be replaced.
  • Type Offset
    The compiler generates a map with an array created on the stack for each trait type. In order to know which wrapper in which array element it should point to at runtime, it must add the offset data of the required type to the trait container class. This isn't that bad, but it does involve unnecessary overhead: a pointer arithmetic for the required type is executed every time trait is used. This is simply like array_ptr + offset.

About New Implementation

The new approach is very similar to the current practice but includes some significant efficiency improvements.

According to the approach of the new implementation, each trait instance should have 3 different data;

  • Allocation
    No update. Efficient enough.
  • Pointer State
    No update. Efficient enough.
  • Type Pointer
    The new approach maintains a general pointer and this pointer is not traced by the GC because it is guaranteed to always will point to static memory that will be available for the lifetime of the program. This pointer points directly to the type handler structure automatically created by the compiler. The only difference is that this handler structure now includes the deallocator function required for the type. The deallocator function is the first field of the structure. In this way, with a simple reinterpretation, the trait container can call the deallocator function when necessary. Each time trait is used, the type pointer is reinterpreted for the correct type, providing direct access to the required function, without the overhead of the previous pointer arithmetic. With this approach, there is no longer a need for separate type offset and deallocator function pointer datas.

@mertcandav mertcandav added compiler/runtime Related with runtime compiler Related with compiler/compile-time api About API labels Sep 1, 2024
@mertcandav mertcandav merged commit e7e21d0 into master Sep 1, 2024
0 of 42 checks passed
@mertcandav mertcandav deleted the new-traits branch September 1, 2024 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api About API compiler/runtime Related with runtime compiler Related with compiler/compile-time
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant