sympy/csympy — pull request #225 by certik (2014-06-30 17:46:47+00:00)
Title: Use std::atomic for hash_

The `hash_` is only used in the method `Basic::hash()` in `basic-inl.h`:
```c++
inline std::size_t Basic::hash() const
{
if (hash_ == 0)
hash_ = __hash__();
return hash_;
}
```
The `__hash__()` always returns the same value in all threads. However, strictly speaking, we are writing to a variable in some threads and reading from others, so this behavior is undefined by C++11. This PR fixes it, but unfortunately it slows things down.

Compare master with `WITH_CSYMPY_THREAD_SAFE=OFF`:
```
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1037ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1059ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1055ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1058ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1069ms
number of terms: 6272
```
to master with `WITH_CSYMPY_THREAD_SAFE=ON`:
```
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1390ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1391ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1407ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1394ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(master)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1395ms
number of terms: 6272
```
and this PR with `WITH_CSYMPY_THREAD_SAFE=ON`:
```
ondrej@kittiwake:~/repos/csympy/benchmarks(hash_thread)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1670ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(hash_thread)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1703ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(hash_thread)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1675ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(hash_thread)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1684ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(hash_thread)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1689ms
number of terms: 6272
ondrej@kittiwake:~/repos/csympy/benchmarks(hash_thread)$ ./expand2
Expanding: ((y + x + z + w)^15 + w)*(y + x + z + w)^15
1677ms
number of terms: 6272
```
These results are with g++ 4.6.3. I think the timings improve significantly with newer compiler versions. But I still don't like the slowdown.

I wonder if there is a better way around this.