1

I am working on a C# library that will create tons of small objects as part of normal operation (think AST nodes in a compiler). I'd like to assign each instance a unique identifier (unique within the program, not globally unique) in a way that won't hinder performance. The objects are immutable, and can be shared across threads, so the id needs to be unique across threads.

Here are some options I'm considering:

  1. Use a static int or long, and get each new id via calls to Interlocked.Increment()
  2. Use Guid.NewGuid() to generate ids
  3. Use a static int or long field with the [ThreadStatic] attribute, and then make a string id from the current thread's ManagedThreadId property and the next value from the thread-local counter.

Will one of these be more performant than the others? Is there a better way to do this?

Thanks!

EDIT:
I ran a quick benchmark and got the following results:

  1. 0.19 seconds (int vs. long were basically the same)
  2. 1.1 seconds
  3. 3.5 seconds

This seems to point strongly towards using Interlocked. However, is there any risk of Interlocked slowing down in multithreaded scenarios?

Guvante
  • 18,775
  • 1
  • 33
  • 64
ChaseMedallion
  • 20,860
  • 17
  • 88
  • 152
  • Your (1) is by far the best way for your stated problem, in my opinion. Interlocked.Increment() is superfast. As long as you don't generate more than 2^31 of them. ;) – Matthew Watson Feb 23 '13 at 19:25
  • I've always used CombGuids - they are based on the current date/time so unless your code is producing thousands of id's every second - it's almost impossible to get a conflict. See this post for the code. http://stackoverflow.com/questions/665417/sequential-guid-in-linq-to-sql/2187898#2187898 – Jay Feb 23 '13 at 19:26
  • @Jay: Hmm that's a lot of overhead for something that only needs to be unique for a particular run on one program. – Matthew Watson Feb 23 '13 at 19:28
  • option 1. Seeing as you don't need global, guid is a waste of resources – Tony Hopkinson Feb 23 '13 at 19:34
  • @MatthewWatson - It looks slow, but it's not that bad. On my machine it takes 0.1 of a second to generate a million guids, and 1 second to generate 1 million CombGuids. So it's about 10 times slower than generating a Guid, but it is useful because they are almost certainly unique. Just throwing it out there as an option! – Jay Feb 23 '13 at 19:34
  • You're (very) prematurely attempting to optimize. First, work out if this is even the area where performance matters. – Damien_The_Unbeliever Feb 23 '13 at 19:40
  • @Jay Why would anybody choose 10 times slower for "almost certain" uniqueness when they could get 10 times faster with guaranteed uniqueness? – Raymond Chen Feb 23 '13 at 19:41
  • @OP: There is no risk whatsoever of Interlocked.Increment() slowing down. It's pretty much designed to do the job as fast as possible (to the extent that it is often implemented using special processor instructions that exist solely for doing an interlocked increment at least in the Windows API/C++ world). I'm guessing the JIT may do something similar. Even if it doesn't, it's still extremely fast! – Matthew Watson Feb 23 '13 at 19:43
  • @RaymondChen - only benchmarked after first posting. I guess its a good way of helping to discount both guids and combguids as the op wants speed. – Jay Feb 23 '13 at 20:03

1 Answers1

2

Interlocked.Increment works by ensuring that the call to ++ happens as an atomic operation. That means that there is no way for another thread to get the intermediate variable, which could happen using ++.

The only performance issue is the need to synchronize the memory that the variable is stored in, note that this performance cost should be negligible, as your testing seems to show.

Guvante
  • 18,775
  • 1
  • 33
  • 64