September 13th,2023
Today, I have learned
1. Distributed and Asynchronous Logging
I have observed several times that whenever you print something to console (stdout) in Python or any other program, the duration of the program increases. If you are writing lots of stuff rapidly to screen (print or puts), the program slows. This is because when we write something to console, we wait until it is written, but if you write that to a file, you don't actually wait for the line to be written to file. You call write() and forget the response. The kernel takes care of writing that to the file and it does that efficiently by combining all of the logs into big chunks
But even that is an inefficient approach. The better approach is to use a proper logging framework like Log4J(Java) or logging module(python) which spawns a new thread and passes the logging info to that new thread which does the heavy lifting of writing that to a disk or to a socket. This enables the main thread to continue execution on its own without waiting for the logger thread
2. Concurrency vs Parallelism
Concurrency is context-switching when the interpreter/compiler encounters a blocking call while Parallelism is running two instances of the same code at the exact same time. If you are using a single core, you have only one process and one interpreter thus by and you can only have concurrency then. Parallelism is only achieved when you have multiple cores.
3. Python Concurrency/Parallelism Model (Threads and Processes) Threads share a common memory. Processes don't. In Python, each process has its own interpreter and memory space. It is illegal for one process to access the memory space of another process. So you need to copy data between them which induces latency. Intra-thread communication is done by the sender placing messages on a queue and the receiver accessing them. Use multithreading when you need to do concurrent I/O. Use multiprocessing when you need to do complicated concurrent CPU tasks In Python, true multithreading is impossible because of GIL(Global Interpreter Lock). This lock prevents the interpreter from running two threads concurrently. So you have to resort to multiprocessing in Python. However, I/O calls and libraries that call C(pandas, numpy, Dask) do not lock the interpreter, so you can have concurrency in those cases.
Copy_on_Write is basically sharing the same memory space between two processes until one process starts writing something into memory at which point the memory is split and assigned to each of the processes
4. WSGI
Puma/GUnicorn are Web-Servers. Rails/Django are web application frameworks. The webservers act as reverse proxy/interface between load balancers and Application Frameworks. The number of requests served concurrently by the web server is determined by no_of_workers * no_of_threads. These two are configurable in Web Servers.