eliminate_runtime_penalty.xml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.1//EN"
  3. "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
  4. <section id="safe_numerics.eliminate_runtime_penalty">
  5. <title>Eliminating Runtime Penalty</title>
  6. <para>Up until now, we've mostly focused on detecting when incorrect results
  7. are produced and handling these occurrences either by throwing an exception
  8. or invoking some designated function. We've achieved our goal of detecting
  9. and handling arithmetically incorrect behavior - but at cost of checking
  10. many arithmetic operations at runtime. It is a fact that many C++
  11. programmers will find this trade-off unacceptable. So the question arises as
  12. to how we might minimize or eliminate this runtime penalty.</para>
  13. <para>The first step is to determine what parts of a program might invoke
  14. exceptions. The following program is similar to previous examples but uses a
  15. special exception policy: <link
  16. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>.</para>
  17. <para><programlisting><xi:include href="../../example/example81.cpp"
  18. parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>Now,
  19. any expression which <emphasis><emphasis
  20. role="bold">might</emphasis></emphasis> fail at runtime is flagged with a
  21. compile time error. There is no longer any need for <code>try/catch</code>
  22. blocks. Since this program does not compile, the <emphasis
  23. role="bold">library absolutely <emphasis role="bold">guarantees that no
  24. arithmetic expression</emphasis> will yield incorrect results</emphasis>.
  25. Furthermore, it is <emphasis role="bold">absolutely guaranteed that no
  26. exception will ever be thrown</emphasis>. This is our original goal.</para>
  27. <para>Now all we need to do is make the program compile. There are a couple
  28. of ways to achieve this.</para>
  29. <section id="safe_numerics.eliminate_runtime_penalty.2">
  30. <title>Using <link linkend="safe_numerics.safe_range">safe_range</link>
  31. and <link linkend="safe_numerics.safe_literal">safe_literal</link></title>
  32. <para>When trying to avoid arithmetic errors of the above type,
  33. programmers will select data types which are wide enough to hold values
  34. large enough to be certain that results won't overflow, but are not so
  35. large as to make the program needlessly inefficient. In the example below,
  36. we presume we know that the values we want to work with fall in the range
  37. [-24,82]. So we "know" the program will always result in a correct result.
  38. But since we trust no one, and since the program could change and the
  39. expressions be replaced with other ones, we'll still use the <link
  40. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
  41. exception policy to verify at compile time that what we "know" to be true
  42. is in fact true.</para>
  43. <programlisting><xi:include href="../../example/example83.cpp"
  44. parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
  45. <para><itemizedlist>
  46. <listitem>
  47. <para><code><code>safe_signed_range</code></code> defines a type
  48. which is limited to the indicated range. Out of range assignments
  49. will be detected at compile time if possible (as in this case) or at
  50. run time if necessary.</para>
  51. </listitem>
  52. <listitem>
  53. <para>A safe range could be defined with the same minimum and
  54. maximum value effectively restricting the type to holding one
  55. specific value. This is what <code>safe_signed_literal</code>
  56. does.</para>
  57. </listitem>
  58. <listitem>
  59. <para>Defining constants with <code>safe_signed_literal</code>
  60. enables the library to correctly anticipate the correct range of the
  61. results of arithmetic expressions at compile time.</para>
  62. </listitem>
  63. <listitem>
  64. <para>The usage of <code><link
  65. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link></code>
  66. will mean that any assignment to z which could be outside its legal
  67. range will result in a compile time error.</para>
  68. </listitem>
  69. <listitem>
  70. <para>All safe integer operations are implemented as constant
  71. expressions. The usage of <code>constexpr</code> will guarantee that
  72. <code>z</code> will be available at compile time for any subsequent
  73. use.</para>
  74. </listitem>
  75. <listitem>
  76. <para>So if this program compiles, it's guaranteed to return a valid
  77. result.</para>
  78. </listitem>
  79. </itemizedlist>The output uses a custom output manipulator,
  80. <code>safe_format</code>, for safe types to display the underlying type
  81. and its range as well as current value. This program produces the
  82. following run time output.</para>
  83. <screen>example 83:
  84. x = &lt;signed char&gt;[10,10] = 10
  85. y = &lt;signed char&gt;[67,67] = 67
  86. x + y = &lt;int&gt;[77,77] = 77
  87. z = &lt;signed char&gt;[-24,82] = 77</screen>
  88. <para>Take note of the various variable types:<itemizedlist>
  89. <listitem>
  90. <para><code>x</code> and <code>y</code> are safe types with fixed
  91. ranges which encompass one single value. They can hold only that
  92. value which they have been assigned at compile time.</para>
  93. </listitem>
  94. <listitem>
  95. <para><code>The sum x + y can also be determined at compile
  96. time.</code></para>
  97. </listitem>
  98. <listitem>
  99. <para>The type of z is defined so that It can hold only values in
  100. the closed range -24,82. We can assign the sum of x + y because it
  101. is in the range that <code>z</code> is guaranteed to hold. If the
  102. sum could not be be guaranteed to fall in the range of
  103. <code>z</code>, we would get a compile time error due to the fact we
  104. are using the <code>loose_trap_policy</code> exception
  105. policy.</para>
  106. </listitem>
  107. </itemizedlist>All this information regarding the range and values of
  108. variables has been determined at compile time. There is no runtime
  109. overhead. The usage of safe types does not alter the calculations or
  110. results in anyway. So <code>safe_t</code> and <code>const_safe_t</code>
  111. could be redefined to <code>int</code> and <code>const int</code>
  112. respectively and the program would operate identically - although it might
  113. We could compile the program for another machine - as is common when
  114. building embedded systems and know (assuming the target machine
  115. architecture was the same as our native one) that no erroneous results
  116. would ever be produced.</para>
  117. </section>
  118. <section id="safe_numerics.eliminate_runtime_penalty.1">
  119. <title>Using Automatic Type Promotion</title>
  120. <para>The C++ standard describes how binary operations on different
  121. integer types are handled. Here is a simplified version of the
  122. rules:</para>
  123. <itemizedlist>
  124. <listitem>
  125. <para>promote any operand smaller than <code>int</code> to an
  126. <code>int</code> or <code>unsigned int</code>.</para>
  127. </listitem>
  128. <listitem>
  129. <para>if the size of the signed operand is larger than the size of the
  130. signed operand, the type of the result will be signed. Otherwise, the
  131. type of the result will be unsigned.</para>
  132. </listitem>
  133. <listitem>
  134. <para>Convert the type each operand to the type of the result,
  135. expanding the size as necessary.</para>
  136. </listitem>
  137. <listitem>
  138. <para>Perform the operation the two resultant operands.</para>
  139. </listitem>
  140. </itemizedlist>
  141. <para>So the type of the result of some binary operation may be different
  142. than the types of either or both of the original operands.</para>
  143. <para>If the values are large, the result can exceed the size that the
  144. resulting integer type can hold. This is what we call "overflow". The
  145. C/C++ standard characterizes this as undefined behavior and leaves to
  146. compiler implementors the decision as to how such a situation will be
  147. handled. Usually, this means just truncating the result to fit into the
  148. result type - which sometimes will make the result arithmetically
  149. incorrect. However, depending on the compiler and compile time switch
  150. settings, such cases may result in some sort of run time exception or
  151. silently producing some arbitrary result.</para>
  152. <para>The complete signature for a safe integer type is:</para>
  153. <para><programlisting>template &lt;
  154. class T, // underlying integer type
  155. class P = native, // type promotion policy class
  156. class E = default_exception_policy // error handling policy class
  157. &gt;
  158. safe;
  159. </programlisting></para>
  160. <para>The promotion rules for arithmetic operations are implemented in the
  161. default <code><link
  162. linkend="safe_numerics.promotion_policies.native">native</link></code>
  163. type promotion policy are consistent with those of standard C++</para>
  164. <para>Up until now, we've focused on detecting when an arithmetic error
  165. occurs and invoking an exception or other kind of error handler.</para>
  166. <para>But now we look at another option. Using the <link
  167. linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
  168. type promotion policy, we can change the rules of C++ arithmetic for safe
  169. types to something like the following:</para>
  170. <para><itemizedlist>
  171. <listitem>
  172. <para>for any C++ numeric type, we know from <ulink
  173. url="http://en.cppreference.com/w/cpp/types/numeric_limits"><code>std::numeric_limits</code></ulink>
  174. what the maximum and minimum values that a variable can be - this
  175. defines a closed interval.</para>
  176. </listitem>
  177. <listitem>
  178. <para>For any binary operation on these types, we can calculate the
  179. interval of the result at compile time.</para>
  180. </listitem>
  181. <listitem>
  182. <para>From this interval we can select a new type which can be
  183. guaranteed to hold the result and use this for the calculation. This
  184. is more or less equivalent to the following code:</para>
  185. <programlisting>int x, y;
  186. int z = x + y // could overflow
  187. // so replace with the following:
  188. int x, y;
  189. long z = (long)x + (long)y; // can never overflow</programlisting>
  190. <para>One could do this by editing his code manually as above, but
  191. such a task would be tedious, error prone, non-portable and leave
  192. the resulting code hard to read and verify. Using the <link
  193. linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
  194. type promotion policy will achieve the equivalent result without
  195. these problems.</para>
  196. </listitem>
  197. </itemizedlist></para>
  198. <para>When using the <link
  199. linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
  200. type promotion policy, with a given a binary operation, we silently
  201. promote the types of the operands to a wider result type so the result
  202. cannot overflow. This is a fundamental departure from the C++ Standard
  203. behavior.</para>
  204. <para>If the interval of the result cannot be guaranteed to fit in the
  205. largest type that the machine can handle (usually 64 bits these days), the
  206. largest available integer type with the correct result sign is used. So
  207. even with our "automatic" type promotion scheme, it's still possible to
  208. overflow. So while our <link
  209. linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
  210. type promotion policy might eliminate exceptions in our example above, it
  211. wouldn't be guaranteed to eliminate them for all programs.</para>
  212. <para>Using the <link
  213. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
  214. exception policy will produce a compile time error anytime it's possible
  215. for an error to occur.</para>
  216. <para>This small example illustrates how to use automatic type promotion
  217. to eliminate all runtime penalty.</para>
  218. <para><programlisting><xi:include href="../../example/example82.cpp"
  219. parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting></para>
  220. <itemizedlist>
  221. <listitem>
  222. <para>the <link
  223. linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
  224. type promotion policy has rendered the result of the sum of two
  225. <code>integers</code> as a <code>safe&lt;long</code>&gt; type.</para>
  226. </listitem>
  227. <listitem>
  228. <para>our program compiles without error - even when using the <link
  229. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
  230. exception policy. This is because since a <code>long</code> can always
  231. hold the result of the sum of two integers.</para>
  232. </listitem>
  233. <listitem>
  234. <para>We do not need to use the <code>try/catch</code> idiom to handle
  235. arithmetic errors - we will have no exceptions.</para>
  236. </listitem>
  237. <listitem>
  238. <para>We only needed to change two lines of code to achieve our goal
  239. of guaranteed program correctness with no runtime penalty.</para>
  240. </listitem>
  241. </itemizedlist>
  242. <para>The above program produces the following output:</para>
  243. <para><screen>example 82:
  244. x = &lt;int&gt;[-2147483648,2147483647] = 2147483647
  245. y = &lt;int&gt;[-2147483648,2147483647] = 2
  246. x + y = &lt;long&gt;[-4294967296,4294967294] = 2147483649
  247. </screen></para>
  248. <para>Note that if any time in the future we were to change
  249. safe&lt;int&gt; to safe&lt;long long&gt; the program could now overflow.
  250. But since we're using <link
  251. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
  252. the modified program would fail to compile. At this point we'd have to
  253. alter our yet program again to eliminate run time penalty or set aside our
  254. goal of zero run time overhead and change the exception policy to <link
  255. linkend="safe_numerics.exception_policies.default_exception_policy"><code>default_exception_policy</code></link>
  256. .</para>
  257. <para>Note that once we use automatic type promotion, our programming
  258. language isn't C/C++ anymore. So don't be tempted to so something like the
  259. following:</para>
  260. <programlisting>// DON'T DO THIS !
  261. #if defined(NDEBUG)
  262. using safe_t = boost::numeric::safe&lt;
  263. int,
  264. boost::numeric::automatic, // note use of "automatic" policy!!!
  265. boost::numeric::loose_trap_policy
  266. &gt;;
  267. #else
  268. using safe_t = boost::numeric::safe&lt;int&gt;;
  269. #endif
  270. </programlisting>
  271. </section>
  272. <section id="safe_numerics.eliminate_runtime_penalty.3">
  273. <title>Mixing Approaches</title>
  274. <para>For purposes of exposition, we've divided the discussion of how to
  275. eliminate runtime penalties by the different approaches available. A
  276. realistic program could likely include all techniques mentioned above.
  277. Consider the following:</para>
  278. <programlisting><xi:include href="../../example/example84.cpp"
  279. parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
  280. <para><itemizedlist>
  281. <listitem>
  282. <para>As before, we define a type <code>safe_t</code> to reflect our
  283. view of legal values for this program. This uses the <link
  284. linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
  285. type promotion policy as well as the <link
  286. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
  287. exception policy to enforce elimination of runtime penalties.</para>
  288. </listitem>
  289. <listitem>
  290. <para>The function <code>f</code> accepts only arguments of type
  291. <code>safe_t</code> so there is no need to check the input values.
  292. This performs the functionality of <emphasis><emphasis
  293. role="bold">programming by contract</emphasis></emphasis> with no
  294. runtime cost.</para>
  295. </listitem>
  296. <listitem>
  297. <para>In addition, we define <code>input_safe_t</code> to be used
  298. when reading variables from the program console. Clearly, these can
  299. only be checked at runtime so they use the throw_exception policy.
  300. When variables are read from the console they are checked for legal
  301. values. We need no ad hoc code to do this, as these types are
  302. guaranteed to contain legal values and will throw an exception when
  303. this guarantee is violated. In other words, we automatically get
  304. checking of input variables with no additional programming.</para>
  305. </listitem>
  306. <listitem>
  307. <para>On calling of the function <code>f</code>, arguments of type
  308. <code>input_safe_t</code> are converted to values of type
  309. <code>safe_t</code> . In this particular example, it can be
  310. determined at compile time that construction of an instance of a
  311. <code>safe_t</code> from an <code>input_safe_t</code> can never
  312. fail. Hence, no <code>try/catch</code> block is necessary. The usage
  313. of the <link
  314. linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
  315. policy for <code>safe_t</code> types guarantees this to be true at
  316. compile time.</para>
  317. </listitem>
  318. </itemizedlist>Here is the output from the program when values 12 and 32
  319. are input from the console:</para>
  320. <screen>example 84:
  321. type in values in format x y:33 45
  322. x&lt;signed char&gt;[-24,82] = 33
  323. y&lt;signed char&gt;[-24,82] = 45
  324. z = &lt;short&gt;[-48,164] = 78
  325. (x + y) = &lt;short&gt;[-48,164] = 78
  326. (x - y) = &lt;signed char&gt;[-106,106] = -12
  327. &lt;short&gt;[-48,164] = 78
  328. </screen>
  329. </section>
  330. </section>