105_threading2.zig 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. //
  2. // Now that we are familiar with the principles of multi-threading,
  3. // let's boldly venture into a practical example from mathematics.
  4. // We will determine the circle number PI with sufficient accuracy.
  5. //
  6. // There are different methods for this, and some of them are several
  7. // hundred years old. For us, the dusty procedures are surprisingly well
  8. // suited to our exercise. Because the mathematicians of the time didn't
  9. // have fancy computers with which we can calculate something like this
  10. // in seconds today.
  11. // Whereby, of course, it depends on the accuracy, i.e. how many digits
  12. // after the decimal point we are interested in.
  13. // But these old procedures can still be tackled with paper and pencil,
  14. // which is why they are easier for us to understand.
  15. // At least for me. ;-)
  16. //
  17. // So let's take a mental leap back a few years.
  18. // Around 1672 (if you want to know and read about it in detail, you can
  19. // do so on Wikipedia, for example), various mathematicians once again
  20. // discovered a method of approaching the circle number PI.
  21. // There were the Scottish mathematician Gregory and the German
  22. // mathematician Leibniz, and even a few hundred years earlier the Indian
  23. // mathematician Madhava. All of them independently developed the same
  24. // formula, which was published by Leibnitz in 1682 in the journal
  25. // "Acta Eruditorum".
  26. // This is why this method has become known as the "Leibnitz series",
  27. // although the other names are also often used today.
  28. // We will not go into the formula and its derivation in detail, but
  29. // will deal with the series straight away:
  30. //
  31. // 4 4 4 4 4
  32. // PI = --- - --- + --- - --- + --- ...
  33. // 1 3 5 7 9
  34. //
  35. // As you can clearly see, the series starts with the whole number 4 and
  36. // approaches the circle number by subtracting and adding smaller and
  37. // smaller parts of 4. Pretty much everyone has learned PI = 3.14 at school,
  38. // but very few people remember other digits, and this is rarely necessary
  39. // in practice. Because either you don't need the precision, or you use a
  40. // calculator in which the number is stored as a very precise constant.
  41. // But at some point this constant was calculated and we are doing the same
  42. // now.The question at this point is, how many partial values do we have
  43. // to calculate for which accuracy?
  44. //
  45. // The answer is chewing, to get 8 digits after the decimal point we need
  46. // 1,000,000,000 partial values. And for each additional digit we have to
  47. // add a zero.
  48. // Even fast computers - and I mean really fast computers - get a bit warmer
  49. // on the CPU when it comes to really many digits. But the 8 digits are
  50. // enough for us for now, because we want to understand the principle and
  51. // nothing more, right?
  52. //
  53. // As we have already discovered, the Leibnitz series is a series with a
  54. // fixed distance of 2 between the individual partial values. This makes
  55. // it easy to apply a simple loop to it, because if we start with n = 1
  56. // (which is not necessarily useful now) we always have to add 2 in each
  57. // round.
  58. // But wait! The partial values are alternately added and subtracted.
  59. // This could also be achieved with one loop, but not very elegantly.
  60. // It also makes sense to split this between two CPUs, one calculates
  61. // the positive values and the other the negative values. And so we can
  62. // simply start two threads and add everything up at the end and we're
  63. // done.
  64. // We just have to remember that if only the positive or negative values
  65. // are calculated, the distances are twice as large, i.e. 4.
  66. //
  67. // So that the whole thing has a real learning effect, the first thread
  68. // call is specified and you have to make the second.
  69. // But don't worry, it will work out. :-)
  70. //
  71. const std = @import("std");
  72. pub fn main() !void {
  73. const count = 1_000_000_000;
  74. var pi_plus: f64 = 0;
  75. var pi_minus: f64 = 0;
  76. {
  77. // First thread to calculate the plus numbers.
  78. const handle1 = try std.Thread.spawn(.{}, thread_pi, .{ &pi_plus, 5, count });
  79. defer handle1.join();
  80. // Second thread to calculate the minus numbers.
  81. ???
  82. }
  83. // Here we add up the results.
  84. std.debug.print("PI ≈ {d:.8}\n", .{4 + pi_plus - pi_minus});
  85. }
  86. fn thread_pi(pi: *f64, begin: u64, end: u64) !void {
  87. var n: u64 = begin;
  88. while (n < end) : (n += 4) {
  89. pi.* += 4 / @as(f64, @floatFromInt(n));
  90. }
  91. }
  92. // If you wish, you can increase the number of loop passes, which
  93. // improves the number of digits.
  94. //
  95. // But be careful:
  96. // In order for parallel processing to really show its strengths,
  97. // the compiler must be given the "-O ReleaseFast" flag when it
  98. // is created. Otherwise the debug functions slow down the speed
  99. // to such an extent that seconds become minutes during execution.
  100. //
  101. // And you should remove the formatting restriction in "print",
  102. // otherwise you will not be able to see the additional diggits.