-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Conversation
{ | ||
charsWritten = 0; | ||
|
||
var tmpPath = Path.GetTempPath(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i want to avoid this allocation if destination < tmpPath.Length
, on Windows i could use ValueStringBuilder
but on unix side we don't have
private static void GetTempPath(ref ValueStringBuilder builder)
on Path.Unix.cs
there is
public static string GetTempPath()
with Environment.GetEnvironmentVariable(TempEnvVar)
string allocation.
is there private static string GetEnvironmentVariableCoreHelper(string variable, Span<char> buffer)
for unix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there private static string GetEnvironmentVariableCoreHelper(string variable, Span buffer) for unix?
As part of having this we should write the lower level pieces we need. :) I'd make an internal one that takes a ValueStringBuilder. I'm looking into what it would take to polish up that class and make it public in the future, btw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JeremyKuhne @Anipik i get the point, can you help me to find where Environment.GetEnvironmentVariable()
pinvoke for unix are defined?If i follow code with VS i reach Win32Native only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case the actual P/Invoke is provided by the PAL. So the same code is used in both places. The cross-plat model the native parts of the runtime uses is that it has Unix implementations for all of the Win32 APIs. It simply includes the PAL when building for Unix instead of the Windows headers/libs. In the managed code this abstraction is also leveraged. Anything in Win32Native
has a Unix implementation. The source file that is included when building for Unix is coreclr\src\mscorlib\src\Interop\Unix\Interop.Libraries.cs
.
As such, you can use this straight-up:
internal static unsafe int GetEnvironmentVariable(string lpName, Span<char> lpValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TYVM
public static bool TryGetTempPath(Span<char> destination, out int charsWritten) | ||
{ | ||
charsWritten = 0; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extraLine
return false; | ||
|
||
tmpPath.AsSpan().CopyTo(destination); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extraLine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Anipik is there a guideline for "extraLine"?
tmpPath.AsSpan().CopyTo(destination); | ||
|
||
charsWritten = tmpPath.Length; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extraline
{ | ||
charsWritten = 0; | ||
|
||
var tmpPath = Path.GetTempPath(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use string
instead var
keyword. we use var
if we can know the type of the object from RHS
@JeremyKuhne Do we have examples of where the no-allocation version of this API makes a difference? You are about to create a temporary file. Creating a temporary file is orders of magnitude more expensive operation than just allocating a string. I think the no-allocation version of these APIs are adding negative value to the platform. |
We are stabilizing for .NET Core 2.1. New API additions need to wait once we start working for next version. |
Yes, anywhere you need to build paths based off of the temporary folder location. While it isn't common in all applications, it is very common in some (build systems, designers, etc.). |
If somebody calls this on a hot path, they should just call the method once and cache the value. It is going to be much more efficient for them than just them calling this API again and again. |
Sure, for those that have the ability to do so. While it is unlikely, this value can change. If you want to be strictly correct you shouldn't cache it. That is why I didn't cache the existing overload- although I considered it. :) |
|
||
// Get the temp path from the TMPDIR environment variable. | ||
int requiredSize = 0; | ||
while ((requiredSize = Microsoft.Win32.Win32Native.GetEnvironmentVariable(TempEnvVar, builder.Span)) > builder.Capacity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JeremyKuhne @Anipik to avoid allocation of string i need to use GetEnvironmentVariable(string lpName, Span<char> lpValue)
. I've exposed internal span and i'm pretty sure you don't want this. Can you help me with overload for GetEnvironmentVariable(string lpName, ref char lpTempFileName, int size)
or GetEnvironmentVariable(string lpName, ValueStringBuilder builder)
i don't find a way to cast ref char
to char*
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to use fixed
to get the address (char*
) of a ref char. If you follow the code down you'll see:
[DllImport(Interop.Libraries.Kernel32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
private static extern unsafe int GetEnvironmentVariable(string lpName, char* lpValue, int size);
internal static unsafe int GetEnvironmentVariable(string lpName, Span<char> lpValue)
{
fixed (char* lpValuePtr = &MemoryMarshal.GetReference(lpValue))
{
return GetEnvironmentVariable(lpName, lpValuePtr, lpValue.Length);
}
}
What you should really do here is rewrite GetEnvironmentVariableCoreHelper
to take a ValueStringBuilder. Take a look at the changes I made in dotnet/corefx@050bc33 for examples.
char*
is equivalent to ref char
in P\Invoke signatures. As such, the helper in Win32Native can just be removed and the Pinvoke can be changed to take a ref char
as everything is calling it with a Span
. You can avoid having to use the fixed statement that way. (Note that there is currently an issue in CoreRT that requires manually using fixed- if you look at P\Invokes in the shared folder you'll see #ifdef
s that do it both ways.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok TYVM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will have issues with making the same code build with CoreRT. The environment stuff is factored differently there. We may need a (public) no-allocation API to read the environment to make this work well. No-allocation environment access is likely a lot more useful than no-allocation temporary path generator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will have issues with making the same code build with CoreRT.
Ah yes, I forgot that while the P/Invoke isn't shared, this is. We can do the same hack on the P/Invoke there that I mentioned above no? (e.g. ifdef and manually pin the ref char)
We may need a (public) no-allocation API to read the environment to make this work well.
There isn't a need for it to be public to unblock this, is there? I agree that we should have a public API for it eventually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be using the same patterns internally as we are using for our public APIs. If the Try*
pattern that returns bool and Span does not work for out internal use, it is unlikely going to work well for our users as well. This suggests that the pattern we are using for the public API is wrong.
This applies to both GetEnvironmentVariable or GetTempPath APIs.
I think we need to step back and do work on the API design. We may need to expose the ValueStringBuilder as public to make this work well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JeremyKuhne i go on with above idea(paying attention to corert) right? If i success with this could help also on new environment api.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry @jkotas i've got a delay on refresh messages, i read your comment now. I wait your decision to understand how go on!I stay here and "listen". IMHO public ValueStringBuilder will be very useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jkotas I'll open an issue on ValueStringBuilder and link to it. While I think we definitely need VSB type APIs, I'm not confident that not having the existing Try*
and Span
pattern becomes moot with said APIs. What if you want to write to fixed size span and don't want it grabbing from array pool? Does it make sense to have an option on ValueStringBuilder that doesn't allow it to grow? I'll discuss it in the issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -34,6 +34,8 @@ public int Length | |||
|
|||
public int Capacity => _chars.Length; | |||
|
|||
public Span<char> Span => _chars; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have AsSpan() that you should use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need a Span not a ReadOnlySpan to pass to api, right?or you mean create a custom modifiable Span and after Append?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want the ref char (which you do), you use GetPinnableReference()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok TYVM
@JeremyKuhne @jkotas should i go on with this PR or |
|
@JeremyKuhne ok i'll work on that next days while waiting!Feel free to assig to me if you like! https://github.com/dotnet/corefx/issues/28256 |
@JeremyKuhne any news on this? |
@MarcoRossignoli Sorry, with the 2.1 release, 3.0 disclosure/planning, and Build I haven't been able to get a lot of traction yet. Thank you so much for your patience. I need to spend some time chatting with @terrajobst about what we do here and perhaps revisit in API review again. It isn't clear that we can or should use BufferReader/Writer for these. ValueStringBuilder is a much lighter-weight solution, but, on the flip side, it is still another class. |
no problem @JeremyKuhne i didn't want to put more pressure, this is only a light ping 😄!For now i'm working on other PR!Thank you very much for you fast reply! |
Reviewing these APIs again. This one in particular we might drop as the end user can potentially cache. It isn't strictly correct to cache, however, as the temp directory can be changed at any time (but would be colossally stupid to do so). |
Thanks for your efforts here, @MarcoRossignoli. :) |
Closes coreclr part of https://github.com/dotnet/corefx/issues/28255
There is a test ready to push here , tested on Windows and Ubuntu.
cc: @JeremyKuhne @Anipik