I think it is much worse than that. The C# code can chose the table
size for good performance (and I can't imagine it would not). In the
C version, the table was chosen to be as bad as it could be without
failing (i.e. exactly the same size as the number of numbers being put
into the set).
That is not the case, and part of the problem is that you think so
normatively. In a toxic and pseudo-scientific corporate environment,
where (in Adorno's words) "all scientists are candidate for posts"
there's no real problem solving, just a continual (and futile) effort
to establish and destroy credibility.
You are correct in saying that the C version becomes very slow as the
number of table entries approaches the size of the table. I've already
noted that this is a problem, and how to solve it by using a linked
list originating at each entry.
However, the two programs demonstrate my point. C more or less forces
the C program to decide on the maximum table size, therefore the test,
which filled up the table, was realistic, since tables often fill up
in production.
Whereas in a completely encapsulated way the C Sharp program either
preallocated more than enough storage or automatically expanded the
table. We don't know what it did because we do not need to know. In
production, the only responsibility of the user of hashset is to but
additions in try..catch error handling.
Giving C this type of flexibility would involve a lot of extra coding,
or visiting some creepy site.
If it becomes necessary to delete from the C hash table, and the basic
"search forward to available entry" method actually implemented is in
use, a special value is needed to mark deleted entries, since entries
that match the hash code need to be retrieved past the deleted entry.
If linked lists are used, they need to be searched to find the entry
to be deleted. Whereas in the C Sharp code, deletion is one line of
additional code!
Therefore, the benchmark numbers are realistic. A simple C hash table,
implemented in the simplest fashion without a link list, will probably
run much slower than a C Sharp hash table user application with much
more coding effort. It will be much harder to maintain.
C sucks, and it's not "efficient" in practise.