32

I've seen several suggestions on naming files randomly, including using

System.IO.Path.GetRandomFileName()

or using a

System.Guid

and appending a file extension.

My question is: What is the fastest way to generate a unique filename?

Rex M
  • 142,167
  • 33
  • 283
  • 313
Dan Esparza
  • 28,047
  • 29
  • 99
  • 127
  • 39
    Why don't you just use a way that's *fast enough*? – Joren Oct 21 '09 at 18:14
  • 1
    An excellent question. The answer is that I'm trying to squeeze every ounce of performance out of this file system intensive application. – Dan Esparza Oct 21 '09 at 18:17
  • 4
    This really depends on what you mean by "unique" - is it universally unique, unique within a filesystem, unique among different threads in the same program...? – Anders Lindahl Oct 21 '09 at 18:17
  • 1
    @Anders: Good point. I have a folder path already -- so I mean 'unique within a given file folder' – Dan Esparza Oct 21 '09 at 18:18
  • I use the GUID method when generating names for screenshots capturing errors found by test automation. It's an easy fire-and-forget system. – Lee Oct 21 '09 at 18:31
  • 1
    I suggest you measure the effective (in your app, not for(int i=0; i<1000000; i++) { string s = GetFileName(); } ) performance benefits of each of these suggestions. I'll bet you find that the cost of your file system operations completely swamps the differences. – Michael Petrotta Oct 21 '09 at 18:44
  • 17
    You almost certainly don't care. Assuming you're going to do any IO on that random filename, that is going to dwarf the time taken to make the filename. Concentrate optimisations on places where it will make a difference. – MarkR Oct 21 '09 at 18:58

8 Answers8

39

A GUID would be extremely fast, since it's implementation guarantees Windows can generate at least 16,384 GUIDS in a 100-nanosecond timespan. (As others pointed out, the spec doesn't guarantee, only allows for. However, GUID generation is really, really fast. Really.) The likelihood of collision on any filesystem anywhere on any network is very low. It's safe enough that although it'd be best practice to always check to see if that filename is available anyway, in reality you would never even need to do that.

So you're looking at no I/O operations except the save itself, and <0.2 milliseconds (on a test machine) to generate the name itself. Pretty fast.

Rex M
  • 142,167
  • 33
  • 283
  • 313
  • 2
    WOW! Do you have a link source that describes this implementation guarantee? – Dan Esparza Oct 21 '09 at 18:19
  • 7
    I don't believe Windows gaurantees that it can **generate** 16,384 GUIDS in 100 nS - only that the values produced by the GUID algorithm are gauranteed to have that level of distribution. Besides, using logic, 16k GUIDS per 100-Ns would be 163 billion GUIDS per second ... which means it takes less than 1/10the of a CPU cycle to produce a GUID? That doesn't much make sense. – LBushkin Oct 21 '09 at 18:23
  • see this question about GUID uniqueness http://stackoverflow.com/questions/39771/is-a-guid-unique-100-of-the-time – BlackTigerX Oct 21 '09 at 18:24
  • This is obviously a theoretical value which guarantees uniqueness of up to 16K Guids per 100 ns. The actual speed cannot be guaranteed by microsoft, since it depends (among other things) on your cpu speed which does not com close to the theoretical limit as pointed out be LBushkin – Manu Oct 21 '09 at 18:28
  • 2
    @Dan Raymond Chen breaks down the component parts of a GUID (it's not just a random number) here: http://blogs.msdn.com/oldnewthing/archive/2008/06/27/8659071.aspx note the 14-bit uniquifier, which ensures GUIDs generated in the same 100-nanosecond timespan are still one-in-2^14 unique. – Rex M Oct 21 '09 at 18:32
  • 2
    @LBushkin you're right, that was a poor reading of the spec. However, I stand by the assertion that generating GUIDs is so stupid fast you don't need to bother measuring it. – Rex M Oct 21 '09 at 18:37
  • @Rex M: To measure is to know. – LBushkin Oct 21 '09 at 18:42
17

You want System.IO.Path.GetTempFileName()

I can't actually say whether it's fastest or not, but it's the right way to do this, which is more important.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 36
    That actually creates a file. I think you mean System.IO.Path.GetRandomFileName() – Dan Esparza Oct 21 '09 at 18:46
  • 1
    Dan is correct. It creates a file in the Windows temp directory. – adrianbanks Oct 21 '09 at 18:47
  • 1
    It does create a file, but I stand by the answer because it will save you having to create it yourself or test for existence/collisions yourself later. And why create the file name if you're not going to use it? – Joel Coehoorn Oct 21 '09 at 19:03
  • 10
    Three problems with this: 1) No control over the file extension it's always .TMP, 2) No control over the directory where the temp file is created, it's always in TEMP, 3) It uses P/Invoke to have the OS produce the temp file so it's almost certainly not the *fastest* approach, which is what the OP seems to be looking for. – LBushkin Oct 21 '09 at 19:06
  • 2
    meh: control over the name or the path weren't part of the requirements. And who says P/Invoke is necessarily slow? – Joel Coehoorn Oct 21 '09 at 20:34
  • 4
    For anyone who uses GetTempFileName (Either in System.IO.Path or the Win32 call) You may want to read the following article to understand weaknesses with this system: http://dotnet.org.za/markn/archive/2006/04/15/51594.aspx Also, it's likely not the fastest way. Extern calls *are* slow due to the need for marshalling. Making a new Guid involves an internal call to the runtime. I imagine that just passes the call on to the Win32 API for the same (CoCreateGuid or UUIDCreate)... – Troy Howard Oct 22 '09 at 16:15
  • 2
    Be careful that `GetRandomFileName()` might create files with extensions that you might not be able to delete. For example, I have experienced issues with files with extension "msp" which I could not delete once I did not need the temp file anymore. – Stefano Ricciardi Mar 08 '13 at 08:51
  • 1
    Fourth problem with this: If your app or other app leakes 65k files created GetTempFileName(), then it will throw `System.IO.IOException: The file exists`, See https://stackoverflow.com/questions/18350699/system-io-ioexception-the-file-exists-when-using-system-io-path-gettempfilena/18350893 – Gerardo Grignoli May 31 '18 at 18:11
7

Well I have been writing file system drivers for 20 years and would say Rex is correct. Generating a guid is much much faster, since it requires far less overhead than searching for a unique file name. GetTempFileName actually creates a file, which means it has to call through the entire file system driver stack (who knows how many calls that would be and a switch into kernel mode.) GetRandomFileName sounds like it is faster, however I would trust the GUID call to be even faster. What people don't realize is that even testing for the existence of a file requires a complete call through the driver stack. It actually results in an open, get attributes and close (which is at least 3 calls, depending on the level.) In reality it is a minimum of 20 function calls and a transition to kernel mode. GUIDS guarentee of uniqueness is good enough for most purposes.

My recommendation is to generate the name and create the file only if it doesn't exist. If it does, throw an exception and catch it, then generate a new guid and try again. That way, you have zero chance of errors and can sleep easy at night.

On a side note, checking for errors is so overdone. Code should be designed to crash if assumptions are wrong, or catch exceptions and deal with it then. Its much faster to push and pop and address on the exception stack, than to check everytime on every function for an error.

Jordan
  • 453
  • 4
  • 6
4

If you control the destination where the files will be located, and there is only one process and thread that writes to it, just append some auto-incrementing number to a base name.

If you don't control the destination, or need a multithreaded implementation, use a GUID.

LBushkin
  • 129,300
  • 32
  • 216
  • 265
  • 1
    While I almost always use GUID for this problem, LBushkin is right about performance. If you really want *every* drop of perf, incrementing an integer and converting it to a string is faster than generating a Guid and converting it to a string... But will only work if you have control of the directory. – Troy Howard Oct 21 '09 at 19:02
1

Use an Int and increment it for each file.

Tom B
  • 2,160
  • 2
  • 13
  • 15
1

If you control the directory, you could name your file based on the lastWriteTime:

DirectoryInfo info = new DirectoryInfo(directoryPath);
long uniqueKey = info.LastWriteTime.Ticks+1L;
string filename = String.Format("file{0}.txt", key);

But you have to check performances of this code: I guess building a DirectoryInfo does not come for free.

Stéphane
  • 19
  • 1
  • "building a DirectoryInfo does not come for free" - I suspect you're correct, especially if the drive / directory is not local to the computer (network drive / NAS / etc) – Dan Esparza Mar 23 '11 at 18:07
0

I hope this self iterative function will help someone to generate a unique filename.

public string getUniqueFileName(int i, string fullpath, string filename)
{
    string lstDir = fullpath.Substring(0, fullpath.LastIndexOf('\\'));
    string name = Path.GetFileName(fullpath);
    string path = fullpath;

    if (name != filename)
        path = Path.Combine(lstDir, filename);

    if (System.IO.File.Exists(path))
    {
        string ext = Path.GetExtension(name);
        name = Path.GetFileNameWithoutExtension(name);                
        i++;
        filename = getUniqueFileName(i, fullpath, name + "_" + i + ext);
    }

    return filename;
}
Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Bikuz
  • 36
  • 3
  • Wouldn't this create names of form `file_1_2_3`? – user5226582 Mar 02 '18 at 11:39
  • While this generates a unique filename in the path (although it's not thread safe) it will be very slow compared to other methods that just simply generates a unique filename without checking if it's already exists. I suppose the op needs a fast generation of a unique filename. Btw your answer solves the problem and it could be helpful to others. – Perrier Mar 02 '18 at 11:51
  • 1
    yes, it does. My expectation was to make increment on same number but it became concatenate. Need to modify code to make it work as expected. Would be thankful if someone correct my code. Thanks in advance. – Bikuz Mar 02 '18 at 11:52
0

You can do something like:

file.MoveTo(deletedfilesdir + @"\" + f.Name + **DateTime.Now.ToFileTimeUtc()** + f.Extension);
Gauravsa
  • 6,330
  • 2
  • 21
  • 30
Haroon
  • 3,402
  • 6
  • 43
  • 74