High-level abstraction in programming. Our friend and enemy

    Most of the progress that we observe in the window is the result of human laziness. Too lazy to paint the fence with a brush, invented the roller. Laziness was to paint with a roller, they invented a spray gun. Well, you get the point. What does all this have to do with programming? The most immediate!

    Friend


    The first programmers manipulated such concepts as “0” and “1” and this was long and incomprehensible. With the help of zeros and ones, laziness suggested writing a program that converts MOV, ADD, JMP into new zeros and ones that are understandable to the processor.

    In the future, laziness did not doze off and called instead of 6-10 lines of assembler to write 1 high-level command. Then came even more lazy things. OOP, databases, and so on and so forth. Programs became more understandable and understandable, the search for errors was reduced significantly, thanks to various frameworks, the development speed increased. High-level abstraction marched on the planet. And were everyone happy?

    Enemy


    But the enemy did not doze off! At one point, the programmer is faced with a problem that cannot be solved at this level of abstraction, but even understand where his legs grow from this problem. In order to understand what is happening you have to climb into the jungle of zeroes and ones. Examples? Yes, please (all examples are given in C #).

    Example 1

            static void Main(string[] args)
            {
                double tenth = 0.1;
                double first = 0;
                for (int i = 0; i < 10; i++)
                {
                    first += tenth;
                }

                double eighth = 0.125;
                double second = 0;
                for (int i = 0; i < 8; i++)
                {
                    second += eighth;
                }
                Console.WriteLine(first == second);
                Console.WriteLine("0.1 * 10 = {0}\n0.125 * 8 = {1}", first, second);
                
                Console.ReadKey();
            }


    Try to guess what we will see on the screen.

    Have you tried it? And what did you do? So, for those who suggested, well, for those who were too lazy and just read to this place, I inform, we will see - False.
    image
    Notice that the values ​​match? In principle, if we replace 10 with 100, and 8 with 80, we will see that the values ​​are different, but here it is O_o.
    Here it is the level of zeros and ones. In principle, understanding how floating-point numbers are represented, it is clear that 1/8 is nothing more than 2 to -3 degrees and it will add up without errors, but 0.1 cannot be represented as the sum of integer powers of a two in the capacity of a modern double . This is where the error runs up.
    Continue?

    Example 2

            static void Main(string[] args)
            {
                int n = 10000;
                int[,] array1 = new int[n, n];
                int[,] array2 = new int[n, n];
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        array1[i, j] = 0;
                        array2[i, j] = 0;
                    }
                }
                DateTime begin = DateTime.Now;
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        array1[j, i]++;
                    }
                }
                Console.WriteLine("Время работы [j, i] = {0}", DateTime.Now.Subtract(begin).TotalSeconds);
                
                begin = DateTime.Now;
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < n; j++)
                    {
                        array1[i, j]++;
                    }
                }
                Console.WriteLine("Время работы [i, j] = {0}", DateTime.Now.Subtract(begin).TotalSeconds);
                
                Console.ReadKey();
            }

    The most amazing thing is that by changing the order of the indices when accessing the elements of the array, we get a change in the operating speed almost twice (on my computer the first cycle worked in 2.16 s; the second in 1.13 s). With an increase in n to 11000, the difference is already 5 times.
    Where is the difference from? Again from the level of zeros and ones. In this case, work with the cache is manifested and the fact that the two-dimensional array in memory is actually one-dimensional.

    Example 3

    With the advent of generic types, this example may not be relevant, but still I can not resist. In the first case, the execution time was 0.017 seconds, in the second, 0.005 seconds. Here, not so much low-level things come into effect, but the features of working with the conversion of simple types to the object type (anyone interested can read about packaging and unpacking).
            class MyPack
            {
                public int Value { get; set; }
            }
            
            static void Main(string[] args)
            {
                int n = 100000;
                object[] array1 = new object[n];
                object[] array2 = new object[n];
                for (int i = 0; i < n; i++)
                {
                    array1[i] = 0;
                    array2[i] = new MyPack() { Value = 0 };
                }
                DateTime begin = DateTime.Now;
                for (int i = 0; i < n; i++)
                {
                    array1[i] = (int)array1[i] + 1;
                }
                Console.WriteLine("Время работы c int = {0}", DateTime.Now.Subtract(begin).TotalSeconds);

                begin = DateTime.Now;
                for (int i = 0; i < n; i++)
                {
                    ((MyPack)array2[i]).Value = ((MyPack)array2[i]).Value + 1;
                }
                Console.WriteLine("Время работы с MyPack = {0}", DateTime.Now.Subtract(begin).TotalSeconds);
                Console.ReadKey();
            }




    conclusions


    To begin with, I do not consider high-level abstraction to be evil. And the title of the article, if you noticed, is that high-level abstraction is a friend to us, and so that it does not turn into an enemy, I’ll go and read about the new Intel processor architectures.

    Ps At the time of writing, I went out into the corridor, where I ran into a colleague. So, he came up with a problem that when developing an application for Windows Mobile, the application works adequately until the PDA goes to sleep. When you bring it to the normal mode of the application, there is no longer any memory, no error messages, nothing. What do you think of what? I think because of a hole in a high-level abstraction.

    Also popular now: