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

DokanFileInfo should it really be a class? #293

Open
TrabacchinLuigi opened this issue Mar 5, 2022 · 17 comments
Open

DokanFileInfo should it really be a class? #293

TrabacchinLuigi opened this issue Mar 5, 2022 · 17 comments

Comments

@TrabacchinLuigi
Copy link
Contributor

in dokan net DokanFileInfo is a class which makes me think it lives in the memoryHeap, but maybe it lives in the stack, so it would make more sense if it was a struct, and we could decide to pass by reference or copy it around...
This is again a question because i'm not really sure how instances are marshalled between the kernel and the userland

@LTRData
Copy link
Contributor

LTRData commented Mar 5, 2022

It gives quite a bit of performance gain to make it a struct, particularly in use cases with lots of file I/O. In my "high-performance" fork I have done exactly that and also changed most list buffers to enumerating results etc. But it involves several breaking changes and I would say that it is difficult to implement at this time in Dokan.net.

Link to the fork, if it sounds interesting:
https://github.com/LTRData/dokan-dotnet

I think though that some other things from this fork can easily be migrated back into the original dokan.net, for example the way it uses Span<byte> to avoid temporary byte arrays for Stream backends in .NET Core etc. (But I will make a separate post about that in some future.)

@TrabacchinLuigi
Copy link
Contributor Author

that is quite intresting, as we are observing a lot of gc pressure using dokan

@TrabacchinLuigi
Copy link
Contributor Author

i was looking at your fork, and i was thinking about why not using the in keywork instead of ref, it should be "safer", also i would pass all the other params with the in keyword

@LTRData
Copy link
Contributor

LTRData commented Mar 5, 2022

i was looking at your fork, and i was thinking about why not using the in keywork instead of ref, it should be "safer", also i would pass all the other params with the in keyword

The in keyword is really only relevant for value types, so it would only be confusing to use it for other parameters.

@TrabacchinLuigi
Copy link
Contributor Author

what about removing the object from the dokanfileinfo and adding an out parameter in createfile ? that would make sense, except for the added api change

@LTRData
Copy link
Contributor

LTRData commented Mar 5, 2022

what about removing the object from the dokanfileinfo and adding an out parameter in createfile ? that would make sense, except for the added api change

Not sure I understand, the object needs to be marshalled by the DokanFileInfo.Context property logic and the GC pointer needs to be stored in a field in DokanFileInfo, so I think it makes sense to keep it a property in DokanFileInfo. Or did you mean something else?

@TrabacchinLuigi
Copy link
Contributor Author

doesn't matter i was proposing a change in dokany

@Liryna
Copy link
Member

Liryna commented Mar 7, 2022

I am fine with performance improvement that breaks the compatibility 👍

@TrabacchinLuigi
Copy link
Contributor Author

TrabacchinLuigi commented Mar 13, 2022

another change we could try out to increment performance and reduce marshalling is create two structures for each method, instead of having let's say

public delegate NtStatus ZwCreateFileDelegate(
          [MarshalAs(UnmanagedType.LPWStr)] string rawFileName,
          IntPtr securityContext,
          uint rawDesiredAccess,
          uint rawFileAttributes,
          uint rawShareAccess,
          uint rawCreateDisposition,
          uint rawCreateOptions,
          [MarshalAs(UnmanagedType.LPStruct), In, Out] DokanFileInfo dokanFileInfo);

we could have

public delegate CreateFileResult CreateFileDelegate(in CreateFileInput input)
this would also increase readability
@LTRData you seem quite expert about those kind of changes

@LTRData
Copy link
Contributor

LTRData commented Mar 13, 2022

The parameters to ZwCreateFile etc need to be the way they are to maintain compatibility with the C++ dll. Also, it would not give any particular performance gain to wrap it in a structure either because it just moves necessary marshalling to another place, when marshalling the structure instead.

The only thing that really needs marshalling in my fork with struct version of DokanFileInfo is the string with the file name and there is not much we can do about that. Everything else is just mapped values.

@TrabacchinLuigi
Copy link
Contributor Author

i was proposing changin the C++ dll aswell

@LTRData
Copy link
Contributor

LTRData commented Mar 13, 2022

Okay, but really, there is really no performance difference between marshalling fields in a structure compared to parameters. It is basically the same thing.

@Liryna
Copy link
Member

Liryna commented Mar 14, 2022

@TrabacchinLuigi the V2 is a partial merge of a pr that is still opened on dokany repository and it is addressing this of have a single struct pointer that has all of the parameters in it.
I must they that I really liked the idea but in reality have all the wrappers and existing implementation to migrate to it is ... difficult. Like I am afraid the just loose people.

Regarding marshalling that another subject :D

@LTRData
Copy link
Contributor

LTRData commented Mar 14, 2022

If we are talking about native C++, in my opinion the idea of allocating a struct on stack and send a pointer to it in function calls made a lot of sense on x86 architecture. However, on x64 architecture parameters are passed in CPU registers and do not require stack writes or reads, but a struct would need stack writes and then sending a pointer to that struct in a CPU register and the called functions need stack reads to get the values instead of having them in registers directly from start. The performance difference is extremely small, but in most cases it gives better performance to send parameters directly instead of wrapping them in a struct and send a pointer to the struct.

Then on to managed code and marshalling, any needed marshalling is basically the same for fields in structs and for parameters so there is no real performance gain for using one over another.

@TrabacchinLuigi
Copy link
Contributor Author

So: + readability, -lot of work (double minus because i won't have much allocated time), -users would need to migrate projects. -would probably conflict with some already started work.
I think i'll skip that. but thanks to have took the time to reason about it with me

@Liryna
Copy link
Member

Liryna commented Mar 14, 2022

FYI DokanFileInfo is since v2 part of DOKAN_IO_EVENT which is allocated / pop from the memory pool. One per thread.
https://github.com/dokan-dev/dokany/blob/master/dokan/cleanup.c#L43
https://github.com/dokan-dev/dokany/blob/master/dokan/dokan.c#L800

@LTRData
Copy link
Contributor

LTRData commented Mar 14, 2022

That is a really nice solution. If it is turned into a blittable struct in the .NET world it can be immediately mapped as a .NET struct without any marshalling needed. It is typically the fastest and most efficient that can be done when calling managed code from unmanaged etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants