Friday, January 18, 2019

Roadmap to Developing an Understanding of Python Concurrency

We will provide a suggested roadmap for developing an understanding of concurrency in general and its implementation in Python.

The following questions will be addressed:
  1. What is concurrency?
    1. What are the different flavors of concurrency?
      1. How does Python implement concurrency?
        The road map will provide brief descriptions of key concepts and how they fit together. For those that want to delve deeper into any concept, links will be provided.

        Motivation for Blog Creation


        I was "volunteered" to give a presentation on Python concurrency at the Pittsburgh Python users group.

        One of the questions that come up for people is whether or not to go to the presentation. It is hard to determine this from a brief description. So, my solution is to create a blog post for the presentation ahead of time.

        Another question that comes up is how to manage the quantity of material presented in a short period of time. This will be especially true in the case of Python concurrency. So, having the blog post ahead of time will allow people to focus on their interactions with others and spend less time taking notes.

        Also, some will take advantage of the blog post to prepare questions and comments for the presentation.

        Definitions


        Dictionary.com defines concurrence as "simultaneous occurence". The phrase "from a human perspective" needs to be added. So, our definition of concurrence is "simultaneous occurence from a human perspective."

        Compared to computers, humans are really slow. I am going to pick 100 ms response time as being instantaneous from a human perspective. If you want to pick another value, refer to How Fast is Realtime? Human Perception and Technology by PubNub Staff. Computer instructions can be measured in Millions of Instructions Per Second (MIPS). So, even if you had just 1 MIP available, you could execute 100,000 instructions before a human would notice. Please note that an old Pentium based PC was rated at 100 MIPS.

        This human slowness allows us to play tricks when implementing software. Some of the tricks center around the ability of the software to switch between "stuff" without the human ever noticing. The "stuff" goes by names like threads and tasks. These names will be explicitly defined later in the blog.

        Number of Cores: Many - Multiprocessing


        One flavor of concurrency is multiprocessing. Let's begin to create a definition for multiprocessing with a picture.



        Here we have individuals juggling independently of each other. There is no interaction between them. So, a process is execution which shares no resources with another process. The operating system (OS) assigns a process resources and that is that. Depending on the computer load, the OS might suspend the process and assign those resources to another process. Note that there is no interaction between the suspended process and the new process. If more information is desired regarding multiprocessing, refer to the Wikipedia multiprocessing article.

        Working Only with Python Code - Use Python's Multiprocessing Module


        If more information is desired regarding Python's implementation of multiprocessing when only Python code is involved, refer to its multiprocessing module. It provides queues, events, worker pools, and other marshaling paradigms.

        Working with Non-Python Code - Use Python's Subprocess Module


        Use cases exist where have to concurrently invoke non-Python code. In such circumstances, the subprocess module should be used. Unfortunately, it does not provide queues, events, worker pools, and other marshaling paradigms. This is to be expected because each external interface can be different.

        Number of Cores: 1 - Multitasking


        If you aren't using the 2 previously mentioned modules, then execution can occur in only 1 core. The reason for this is Python's Global Interpreter Lock (GIL). For further details, refer to the blog What is the Python Global Interpreter Lock (GIL)? by Abhinav Ajitsaria.

        This other flavor of concurrency is called multitasking. In turn, multitasking has 2 sub-flavors: pre-emptive multitasking and cooperative multitasking.

        Pre-Emptive Multitasking (Threading)


        As before, we will begin to create a definition with a picture.



        If you notice, there is one individual and he can; at will, pick up a ball and place it in another location. This is done without examining the state of the ball. Pre-emptive multitasking is the same thing except that the individual is replaced by the OS and the balls are replaced by threads.

        Below are the standard threading questions
        1. Is the code thread safe? For further details, refer to "Wikipedia: Thread safety."
          1. Another way of asking the above question but using different words:
            1. Do you have any race conditions? For further details, refer to "Wikipedia: Race condition."
            2. How many threads to create? Experimentation is usually required to answer this question.
              Please note that the above is not "just 3 questions." Each question by itself involves a lot of complexity.

              Cooperative Multitasking (Asyncio)


              For the third time, we will begin to create a definition with a picture.



              If you notice the 3 individuals have to cooperate very closely in order to successfully juggle. This cooperation is the key to cooperative multitasking. Regrettably, that is as far as the analogy will take us this time.

              In the case of asyncio, the entities doing work are called tasks. Tasks cannot be interrupted. In other words, they are not thread-safe. However, the advantage of not being thread safe is that it simplifies them. Also, each task can be in a "ready to run" or "waiting" state. The coordination of the tasks is accomplished via the simple concept of an Event Loop. This again reduces the complexity.

              The advantage of cooperative multitasking is that don't have to worry about thread safety. The con is the added complexity of writing the software to make sure that everything cooperates.

              Use Cases


              Now that we have discussed the concepts and tools, let's determine when they should be used.

              The blog post Speed Up Your Python Program With Concurrency by Jim Anderson contains a really nice set of code snippets that demonstrate the tools. Its code snippets will be extensively used below.

              All code was executed using Python 3.6.7.

              Use Case 1: I/O Bound


              The I/O bound condition is created by the download of internet web pages. In this situation, the CPU spends a large portion of its time waiting for data to arrive over the internet. 


              Non-Concurrent Code


              To go to the non-concurrent code from Jim Anderson's blog, click here. The code requires that the requests module is installed.

              Pro: Code simplicity

              Con: Slow compared to the other approaches.

              Use Threading


              To go to the code that uses threading from Jim Anderson's blog, click here.

              Notice the change in the download_all_sites function between the non-concurrent code and the code that uses threading. The corresponding code snippets are below.

              Non-Concurrent Code
              
              
              def download_all_sites(sites):
                  with requests.Session() as session:
                      for url in sites:
                          download_site(url, session)
              
              
              Threading: Code Snippet 1
              
              
              def download_all_sites(sites):
                  with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
                      executor.map(download_site, sites)
              
              
              Let's examine ThreadPoolExecutor. As the name implies, you get a thread, a pool and an executor. In this particular case, you get a pool of 5 threads. The executor is the guy that does the co-ordination.

              Thread safety is addressed by using Thread-Local Data in the get_session function. The associated code snippet is below. Notice that created the thread_local object once and not once for each thread.

              Threading: Code Snippet 2
              threadLocal = threading.local()
              
              def get_session():
                  if not getattr(thread_local, "session", None):
                      thread_local.session = requests.Session()
                  return thread_local.session
              
              
              Please note that requests.Session() is not thread-safe.

              Pro: Faster than non-concurrent code

              On my laptop, the code that used threading was about 0.2 seconds faster than the non-concurrent code. This may not seem like much; but at scale, it makes a difference. Note that, the elapsed time is a good way to start assessing performance. If more precise measurements are needed consider using timeit with process_time().

              Con: Dramatic increase in complexity

              We now have to deal with
              1. Executors
                1. Pools
                  1. Thread Safety
                    1. Threads

                    Use Asyncio


                    To go to the code that uses asyncio from Jim Anderson's blog, click here. The code requires that the aiohttp module is installed.

                    Once again, let's focus on the download_all_sites function.

                    Asyncio Code
                    
                    
                    async def download_all_sites(sites):
                        async with aiohttp.ClientSession() as session:
                            tasks = []
                            for url in sites:
                                task = asyncio.ensure_future(download_site(session, url))
                                tasks.append(task)
                            await asyncio.gather(*tasks, return_exceptions=True)
                    
                    Lets start by noticing that the keyword async prefixes the function definition. This turns the function into a coroutine. Coroutines are code blocks that have the ability to yield execution to other code blocks.

                    Next, notice that Session is created only once and shared among all the tasks. This is possible because a task can't be interrupted and leave the Session in a bad state. So, the tasks can share the Session.

                    The tasks are created and started using asyncio.ensure_future(). If you are using Python 3.7 or later, you can use asyncio.create_task() instead. To ensure that all the tasks are completed before the Session is terminated, asyncio.gather() is used.

                    The await keyword is how the task hands control back to the event loop.

                    Now that we have the tasks, we need to start the event loop and give it the tasks. This is accomplished with the following in __main__


                    If you are using Python 3.7 or higher, you can use asyncio.run() instead.

                    Pro: Noticeably faster than using threads

                    On my laptop, the code that used asyncio was about 5 seconds faster than the code using threads. Notice going from non-current code to threading only improved performance by about 0.2 seconds. Going from threading to asyncio, performance improved by about 5 seconds. This is over an order of magnitude faster!

                    Con: More complex syntax

                    The code becomes littered with "async." You need to create and start tasks. You then need to make sure that all the tasks complete before the session is terminated. Last but not least, you have to take care of the event loop.

                      Use asyncio when you can - Threading when you must


                      The following is a direct quote from Jim Anderson's article
                      1. For I/O-bound problems, there’s a general rule of thumb in the Python community: “Use asyncio when you can, threading when you must.”
                        This is such a critical rule of thumb that it deserves its own section.

                        Use Multiprocessing


                        To go to the code that uses multiprocessing from Jim Anderson's blog, click here.

                        As in the previous cases, let's focus on the download_all_sites function.

                        Multiprocessing Code Snippet 1
                        
                        
                        def download_all_sites(sites):
                            with multiprocessing.Pool(initializer=set_global_session) as pool:
                                pool.map(download_site, sites)
                        
                        Lets start by noticing multiprocessing.Pool(). It creates a pool of worker processes to which jobs can be submitted. Since the processes parameter was not specified, it defaults to the number of CPUs in the system. Picking the number of worker processes is just as hard as picking the number of threads. The other thing to notice is that it has a parallel map implementation. So "pool.map(download_site, sites)", maps each URL to the function download_site.

                        Pro: Can take advantage of all the cores on the computer.

                        Con: Complexity of trying to align Session and processes results in the weirdness of "global session." See code snippet below.

                        Multiprocessing Code Snippet 2
                        
                        
                        session = None
                        
                        def set_global_session():
                            global session
                            if not session:
                                session = requests.Session()
                        
                        Since every process has its own memory, they can not share a Session object. At the other extreme, you don't want to create a Session each time the function is called. The goal is to create a Session for each process. This is accomplished by using the initializer parameter as shown in "Multiprocessing Code Snippet 1."

                        In terms of coding complexity, multiprocessing code is on par with the code that uses threading. In terms of performance, at least on my laptop, it was slower than asyncio but faster than the other 2 approaches. Below is a table of the run times.

                        Approach        Run Time (seconds)
                        --------------  ------------------
                        No Concurrency  7.4
                        Threading       7.3
                        Multiprocessing 5.8
                        Asycnio         2.2
                        
                        Notice how much faster asyncio is.

                        Use Case 2: CPU Bound


                        We will continue to use the code snippets from Jim Anderson's blog. These code snippets compute the sum of the square of each number from 1 to a passed in value to create a CPU bound condition.

                        Non-Concurrent Code


                        To go to the non-concurrent code from Jim Anderson's blog, click here. Nothing much to say. We should be able to do better using multiple cores.

                        Use Threading


                        To go to the code that uses threading from Jim Anderson's blog, click here. Again, nothing much to say. As expected, the threading approach takes longer than the non-current approach.

                        Use Multiprocessing


                        To go to the code that uses multiprocessing from Jim Anderson's blog, click here.

                        Just like in the I/O bound multiprocessing case, multiprocessing.Pool() and pool.map() are used.

                        Multiprocessing Code Snippet
                        
                        
                        def find_sums(numbers):
                            with multiprocessing.Pool() as pool:
                                pool.map(cpu_bound, numbers)
                        
                        As expected multiprocessing shines on CPU bound problems. It was over 3 times faster than the non-concurrent version of the code.

                        Pro: Significant performance improvement with little-added code complexity.

                        The very little code complexity arises because nothing is shared.

                        Con: Splitting up the problem so that each processor can work independently can be difficult.

                        This issue did not show up in the trivial example that we did. Sometimes, problems are not embarrassingly parallel.

                        Use Case 3: Python Concurrent Interaction with Non-Python Code


                        To Do: Put together code that cycles through directories that invokes Java to convert DocBook XML to HTML.

                        As an aside, the above points to the fact that you can use Python instead of bash.

                        Alternatives


                        There are many other options to achieve concurrency. For example, Cython supports native parallelism through the cython.parallel module. A good place to start exploring these other alternatives is "General concepts: concurrency, parallelism, threads and processes" at gevent.

                        Perhaps Concurrency Cost is Not Worth It?


                        As the preceding material indicates, concurrency is extremely complex with a lot of nuances. Which form of concurrency do I use? Is my code thread safe? Did I make sure that all my tasks completed prior to the Session terminating? And so on.

                        One option might be to just wait. The business people want to be able to run the report several times during the day but they aren't willing to allocate resources to make this happen. In that case, they will have to wait for the overnight run to complete.

                        Another option is to just to apply brute force. Buy a bigger machine or faster disks. There is nothing wrong with brute force.



                        Let me be very blunt: DO NOT USE CONCURRENCY UNTIL YOU HAVE EXHAUSTED ALL OTHER OPTIONS!

                        Summary


                        Concurrency is a big topic. Python's implementation of concurrency is an even bigger topic. The goal of the preceding material was to provide a roadmap for learning about concurrency and Python's implementation of it at a high level. The roadmap has embedded links which provide jumping off points for further detailed learning.

                        References

                        1. aiohttp module
                          1. Articles
                          2. Books
                            1. Concurrent Patterns and Best Practices by Atul S. Khot
                            2. Distributed Computing with Python by Francesco Pierfederici
                            3. Mastering Concurrency in Python by Quan Nguyen
                            4. Parallel Programming with Python by Jan Palach
                            5. Python High Performance Programming by Gabriele Lanaro
                            6. Python Parallel Programming Cookbook by Giancarlo Zaccone
                          3. Dask
                          4. PGH Python User's Group Meetup: Python Concurrency Guided Tour
                            1. Slides
                          5. Python Documentation
                          6. requests module
                          7. Videos

                          Thursday, December 27, 2018

                          Install VMware Workstation on Ubuntu

                          This blog post is of interest to people that need to install VMware Workstation on their Ubuntu system.

                          The goal is not to create Yet Another Blog (YAB) on this subject. There are already plenty of blogs on this subject. Unfortunately, it is rare to find blogs on performing the installation from the command line. It is even rarer to find detailed prerequisites for the installation.

                          It is assumed that the reader has some familiarity with Unix. This is not an unreasonable assumption because people should not be performing a command line installation of VMware Workstation unless they are familiar with Unix.

                          All the commands below were executed using Workstation Pro 15.0.2 on Ubuntu 18.04.01.
                          1. Verify that Workstation 15.x is certified on Ubuntu 18.04
                          2. Download VMware Workstation
                            1. The download won't be quick because the file is over 400 MB in size.
                              1. Examine the downloaded file
                                rlucente@rlucente-Bonobo-WS:~/lucente_robert/vm_ware/work_station_pro_download$ ls -l 
                                
                                total 484052
                                
                                -rw-rw-r-- 1 rlucente rlucente 495664015 Nov 30 15:37 VMware-Workstation-Full-15.0.2-10952284.x86_64.bundle
                                
                                
                              2. Prepare environment for VMware Workstation
                                1. Preliminaries
                                  1. Update operating sytem
                                    1. Command: sudo apt-get update
                                      1. Output of Command
                                        rlucente@rlucente-Bonobo-WS:~$ sudo apt-get update
                                        [sudo] password for rlucente: 
                                        
                                        Hit:1 http://us.archive.ubuntu.com/ubuntu bionic InRelease
                                        
                                        Get:2 http://us.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
                                        
                                        Hit:3 http://ppa.launchpad.net/system76-dev/stable/ubuntu bionic InRelease
                                        
                                        Get:4 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB]
                                        
                                        Get:5 http://us.archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB]
                                        
                                        Fetched 247 kB in 1s (256 kB/s)
                                        
                                        Reading package lists... Done
                                        
                                        
                                      2. Command: sudo apt-get dist-upgrade
                                        1. Output of Command
                                          rlucente@rlucente-Bonobo-WS:~$ sudo apt-get dist-upgrade
                                          
                                          Reading package lists... Done
                                          
                                          Building dependency tree  
                                          
                                          Reading state information... Done
                                          
                                          Calculating upgrade... Done
                                          
                                          The following packages were automatically installed and are no longer required:
                                          
                                            libnvidia-cfg1-396 libnvidia-common-396 libnvidia-compute-396
                                          
                                            libnvidia-compute-396:i386 libnvidia-decode-396 libnvidia-decode-396:i386
                                          
                                            libnvidia-encode-396 libnvidia-encode-396:i386 libnvidia-fbc1-396
                                          
                                            libnvidia-fbc1-396:i386 libnvidia-gl-396 libnvidia-gl-396:i386
                                          
                                            libnvidia-ifr1-396 libnvidia-ifr1-396:i386 nvidia-compute-utils-396
                                          
                                            nvidia-dkms-396 nvidia-driver-396 nvidia-utils-396
                                          
                                            xserver-xorg-video-nvidia-396
                                          
                                          Use 'sudo apt autoremove' to remove them.
                                          
                                          The following packages will be upgraded:
                                          
                                            python3-update-manager unattended-upgrades update-manager
                                          
                                            update-manager-core
                                          
                                          4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
                                          
                                          Need to get 632 kB of archives.
                                          
                                          After this operation, 1,024 B of additional disk space will be used.
                                          
                                          Do you want to continue? [Y/n] Y
                                          
                                          Get:1 http://us.archive.ubuntu.com/ubuntu bionic-updates/main amd64 python3-update-manager all 1:18.04.11.7 [35.0 kB]
                                          
                                          Get:2 http://us.archive.ubuntu.com/ubuntu bionic-updates/main amd64 update-manager-core all 1:18.04.11.7 [8,484 B]
                                          
                                          Get:3 http://us.archive.ubuntu.com/ubuntu bionic-updates/main amd64 update-manager all 1:18.04.11.7 [551 kB]
                                          
                                          Get:4 http://us.archive.ubuntu.com/ubuntu bionic-updates/main amd64 unattended-upgrades all 1.1ubuntu1.18.04.6 [37.7 kB]
                                          
                                          Fetched 632 kB in 2s (415 kB/s)
                                          
                                          Preconfiguring packages ...
                                          
                                          (Reading database ... 170931 files and directories currently installed.)
                                          
                                          Preparing to unpack .../python3-update-manager_1%3a18.04.11.7_all.deb ...
                                          
                                          Unpacking python3-update-manager (1:18.04.11.7) over (1:18.04.11.6) ...
                                          
                                          Preparing to unpack .../update-manager-core_1%3a18.04.11.7_all.deb ...
                                          
                                          Unpacking update-manager-core (1:18.04.11.7) over (1:18.04.11.6) ...
                                          
                                          Preparing to unpack .../update-manager_1%3a18.04.11.7_all.deb ...
                                          
                                          Unpacking update-manager (1:18.04.11.7) over (1:18.04.11.6) ...
                                          
                                          Preparing to unpack .../unattended-upgrades_1.1ubuntu1.18.04.6_all.deb ...
                                          
                                          Unpacking unattended-upgrades (1.1ubuntu1.18.04.6) over (1.1ubuntu1) ...
                                          
                                          Processing triggers for mime-support (3.60ubuntu1) ...
                                          
                                          Processing triggers for ureadahead (0.100.0-20) ...
                                          
                                          ureadahead will be reprofiled on next reboot
                                          
                                          Processing triggers for desktop-file-utils (0.23-1ubuntu3.18.04.2) ...
                                          
                                          Processing triggers for libglib2.0-0:amd64 (2.56.3-0ubuntu0.18.04.1) ...
                                          
                                          Setting up python3-update-manager (1:18.04.11.7) ...
                                          
                                          Processing triggers for systemd (237-3ubuntu10.9) ...
                                          
                                          Setting up unattended-upgrades (1.1ubuntu1.18.04.6) ...
                                          
                                          Replacing config file /etc/apt/apt.conf.d/50unattended-upgrades with new version
                                          
                                          Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
                                          
                                          Processing triggers for gnome-menus (3.13.3-11ubuntu1.1) ...
                                          
                                          Setting up update-manager-core (1:18.04.11.7) ...
                                          
                                          Setting up update-manager (1:18.04.11.7) ...
                                          
                                          
                                          1. Comment
                                            1. Ignore "The following packages were automatically installed and are no longer required:" because they are related to nvidia.
                                        2. Determine operating system version
                                          1. Command: lsb_release -a
                                            1. Output of Command
                                              rlucente@rlucente-Bonobo-WS:~$ lsb_release -a No LSB modules are available.
                                              Distributor ID: Ubuntu
                                              Description: Ubuntu 18.04.1 LTS
                                              Release: 18.04
                                              Codename: bionic
                                              
                                              
                                          2. Determine kernel release
                                            1. This information is used in many places in the following steps.
                                              1. Command: uname -r
                                                1. Output of Command
                                                  rlucente@rlucente-Bonobo-WS:~$ uname -r
                                                  4.15.0-39-generic
                                                  
                                                  
                                            2. VMware Workstation Prerequisites
                                              1. Operating System Prerequisites for VMware Workstation
                                                1. Real-Time Clock (RTC) Function Compiled into Kernel
                                                  1. Command: sudo hwclock --debug
                                                    1. Output of Command
                                                      rlucente@rlucente-Bonobo-WS:~$ sudo hwclock --debug
                                                      
                                                      hwclock from util-linux 2.31.1
                                                      
                                                      System Time: 1543604238.820144
                                                      
                                                      Trying to open: /dev/rtc0
                                                      
                                                      Using the rtc interface to the clock.
                                                      
                                                      Assuming hardware clock is kept in UTC time.
                                                      
                                                      Waiting for clock tick...
                                                      
                                                      ...got clock tick
                                                      
                                                      Time read from Hardware Clock: 2018/11/30 18:57:19 
                                                      
                                                      Hw clock time : 2018/11/30 18:57:19 = 1543604239 seconds since 1969
                                                      
                                                      Time since last adjustment is 1543604239 seconds
                                                      
                                                      Calculated Hardware Clock drift is 0.000000 seconds
                                                      
                                                      2018-11-30 13:57:18.795241-0500
                                                      
                                                      
                                                      1. Comment
                                                        1. The following 2 lines indicate that RTC function is compiled into the kernel
                                                          1. Trying to open: /dev/rtc0
                                                            1. Using the rtc interface to the clock.
                                                        2. Parallel port PC-style hardware option (CONFIG_PARPORT_PC) set to m
                                                          1. Content of file /boot/config-4.15.0-39-generic
                                                            #
                                                            # Automatically generated file; DO NOT EDIT.
                                                            # Linux/x86 4.15.0-39-generic Kernel Configuration
                                                            #
                                                            CONFIG_64BIT=y
                                                            CONFIG_X86_64=y
                                                            CONFIG_X86=y
                                                            
                                                            ...
                                                            
                                                            CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
                                                            CONFIG_PARPORT=m
                                                            
                                                            CONFIG_PARPORT_PC=m
                                                            
                                                            CONFIG_PARPORT_SERIAL=m
                                                            CONFIG_PARPORT_PC_FIFO=y
                                                            
                                                            ...
                                                            
                                                            
                                                          2. Swap space must be 8 GB or larger
                                                            1. For the details, refer to my blog post "Increasing Swap Size on Ubuntu
                                                          3. Package Prerequisites for VMware Workstation
                                                            1. gcc (Gnu C compiler)
                                                              1. Command: sudo apt-get install gcc
                                                                1. Output of Command
                                                                  rlucente@rlucente-Bonobo-WS:/boot$ sudo apt-get install gcc 
                                                                  
                                                                  [sudo] password for rlucente: 
                                                                  
                                                                  Reading package lists... Done
                                                                  
                                                                  Building dependency tree       
                                                                  
                                                                  Reading state information... Done
                                                                  
                                                                  gcc is already the newest version (4:7.3.0-3ubuntu2.1).
                                                                  
                                                                  gcc set to manually installed.
                                                                  
                                                                  The following packages were automatically installed and are no longer required:
                                                                  
                                                                    libnvidia-cfg1-396 libnvidia-common-396 libnvidia-compute-396
                                                                  
                                                                    libnvidia-compute-396:i386 libnvidia-decode-396 libnvidia-decode-396:i386
                                                                  
                                                                    libnvidia-encode-396 libnvidia-encode-396:i386 libnvidia-fbc1-396
                                                                  
                                                                    libnvidia-fbc1-396:i386 libnvidia-gl-396 libnvidia-gl-396:i386
                                                                  
                                                                    libnvidia-ifr1-396 libnvidia-ifr1-396:i386 nvidia-compute-utils-396
                                                                  
                                                                    nvidia-dkms-396 nvidia-driver-396 nvidia-utils-396
                                                                  
                                                                    xserver-xorg-video-nvidia-396
                                                                  
                                                                  Use 'sudo apt autoremove' to remove them.
                                                                  
                                                                  0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
                                                                  
                                                                  
                                                                2. make (determines automatically which pieces of a program need to be re-compiled and issues the commands necessary to recompile them)
                                                                  1. Command: sudo apt-get install make
                                                                    1. Output of Command
                                                                      rlucente@rlucente-Bonobo-WS:/boot$ sudo apt-get install make
                                                                      
                                                                      Reading package lists... Done
                                                                      
                                                                      Building dependency tree
                                                                      
                                                                      Reading state information... Done
                                                                      
                                                                      make is already the newest version (4.1-9.1ubuntu1).
                                                                      
                                                                      make set to manually installed.
                                                                      
                                                                      The following packages were automatically installed and are no longer required:
                                                                      
                                                                        libnvidia-cfg1-396 libnvidia-common-396 libnvidia-compute-396
                                                                      
                                                                        libnvidia-compute-396:i386 libnvidia-decode-396 libnvidia-decode-396:i386
                                                                      
                                                                        libnvidia-encode-396 libnvidia-encode-396:i386 libnvidia-fbc1-396
                                                                      
                                                                        libnvidia-fbc1-396:i386 libnvidia-gl-396 libnvidia-gl-396:i386
                                                                      
                                                                        libnvidia-ifr1-396 libnvidia-ifr1-396:i386 nvidia-compute-utils-396
                                                                      
                                                                        nvidia-dkms-396 nvidia-driver-396 nvidia-utils-396
                                                                      
                                                                        xserver-xorg-video-nvidia-396
                                                                      
                                                                      Use 'sudo apt autoremove' to remove them.
                                                                      
                                                                      0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
                                                                      
                                                                      
                                                                    2. linux-headers (headers act as an interface between internal kernel components and also between userspace and the kernel)
                                                                      1. Command: sudo apt-get install linux-headers-$(uname -r)
                                                                        1. Output of Command
                                                                          rlucente@rlucente-Bonobo-WS:/boot$ sudo apt-get install linux-headers-$(uname -r)
                                                                          
                                                                          Reading package lists... Done
                                                                          
                                                                          Building dependency tree
                                                                          
                                                                          Reading state information... Done
                                                                          
                                                                          linux-headers-4.15.0-39-generic is already the newest version (4.15.0-39.42).
                                                                          
                                                                          linux-headers-4.15.0-39-generic set to manually installed.
                                                                          
                                                                          The following packages were automatically installed and are no longer required:
                                                                          
                                                                            libnvidia-cfg1-396 libnvidia-common-396 libnvidia-compute-396 libnvidia-compute-396:i386 libnvidia-decode-396 libnvidia-decode-396:i386 libnvidia-encode-396
                                                                          
                                                                            libnvidia-encode-396:i386 libnvidia-fbc1-396 libnvidia-fbc1-396:i386 libnvidia-gl-396 libnvidia-gl-396:i386 libnvidia-ifr1-396 libnvidia-ifr1-396:i386
                                                                          
                                                                            nvidia-compute-utils-396 nvidia-dkms-396 nvidia-driver-396 nvidia-utils-396 xserver-xorg-video-nvidia-396 Use 'sudo apt autoremove' to remove them.
                                                                          
                                                                          0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
                                                                          
                                                                          
                                                                        2. dkms (Dynamic Kernel Module Support)
                                                                          1. Dynamic Kernel Module Support (DKMS) is a program/framework that enables generating Linux kernel modules whose sources generally reside outside the kernel source tree. The concept is to have DKMS modules automatically rebuilt when a new kernel is installed.
                                                                            1. Command: sudo apt-get install dkms
                                                                              1. Output of Command
                                                                                rlucente@rlucente-Bonobo-WS:/boot$ sudo apt-get install dkms
                                                                                
                                                                                Reading package lists... Done
                                                                                
                                                                                Building dependency tree
                                                                                
                                                                                Reading state information... Done
                                                                                
                                                                                dkms is already the newest version (2.3-3ubuntu9.2).
                                                                                
                                                                                dkms set to manually installed.
                                                                                
                                                                                The following packages were automatically installed and are no longer required:
                                                                                
                                                                                  libnvidia-cfg1-396 libnvidia-common-396 libnvidia-compute-396 libnvidia-compute-396:i386 libnvidia-decode-396 libnvidia-decode-396:i386 libnvidia-encode-396
                                                                                
                                                                                  libnvidia-encode-396:i386 libnvidia-fbc1-396 libnvidia-fbc1-396:i386 libnvidia-gl-396 libnvidia-gl-396:i386 libnvidia-ifr1-396 libnvidia-ifr1-396:i386
                                                                                
                                                                                  nvidia-compute-utils-396 nvidia-dkms-396 nvidia-driver-396 nvidia-utils-396 xserver-xorg-video-nvidia-396 Use 'sudo apt autoremove' to remove them.
                                                                                
                                                                                0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
                                                                                
                                                                                
                                                                              2. build-essential (convenient way to install a basic C/C++ development environment)
                                                                                1. Note that "build-essential" is singular. Some blogs make the mistake of making it plural.
                                                                                  1. Command: sudo apt-get install build-essential
                                                                                    1. Output of Command
                                                                                      rlucente@rlucente-Bonobo-WS:/boot$ sudo apt-get install build-essential
                                                                                      
                                                                                      Reading package lists... Done
                                                                                      
                                                                                      Building dependency tree
                                                                                      
                                                                                      Reading state information... Done
                                                                                      
                                                                                      build-essential is already the newest version (12.4ubuntu1).
                                                                                      
                                                                                      build-essential set to manually installed.
                                                                                      
                                                                                      The following packages were automatically installed and are no longer required:
                                                                                      
                                                                                        libnvidia-cfg1-396 libnvidia-common-396 libnvidia-compute-396 libnvidia-compute-396:i386 libnvidia-decode-396 libnvidia-decode-396:i386 libnvidia-encode-396
                                                                                      
                                                                                        libnvidia-encode-396:i386 libnvidia-fbc1-396 libnvidia-fbc1-396:i386 libnvidia-gl-396 libnvidia-gl-396:i386 libnvidia-ifr1-396 libnvidia-ifr1-396:i386
                                                                                      
                                                                                        nvidia-compute-utils-396 nvidia-dkms-396 nvidia-driver-396 nvidia-utils-396 xserver-xorg-video-nvidia-396 Use 'sudo apt autoremove' to remove them.
                                                                                      
                                                                                      0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
                                                                                      
                                                                                      
                                                                                    2. libaio1 (Linux kernel Asynchronous I/O [AIO] library)
                                                                                      1. library enables userspace to use Linux kernel asynchronous I/O system calls, important for the performance of databases and other advanced applications.
                                                                                        1. Command: sudo apt-get install libaio1
                                                                                          1. Output of Command
                                                                                            $ sudo apt-get install libaio1
                                                                                            
                                                                                            Reading package lists... Done
                                                                                            
                                                                                            Building dependency tree
                                                                                            
                                                                                            Reading state information... Done
                                                                                            
                                                                                            The following packages were automatically installed and are no longer required:
                                                                                            
                                                                                              libnvidia-cfg1-396 libnvidia-common-396 libnvidia-compute-396 libnvidia-compute-396:i386 libnvidia-decode-396
                                                                                            
                                                                                              libnvidia-decode-396:i386 libnvidia-encode-396 libnvidia-encode-396:i386 libnvidia-fbc1-396
                                                                                            
                                                                                              libnvidia-fbc1-396:i386 libnvidia-gl-396 libnvidia-gl-396:i386 libnvidia-ifr1-396 libnvidia-ifr1-396:i386
                                                                                            
                                                                                              nvidia-compute-utils-396 nvidia-dkms-396 nvidia-driver-396 nvidia-utils-396 xserver-xorg-video-nvidia-396 Use 'sudo apt autoremove' to remove them.
                                                                                            
                                                                                            The following NEW packages will be installed:
                                                                                            
                                                                                              libaio1
                                                                                            
                                                                                            0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
                                                                                            
                                                                                            Need to get 6,448 B of archives.
                                                                                            
                                                                                            After this operation, 30.7 kB of additional disk space will be used.
                                                                                            
                                                                                            Get:1 http://us.archive.ubuntu.com/ubuntu bionic/main amd64 libaio1 amd64 0.3.110-5 [6,448 B]
                                                                                            
                                                                                            Fetched 6,448 B in 0s (53.2 kB/s)
                                                                                            
                                                                                            Selecting previously unselected package libaio1:amd64.
                                                                                            
                                                                                            (Reading database ... 170931 files and directories currently installed.)
                                                                                            
                                                                                            Preparing to unpack .../libaio1_0.3.110-5_amd64.deb ...
                                                                                            
                                                                                            Unpacking libaio1:amd64 (0.3.110-5) ...
                                                                                            
                                                                                            Processing triggers for libc-bin (2.27-3ubuntu1) ...
                                                                                            
                                                                                            Setting up libaio1:amd64 (0.3.110-5) ...
                                                                                            
                                                                                            Processing triggers for libc-bin (2.27-3ubuntu1) ...
                                                                                            
                                                                                            
                                                                                      2. Create directories to house virtual machines
                                                                                        1. /home/rlucente/lucente_robert/vmware/virtual_machines/<Virtual Machine Names>
                                                                                      3. Install VMware Workstation
                                                                                        1. Make bundle installer executable
                                                                                          rlucente@rlucente-Bonobo-WS:~/lucente_robert/vm_ware/work_station_pro_download$ chmod +x *bundle 
                                                                                          
                                                                                          
                                                                                          1. Determine options of bundle installer
                                                                                            1. Command & Output of Command
                                                                                              rlucente@rlucente-Bonobo-WS:~/lucente_robert/vm_ware/work_station_pro_download$ ./VMware-Workstation-Full-15.0.2-10952284.x86_64.bundle --help 
                                                                                              
                                                                                              Extracting VMware Installer...done.
                                                                                              
                                                                                              [AppLoader] Use shipped Linux kernel AIO access library.
                                                                                              
                                                                                              An up-to-date "libaio" or "libaio1" package from your system is preferred.
                                                                                              
                                                                                              Usage: vmware-installer [options]
                                                                                              
                                                                                              VMware Installer
                                                                                              
                                                                                              Options:
                                                                                              
                                                                                                --version             show program's version number and exit
                                                                                              
                                                                                                -h, --help            show this help message and exit
                                                                                              
                                                                                                Manage:
                                                                                              
                                                                                                  Install or uninstall products
                                                                                              
                                                                                                  -i FILE, --install-bundle=FILE
                                                                                              
                                                                                                                      Install bundle from FILE
                                                                                              
                                                                                                  --install-component=FILE
                                                                                              
                                                                                                                      Install a component
                                                                                              
                                                                                                  --uninstall-component=NAME
                                                                                              
                                                                                                                      Force uninstallation of a component
                                                                                              
                                                                                                  -u NAME, --uninstall-product=NAME
                                                                                              
                                                                                                                      Uninstall a product
                                                                                              
                                                                                                  -r, --resolve-system
                                                                                              
                                                                                                                      Force the system to resolve the current state
                                                                                              
                                                                                                  --register-file=COMPONENT_NAME (config|regular) FILE
                                                                                              
                                                                                                                      Register a file in the database
                                                                                              
                                                                                                  -x DIR, --extract=DIR
                                                                                              
                                                                                                                      Extract the contents of the bundle into DIR
                                                                                              
                                                                                                  -p DIR, --prefix=DIR
                                                                                              
                                                                                                                      Set a custom install location
                                                                                              
                                                                                                Information:
                                                                                              
                                                                                                  Look up information on installed products
                                                                                              
                                                                                                  -l, --list-products
                                                                                              
                                                                                                                      List installed products
                                                                                              
                                                                                                  -t, --list-components
                                                                                              
                                                                                                                      List the installed components
                                                                                              
                                                                                                  -L COMPONENT, --list-files=COMPONENT
                                                                                              
                                                                                                                      List files for a given component
                                                                                              
                                                                                                  -S FILE, --find-file=FILE
                                                                                              
                                                                                                                      List components and files matching the given pattern
                                                                                              
                                                                                                Settings:
                                                                                              
                                                                                                  Set and retrieve settings
                                                                                              
                                                                                                  -g COMPONENT KEY, --get-setting=COMPONENT KEY
                                                                                              
                                                                                                                      Get setting
                                                                                              
                                                                                                  -s COMPONENT KEY VALUE, --set-setting=COMPONENT KEY VALUE
                                                                                              
                                                                                                                      Set setting
                                                                                              
                                                                                                  -d COMPONENT KEY, --delete-setting=COMPONENT KEY
                                                                                              
                                                                                                                      Delete setting
                                                                                              
                                                                                                Options:
                                                                                              
                                                                                                  --gtk               Use the Gtk+ UI (Default)
                                                                                              
                                                                                                  --console           Use the console UI
                                                                                              
                                                                                                  --custom            Allow customization of the install, including file
                                                                                                                      locations.
                                                                                              
                                                                                                  --regular           Displays questions that have no good defaults
                                                                                                                      (Default)
                                                                                              
                                                                                                  --required          Displays only questions absolutely required
                                                                                              
                                                                                                  -I, --ignore-errors
                                                                                              
                                                                                                                      Ignore component script errors
                                                                                              
                                                                                                  --eulas-agreed      Agree to the EULA
                                                                                              
                                                                                              rlucente@rlucente-Bonobo-WS:~/lucente_robert/vm_ware/work_station_pro_download$ 
                                                                                              
                                                                                              
                                                                                            2. Start VMware Workstation installer in console (non-gui) and custom modes
                                                                                              1. Command: sudo ./VMware-Workstation-Full-15.0.2-10952284.x86_64.bundle --console --custom
                                                                                                1. Output of Command
                                                                                                  Extracting VMware Installer...done.
                                                                                                  
                                                                                                  
                                                                                                2. Question Before Intallation
                                                                                                  1. End User License Agreement
                                                                                                    1. Hit the letter "q" and type "yes" to the prompt that shows up
                                                                                                      Do you agree? [yes/no]: yes
                                                                                                      
                                                                                                      
                                                                                                    2. VMware OVF Tool component
                                                                                                      1. Screen Output
                                                                                                        You must accept the VMware OVF Tool component for Linux End User
                                                                                                        License Agreement to continue.  Press Enter to proceed.
                                                                                                        
                                                                                                        
                                                                                                        1. Hit the letter "q" and type "yes" to the prompt that shows up
                                                                                                          Do you agree? [yes/no]: yes
                                                                                                          
                                                                                                          
                                                                                                        2. Product installation path prefix
                                                                                                          1. Screen Output
                                                                                                            Product installation path prefix. (Desktop integration files, such as
                                                                                                            icons and .desktop entries, will still be installed under /usr.)
                                                                                                            [/usr]: 
                                                                                                            
                                                                                                            
                                                                                                          2. System service runlevel directory
                                                                                                            1. Screen Output
                                                                                                              System service runlevel directory (contains rc?.d directories).  Use
                                                                                                              an empty directory if your system does not support rc?.d style
                                                                                                              directories. [/etc]: 
                                                                                                              
                                                                                                              
                                                                                                            2. System service scripts directory
                                                                                                              1. Screen Output
                                                                                                                System service scripts directory (commonly /etc/init.d). [/etc/init.d]: 
                                                                                                                
                                                                                                                
                                                                                                              2. Shortcuts for desktop
                                                                                                                1. Screen Output
                                                                                                                  Do you want to install shortcuts for your desktop? [yes]: yes
                                                                                                                  
                                                                                                                  
                                                                                                                2. Check for product updates on startup?
                                                                                                                  1. Screen Output
                                                                                                                    Would you like to check for product updates on startup? [yes]: yes
                                                                                                                    
                                                                                                                    
                                                                                                                  2. Customer Experience Improvement Program ("CEIP")
                                                                                                                    1. Screen Output
                                                                                                                      VMware's Customer Experience Improvement Program ("CEIP") ... [yes]: yes
                                                                                                                      
                                                                                                                      
                                                                                                                    2. User that will initially connect to Workstation Server
                                                                                                                      1. Screen Output
                                                                                                                        Please enter the user that will initially connect to Workstation
                                                                                                                        Server. Without setting this correctly, you will not be able to share
                                                                                                                        VMs with other users. Additional users and administrators can be
                                                                                                                        configured later in Workstation by selecting "Shared VMs" and clicking
                                                                                                                        "Permissions".  [rlucente]: 
                                                                                                                        
                                                                                                                        
                                                                                                                      2. Directory for shared virtual machines
                                                                                                                        1. Screen Output
                                                                                                                          Please choose a directory for your shared virtual machines.
                                                                                                                          [/var/lib/vmware/Shared VMs]: 
                                                                                                                          
                                                                                                                          
                                                                                                                        2. Port for https access to Workstation Server
                                                                                                                          1. Screen Output
                                                                                                                            Please enter the port to use for https access to Workstation Server.
                                                                                                                            (HTTPS port:) [443]: 
                                                                                                                            
                                                                                                                            
                                                                                                                          2. License key
                                                                                                                            1. Screen Output
                                                                                                                              Enter license key. (optional) You can enter this information later.: xxx
                                                                                                                              
                                                                                                                              
                                                                                                                          3. Product ready to be installed / Begin Installation / Complete Installation
                                                                                                                            1. Screen Output
                                                                                                                              The product is ready to be installed.  Press Enter to begin
                                                                                                                              installation or Ctrl-C to cancel.
                                                                                                                              
                                                                                                                              Installing VMware VMX 15.0.2
                                                                                                                                  Configuring...
                                                                                                                              
                                                                                                                              Installing VMware Workstation 15.0.2
                                                                                                                                  Configuring...
                                                                                                                              [######################################################################] 100%
                                                                                                                              Installation was successful.
                                                                                                                              
                                                                                                                              
                                                                                                                          4. Start Workstation from a Terminal Window
                                                                                                                            1. Command: sudo vmware
                                                                                                                            2. Upgrade virtual machines from version 14 to 15
                                                                                                                              1. Upgrade VMware Tools on virtual machines

                                                                                                                                Saturday, December 22, 2018

                                                                                                                                Increasing Swap Size on Ubuntu

                                                                                                                                Some of the blogs on increasing swap size on Ubuntu provide incorrect information. For example, some blogs suggest using fallocate which will not work for certain file systems. For the details on which file systems fallocate does and does not work on, refer to the swapon/swapoff man page. We avoid this complexity by using dd.

                                                                                                                                There are many potential scenarios. In this particular case, swap is initially configured to 2 GB and we want to increase it to 16 GB.


                                                                                                                                All the commands below were executed on Ubuntu 18.04 LTS


                                                                                                                                1. Check Ubuntu version
                                                                                                                                  1. Command: lsb_release -a
                                                                                                                                    1. Output
                                                                                                                                      No LSB modules are available.
                                                                                                                                      Distributor ID: Ubuntu
                                                                                                                                      Description:    Ubuntu 18.04.1 LTS
                                                                                                                                      Release:        18.04
                                                                                                                                      Codename:       bionic
                                                                                                                                      
                                                                                                                                    2. Check swap configuration
                                                                                                                                      1. Command: sudo swapon --show --bytes
                                                                                                                                        1. Output
                                                                                                                                          NAME      TYPE       SIZE USED PRIO
                                                                                                                                          /swapfile file 2147479552    0   -2
                                                                                                                                          
                                                                                                                                          
                                                                                                                                          1. Comment
                                                                                                                                            1. The size of 2,147,479,552 bytes is close to but does not exactly correspond to 2 GB (2,147,483,648). The option "--bytes" was used to illustrate that there will be differences and that should not be surprised by this. The discrepancy is so small that you should not worry about it. An explanation for the discrepancy is beyond the scope of this blog.
                                                                                                                                          2. Examine the metadata of the swap file
                                                                                                                                            1. Command: ls -lh /swapfile
                                                                                                                                              1. Output
                                                                                                                                                -rw------- 1 root root 2.0G Oct 18 18:55 /swapfile
                                                                                                                                                
                                                                                                                                                1. Comments
                                                                                                                                                  1. Root should be the only user that can read/write to the swapfile. The above output confirms that this is happening.
                                                                                                                                                    1. Notice that the size of the swapfile is displayed as "2.0G".
                                                                                                                                                  2. Disable swap file
                                                                                                                                                    1. Command: sudo swapoff /swapfile
                                                                                                                                                      1. Output: None
                                                                                                                                                      2. Check swap configuration
                                                                                                                                                        1. Command: sudo swapon --show --bytes
                                                                                                                                                          1. Output: None
                                                                                                                                                          2. Add 14 GB to the existing swap file
                                                                                                                                                            1. Command: sudo dd if=/dev/zero of=/swapfile bs=1M count=14336 oflag=append conv=nocreat,notrunc,fsync status=progress
                                                                                                                                                            2. Output
                                                                                                                                                              14579400704 bytes (15 GB, 14 GiB) copied, 5 s, 2.9 GB/s 
                                                                                                                                                              14336+0 records in
                                                                                                                                                              14336+0 records out
                                                                                                                                                              15032385536 bytes (15 GB, 14 GiB) copied, 8.94244 s, 1.7 GB/s
                                                                                                                                                              
                                                                                                                                                              1. Comments
                                                                                                                                                                1. Since the number of input bytes is 1M, need the count to be 14,336 (14 * 1,024).
                                                                                                                                                                  1. Used fsync to make sure that physically wrote output file as well as its associated metadata before finishing.
                                                                                                                                                                2. Examine the metadata of the swap file
                                                                                                                                                                  1. Command: ls -lh /swapfile
                                                                                                                                                                    1. Output
                                                                                                                                                                      -rw------- 1 root root 16G Dec 22 15:44 /swapfile
                                                                                                                                                                      
                                                                                                                                                                      1. Comment
                                                                                                                                                                        1. Confirmed that the final swapfile size is 16 GB.
                                                                                                                                                                      2. Make swapfile usable
                                                                                                                                                                        1. Command: sudo mkswap --check /swapfile
                                                                                                                                                                          1. Output
                                                                                                                                                                            mkswap: warning: checking bad blocks from swap file is not supported: /swapfile
                                                                                                                                                                            mkswap: /swapfile: warning: wiping old swap signature.
                                                                                                                                                                            Setting up swapspace version 1, size = 16 GiB (17179865088 bytes)
                                                                                                                                                                            no label, UUID=9ec7f376-65ab-44f0-b40b-31b8e725f3b6
                                                                                                                                                                            
                                                                                                                                                                            1. Comments
                                                                                                                                                                              1. Don't worry about the warning "checking bad blocks from swap file is not supported". This is because the check can only be performed on block devices. In this particular case, the swapfile is not a block device.
                                                                                                                                                                                1. The size of 17,179,865,088 bytes is close to but does not exactly corresponds to 16 GB (17,179,869,184). As stated previously, the discrepancy is so small that you should not worry about it.
                                                                                                                                                                              2. Enable swaping
                                                                                                                                                                                1. Command: sudo swapon --verbose /swapfile
                                                                                                                                                                                  1. Output
                                                                                                                                                                                    swapon: /swapfile: found signature [pagesize=4096, signature=swap]
                                                                                                                                                                                    swapon: /swapfile: pagesize=4096, swapsize=17179869184, devsize=17179869184
                                                                                                                                                                                    
                                                                                                                                                                                    1. Comment
                                                                                                                                                                                      1. The size of 17,179,869,184 bytes corresponds exactly to to 16 GB.
                                                                                                                                                                                    2. Check swap configuration
                                                                                                                                                                                      1. Command: sudo swapon --show --bytes
                                                                                                                                                                                        1. Output
                                                                                                                                                                                          NAME      TYPE        SIZE USED PRIO
                                                                                                                                                                                          /swapfile file 17179865088    0   -2
                                                                                                                                                                                      2. Reboot
                                                                                                                                                                                        1. Check swap configuration
                                                                                                                                                                                          1. Command: sudo swapon --show --bytes
                                                                                                                                                                                            1. Output
                                                                                                                                                                                              NAME      TYPE        SIZE USED PRIO
                                                                                                                                                                                              /swapfile file 17179865088    0   -2
                                                                                                                                                                                              
                                                                                                                                                                                              1. Comment
                                                                                                                                                                                                1. The reboot verified that fstab is properly configured for the swapfile.

                                                                                                                                                                                            References


                                                                                                                                                                                            Sunday, October 21, 2018

                                                                                                                                                                                            Python: Iterators - Yield Statement - Generators - Comprehensions

                                                                                                                                                                                            The topics of iterators, yield statement, generators and comprehensions are of interest to anyone that uses for loops, nested for loops and is concerned about the compactness of their code and it's associated performance.

                                                                                                                                                                                            The purpose of this blog post is to provide a single spot where an introduction to iterators, the yield statement, generators and comprehensions can be found. The introductions will be made by first starting with a code snippet and then providing an explanation. For those that are interested in the corresponding Python documentation, a reference section is provided at the end.

                                                                                                                                                                                            Below is a pictorial of our journey.



                                                                                                                                                                                            All code below was executed using Python 3.7.0.

                                                                                                                                                                                            Add Sequence Numbers to a List


                                                                                                                                                                                            We are going to begin by adding a sequence number to the items in a list
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            >>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
                                                                                                                                                                                            
                                                                                                                                                                                            >>> list(enumerate(seasons,start=10))
                                                                                                                                                                                            [(10, 'Spring'), (11, 'Summer'), (12, 'Fall'), (13, 'Winter')]
                                                                                                                                                                                            
                                                                                                                                                                                            

                                                                                                                                                                                            Create Enumerate Equivalent Function Using Yield


                                                                                                                                                                                            An equivalent function for enumerate is provided below.
                                                                                                                                                                                            def enumerate_function( sequence, start = 0 ):
                                                                                                                                                                                                n = start
                                                                                                                                                                                                for element in sequence:
                                                                                                                                                                                                    yield n, element
                                                                                                                                                                                                    n += 1
                                                                                                                                                                                            Next, let's exercise the above function.
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            seasons = [ 'Spring', 'Summer', 'Fall', 'Winter' ]
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            for sequence_number, season in enumerate_function( seasons, start = 20 ):
                                                                                                                                                                                                print("The season ", season, " has been assigned a sequence number of ", sequence_number)
                                                                                                                                                                                            The generated output is:
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            The season  Spring  has been assigned a sequence number of  20
                                                                                                                                                                                            The season  Summer  has been assigned a sequence number of  21
                                                                                                                                                                                            The season  Fall  has been assigned a sequence number of  22
                                                                                                                                                                                            The season  Winter  has been assigned a sequence number of  23
                                                                                                                                                                                            Notice the yield statement in enumerate_function.

                                                                                                                                                                                            Let's examine the execution of the enumerate_function to determine what the yield statement is doing. The execution starts when the function is called and proceeds to the first yield statement. At that point in time, execution is suspended and the associated values are returned. During suspension, the full state of the function is retained. When the function is invoked once again, the execution of the function resumes immediately after the yield statement.

                                                                                                                                                                                            The above seems awfully complicated. You stop the execution of a function, you save its entire state, you return a value and you resume processing after the function is invoked once again. The cost of this added complexity is worth it because it reduces the amount of required memory. If the values are not generated as they are needed, they would have to be all created at the beginning and stored in memory. For large data sets, a large amount of memory would be required.

                                                                                                                                                                                            If a function has a yield statement, it is called a "generator function". It is important to differentiate between a "generator function" and a "normal function". In the former, the function can be executed many times; while the latter is executed only once.


                                                                                                                                                                                            For Illustrative Purposes, Replace For Loop with While Loop


                                                                                                                                                                                            The phrase "When the function is invoked once again" needs to be further explained. As usual, we are going to start with a code snippet. Specifically, we are going to replace the for loop in the enumerate_function with an infinite while loop helped by iter() and next().


                                                                                                                                                                                            Below is the updated code snippet.
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            def enumerate_function_rev_1( sequence, start = 0 ):
                                                                                                                                                                                                n = start
                                                                                                                                                                                                iterator = iter(sequence)
                                                                                                                                                                                                while True:
                                                                                                                                                                                                    try:
                                                                                                                                                                                                        yield n, next(iterator)
                                                                                                                                                                                                    except StopIteration:
                                                                                                                                                                                                        break
                                                                                                                                                                                                    n += 1
                                                                                                                                                                                            Also, as usual, we will explain the concepts after providing a code snippet. The iter() function returns an iterator object. The next() function retrieves the next item from the iterator. The infinite while loop continues to retrieve data until the StopIteration exception is raised.

                                                                                                                                                                                            We originally added complexity to a for loop by introducing the yield statement. Now we have added more complexity by adding the concept of an iterator with its supporting functions. We are doing this for illustrative purposes only.

                                                                                                                                                                                            Here, we are trying to illustrate that under the hood, the for loop uses an iterator. Also, it provided an opportunity to introduce the concept of an iterator. The remainder of the blog post will expand on this concept. Please note that you should continue to use the for loop and not replace it with an infinite while loop.

                                                                                                                                                                                            By the way, the use of iterator with its supporting functions is commonly referred to as the "iterator protocol".

                                                                                                                                                                                            Iterator Performance Benefit: Less Memory Consumed


                                                                                                                                                                                            Why bother with the concept of the iterator protocol? The one word answer: performance. Please note that the preceding code snippets were only used to demonstrate concepts and were not intended to address performance concerns.

                                                                                                                                                                                            Below is a code snippet to generate 10 million numbers. When dealing with big data, this is not an unreasonable number.
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            import sys
                                                                                                                                                                                            
                                                                                                                                                                                            a_list = [1 for i in range(10000000) ]
                                                                                                                                                                                            a_generator = (1 for i in range(10000000) )
                                                                                                                                                                                            
                                                                                                                                                                                            sys.getsizeof(a_list)      # 81,528,056 Bytes
                                                                                                                                                                                            sys.getsizeof(a_generator) #        120 Bytes

                                                                                                                                                                                            The list requires 78 MB while the generator only requires 120 bytes. This is 679,400 times as much. Allocating over 600,000 times as much memory and doing this many times during processing will quickly impact performance.

                                                                                                                                                                                            The following is a quote from the Python official documenation: The superior memory performance is kept by processing elements one at a time rather than bringing the whole iterable into memory all at once. Code volume is kept small by linking the tools together in a functional style which helps eliminate temporary variables. High speed is retained by preferring “vectorized” building blocks over the use of for-loops and generators which incur interpreter overhead.

                                                                                                                                                                                            The idea that code volume is kept small by linking the tools together in a functional style will be demonstrated below with the dot product computation. Vectorization is beyond the scope of this blog. However, if this is of interest to you, a good place to start is "Look Ma, No For-Loops: Array Programming With NumPy" by Brad Solomon.

                                                                                                                                                                                            Use Dot Product Computation to Demonstrate Iterator Protocol


                                                                                                                                                                                            We can use the computation of the dot product to demonstrate the above. If you have list/vector a = [1, 3, -5] and list/vector b = [4, -2, -1], the dot product is "(1 * 4) + (3 * -2) + (-5 * -1)" which is equal to 3.

                                                                                                                                                                                            Below is a code snippet to implement the dot product
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            import operator
                                                                                                                                                                                            
                                                                                                                                                                                            a = [1,  3, -5]
                                                                                                                                                                                            b = [4, -2, -1]
                                                                                                                                                                                            
                                                                                                                                                                                            sum( map( operator.mul, a, b ) )
                                                                                                                                                                                            The key to understanding the above code snippet is map(function, iterable, ...). In this case, the function being applied is multiplication via operator.mul(a, b). Iterable 1 is list a. Iterable 2 is list b. Notice how the concept of an iterator allows us to write one line of easy to understand code to compute the dot product. For the other benefits, please re-read the paragraph that starts with "Why bother with the concept of the iterator protocol?".

                                                                                                                                                                                            For those familiar with Excel, the above corresponds to the SUMPRODUCT function.

                                                                                                                                                                                            List Comprehension


                                                                                                                                                                                            No conversation of this type would be complete without mentioning list comprehensions because they also use the concept of an iterator. As an example, let's create a list of the square of the numbers 0 through 9 inclusive.
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            >>> [x**2 for x in range(10)]
                                                                                                                                                                                            [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
                                                                                                                                                                                            A more complex example of a list comprehension is to combine the elements of two lists only if they are not equal.
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            
                                                                                                                                                                                            >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
                                                                                                                                                                                            [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
                                                                                                                                                                                            Notice how (1,1) and (3,3) are not in the results. This arises from "if x != y".

                                                                                                                                                                                            There are also set and dictionary comprehensions. These two comprehensions are not conceptually that different from list comprehensions. Consequently, nothing more will be said about them in this blog post.

                                                                                                                                                                                            The thing to notice as you read the Python documentation is that a comprehension is a flavor of the special syntax called "displays". The following is a quote from the documentation:
                                                                                                                                                                                              1. either the container contents are listed explicitly, or
                                                                                                                                                                                                1. they are computed via a set of looping and filtering instructions, called a comprehension.

                                                                                                                                                                                              Summary


                                                                                                                                                                                              In summary, we started out adding a sequence number to a list by simply using Python's built-in enumerate function. We then explicitly implemented our own version of the enumerate function so that we could introduce the yield statement. The yield statement in turn led to the concept of the generator function.

                                                                                                                                                                                              Also, just like we decomposed the built in function enumerate, we also decomposed the for loop. We replaced the for loop with an infinite while loop helped by iter() and next(). This allowed us to introduce the concept of the iterator object. We then discussed the performance benefits of using iterator objects which was demonstrated by computing the dot product.

                                                                                                                                                                                              We closed by talking about list, set and dictionary comprehensions.