You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
2.5 KiB

  1. #include "measure_time.h"
  2. #include <atomic>
  3. #include <cstdint>
  4. #include <thread>
  5. #include <ext/concurrence.h>
  6. #define COUNT (1u << 24)
  7. // use "incl" for 32-bit and "incq" for 64-bit in asm below:
  8. // use 32-bit for now: _Atomic_word should be 32-bit too;
  9. // 64-bit is basically as fast as 32-bit if it fits in your cache
  10. typedef std::uint32_t counting;
  11. volatile counting __attribute__ ((aligned (64))) volatile_counter;
  12. counting __attribute__ ((aligned (64))) simple_counter;
  13. std::atomic<counting> __attribute__ ((aligned (64))) atomic_counter;
  14. _Atomic_word __attribute__ ((aligned (64))) gxx_counter;
  15. void benchmark_volatile() {
  16. // should be the same as asm below
  17. volatile_counter = 0;
  18. for (size_t i = 0; i < COUNT; ++i) {
  19. ++volatile_counter;
  20. }
  21. }
  22. void benchmark_asm() {
  23. simple_counter = 0;
  24. for (size_t i = 0; i < COUNT; ++i) {
  25. asm volatile(
  26. "incl %0;"
  27. : "+m"(simple_counter)
  28. );
  29. }
  30. }
  31. void benchmark_local_atomic() {
  32. // compiler doesn't realize no one else can read this counter and
  33. // uses real atomic (i.e. "lock")
  34. std::atomic<counting> local_atomic_counter;
  35. local_atomic_counter = 0;
  36. for (size_t i = 0; i < COUNT; ++i) {
  37. ++local_atomic_counter;
  38. }
  39. }
  40. void benchmark_atomic() {
  41. // should be the same as "lock_asm"
  42. atomic_counter = 0;
  43. for (size_t i = 0; i < COUNT; ++i) {
  44. ++atomic_counter;
  45. }
  46. }
  47. void benchmark_lock_asm() {
  48. simple_counter = 0;
  49. for (size_t i = 0; i < COUNT; ++i) {
  50. asm volatile(
  51. "lock incl %0;"
  52. : "+m"(simple_counter)
  53. );
  54. }
  55. }
  56. void benchmark_gxx_atomic() {
  57. // if linked with pthread this will use "lock" same as atomic, otherwise
  58. // it uses a simple non-volatile non-atomic add instruction
  59. // https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_concurrency.html
  60. // claims __atomic_add_dispatch uses a volatile counter, but this isn't
  61. // true in gcc 4.9
  62. gxx_counter = 0;
  63. for (size_t i = 0; i < COUNT; ++i) {
  64. __gnu_cxx::__atomic_add_dispatch(&gxx_counter, 1);
  65. }
  66. }
  67. int main() {
  68. // std::thread fake([] { return; });
  69. // fake.join();
  70. std::cout << "volatile counter : ";
  71. measure_time_with_dry_run(benchmark_volatile);
  72. std::cout << "asm counter : ";
  73. measure_time_with_dry_run(benchmark_asm);
  74. std::cout << "local atomic counter: ";
  75. measure_time_with_dry_run(benchmark_local_atomic);
  76. std::cout << "atomic counter : ";
  77. measure_time_with_dry_run(benchmark_atomic);
  78. std::cout << "lock asm counter : ";
  79. measure_time_with_dry_run(benchmark_lock_asm);
  80. std::cout << "gxx atomic counter : ";
  81. measure_time_with_dry_run(benchmark_gxx_atomic);
  82. }