Showing posts with label Interview - Review. Show all posts
Showing posts with label Interview - Review. Show all posts

Tuesday, May 24, 2016

Design Snake game



https://discuss.leetcode.com/topic/110/design-snake-game
Design a Snake game on a device that has screen of width x height.
Initially the snake is at position (0,0) with length = 1 unit.
You are given a list of food's positions. When a snake eats the food, its length and the game's score both increase by 1.
Each food appears one after another. For example, the second food will not appear until the first food was eaten by the snake. Same with others.
class Snake {
    public Snake(int width, int height, List<int[2]> food) {

    }

    /**
     * @param direction U = Up, L = Left, R = Right, D = Down
     * @return The game's score after the move. Return -1 if game over.
     */
    public int move(char direction) {

    }
}
The list contains all food's positions. Initially the food appears at position food.get(0), only one food may appear at the screen at once. After the snake eats the first food, the second food will appear at position food.get(1), and the same for the third food and so on...

Each move controls the snake head's direction. So if moving the snake head in that direction causes the head to touch the snake's body, it results in game over.
It should be List<int[2]>. Each food is represent by an array of size 2, ary[0] = row, ary[1] = col.


Yes the food is given in the constructor. If the position of the food is occupied by the snake body then according to the rules, the snake's length and the game score immediately increase by one.
In real game you would generate randomly the next food's position, but I think you want a deterministic score after each move so it is easier to test.
valid move down
/###H
/###T
becomes
/####
/##TH
valid move up
/###T
/###H
becomes
/##TH
/####
valid move right
/ ####
/ ##HT
becomes
/ ###T
/ ###H
valid move left
/####
/TH##
becomes
/ T###
/ H###
invalid cross with screen borders
From my experience playing snake game, when the snake's head moves toward the food, the snake's tail does not move, which essentially grows the length by 1.
    public static class Node {
        int x;
        int y;
        Node pre;
        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    int len, foodIdx;
    Node[][] screen;
    List<int[][]> food;
    Node head;
    Node tail;
    boolean over;

    public Snake(int width, int height, List<int[][]> food) {
        this.food = food;
        screen = new Node[width + 1][height + 1];
        head = new Node(0, 0);
        screen[0][0] = head;
        tail = head;
        len = 1;
        foodIdx = 0;
        over = false;
    }

    /**
     * @param direction
     *            U = Up, L = Left, R = Right, D = Down
     * @return The game's score after the move. Return -1 if game over.
     */
    public int move(char direction) {
        int x = head.x;
        int y = head.y;

        if ('U' == direction) {
            y++;
        } else if ('L' == direction) {
            x--;
        } else if ('R' == direction) {
            x++;
        } else if ('D' == direction) {
            y--;
        }

        if (!check(x, y)) {
            over = true;
            return -1;
        }

        move(x, y);
        len += eat(x, y);
        return len - 1;
    }

    private int eat(int x, int y) {
        if (food != null && food.size() > foodIdx) {
            int[][] f = food.get(foodIdx);
            if (f[x][y] > 0) {
                foodIdx++;
                return 1;
            } else {
                // delete tail node
                Node tmp = tail;
                tail = tail.pre;
                screen[tmp.x][tmp.y] = null;
            }
        }
        return 0;
    }

    private boolean check(int x, int y) {
        return (!over && x >= 0 && y >= 0 && x < screen.length && y < screen[0].length && screen[x][y] == null);
    }

    private void move(int x, int y) {
        Node n = new Node(x, y);
        screen[x][y] = n;
        head.pre = n;
        head = n;
    }

    public void print() {
        int[][] f = null;
        if (food != null && food.size() > foodIdx) {
            f = food.get(foodIdx);
        }

        for (int i = 0; i < screen.length; i++) {
            for (int j = 0; j < screen[0].length; j++) {
                if (f!=null && f.length > i && f[0].length > j && f[i][j] > 0) {
                    System.out.print("2");
                } else {
                    if (screen[i][j] != null) {
                        System.out.print("1");
                    } else {
                        System.out.print("0");
                    }
                }
            }
            System.out.println();
        }

    }
I mean the matrix “screen”, it's like a Sparse Matrix . most of time the snake's size is far less than the matrix's size. I can use LinkedHashMap or LinkedHashSet store the snake.

X. Solution
public class Snake { 
 enum Direction {
  Up,
  Down,
  Right,
  Left
 } 
 
 private Queue<int[]> foodList;
 private LinkedList<int[]> body;
 private Set<Integer> bodyIndex;
 private int width;
 private int height; 
 private int score;
 
 public Snake(int w, int h , List<int[]> food) {
  foodList = new LinkedList<>(food);
  width = w;
  height = h;
  body = new LinkedList<>();
  bodyIndex = new HashSet<>();
  body.add(new int[] {0,0});
  bodyIndex.add(0);
 }
 
 private int[] moveToCell(Direction dir, int[] head) {  
  int res[]  = Arrays.copyOf(head, 2);
  switch (dir) {
   case Up : res[0]--; break; 
   case Down : res[0]++; break; 
   case Left : res[1]--; break; 
   case Right : res[1]++; break; 
  }
  return res;
 }
 
 int move(Direction dir) {
  if (foodList.isEmpty())
   return  score;
  else {
   int[] currFood = foodList.peek();
   int[] next = moveToCell(dir, body.getFirst()); 
   if ((next[0] == width) || (next[0] < 0) || (next[1] == height) || (next[1] < 0))
    return -1;
   int index = next[0] * width + next[1];
   if(bodyIndex.contains(index))
    return -1;
   if (next[0] != currFood[0] || next[1] != currFood[1]) {
    int[] tail = body.removeLast();
    bodyIndex.remove(tail[0] * width + tail[1]);    
   }
   else
   {
    foodList.remove(); 
    score++;  
   }
   body.addFirst(next);
   bodyIndex.add(index); 
      
   return score;
  }
 }
}
By the way I think the food should not appear in any block that is occupied by the Snake. To see why this is so, imagine the food appear at (0,0) right at where the Snake is initially positioned. Since the Snake eats the food by default, in which direction does the Snake's tail grow?
You are right, in this case there will be ambiguity in where the tail grows.

 When you eat the food, the tail does not move. Otherwise you check if it touches the body (not including the tail/head). Then update the snake by removing the tail and insert it into the head.
In my code forgot to check if my next step is tail
I can change function "move" to check if the snake moves in cell occupied by its body only when this cell is not occupied by food.
I also think that if the player avoid moving to snakes body cells, the game will return -1 only when the snake tail is so large as the whole grid

Saturday, March 12, 2016

How to Design Distributed Priority Queue



http://en.clouddesignpattern.org/index.php/CDP:Priority_Queue_Pattern
There are cases where a large number of batch jobs may need processing, and where the the jobs may need to be re-prioritized.

A queue is used in controlling batch jobs. The queue need only be provided with priority numbers. Job requests are controlled by the queue, and the job requests in the queue are processed by a batch server. In Cloud computing, a highly reliable queue is provided as a service, which you can use to structure a highly reliable batch system with ease. You may prepare multiple queues depending on priority levels, with job requests put into the queues depending on their priority levels, to apply prioritization to batch processes. 

  • You can increase or decrease the number of servers for processing jobs to change automatically the processing speeds of the priority queues and secondary queues.
  • You can handle performance and service requirements through merely increasing or decreasing the number of EC2 instances used in job processing.
  • Even if an EC2 were to fail, the messages (jobs) would remain in the queue service, enabling processing to be continued immediately upon recovery of the EC2 instance, producing a system that is robust to failure.

  • Use SQS to prepare multiple queues for the individual priority levels.
  • Place those processes to be executed immediately (job requests) in the high priority queue.
  • Prepare numbers of batch servers, for processing the job requests of the queues, depending on the priority levels.
  • Queues have a message "Delayed Send" function. You can use this to delay the time for starting a process.
https://www.quora.com/Distributed-Systems/How-to-implement-a-high-availability-priority-Queue-that-can-easily-scale
 create one SQS queue per priority level (assuming a fixed set of priorities). At that point you have a number of options of how to pop off of these queues. The algorithm that I settled on is as follows:

I issue N pop requests against the highest priority queue then M requests against the next highest queue (where M < N) and so on such that each queue gets fewer pop requests as the priority decreases. Since N can be quite large if at any point that particular queue is drained I short circuit the loop and go to the next lowest priority queue.

http://curator.apache.org/curator-recipes/distributed-priority-queue.html
queue.put(aMessage, priority);
https://cwiki.apache.org/confluence/display/CURATOR/TN4
ZooKeeper makes a very bad Queue source.
The ZooKeeper recipes page lists Queues as a possible use-case for ZooKeeper. Curator includes several Queue recipes. In our experience, however, it is a bad idea to use ZooKeeper as a Queue:
  • ZooKeeper has a 1MB transport limitation. In practice this means that ZNodes must be relatively small. Typically, queues can contain many thousands of messages.
  • ZooKeeper can slow down considerably on startup if there are many large ZNodes. This will be common if you are using ZooKeeper for queues. You will need to significantly increase initLimit and syncLimit.
  • If a ZNode gets too big it can be extremely difficult to clean. getChildren() will fail on the node. At Netflix we had to create a special-purpose program that had a huge value for jute.maxbuffer in order to get the nodes and delete them.
  • ZooKeeper can start to perform badly if there are many nodes with thousands of children.
  • The ZooKeeper database is kept entirely in memory. So, you can never have more messages than can fit in memory.

https://www.quora.com/Whats-a-good-distributed-queue

1. 如何设计一个priorityqueue service,client可以submit job request然后server按照priority执行
https://www.quora.com/Distributed-Systems-How-to-implement-a-high-availability-priority-Queue-that-can-easily-scale
Build something with Redis, which supports list operations. If you want to write your own List/Queue as API, go for it. Or if you want to use SQS, use it.
Do the replication yourself if you are unsure about your Queue unavailability.
Shard it yourself based on time, task queue name or priority. Let's go with the priority approach.
Step 2:
Here is where you can work out a variety of logical ways to dequeue:
Assign weights to each queue. At every dequeue step, add weights to each respective queue per unit time, and subtract one for each task you dequeue. To avoid starvation, use a round robin for each time you dequeue, and you shift to the next.
Return a set of tasks to each worker.
Step 3:


High availability priority queue that can scale

The trick I'm currently using is to create one SQS queue per priority level (assuming a fixed set of priorities). At that point you have a number of options of how to pop off of these queues. The algorithm that I settled on is as follows:

I issue N pop requests against the highest priority queue then M requests against the next highest queue (where M < N) and so on such that each queue gets fewer pop requests as the priority decreases. Since N can be quite large if at any point that particular queue is drained I short circuit the loop and go to the next lowest priority queue.


https://github.com/Netflix/curator/wiki/Distributed-Priority-Queue

Thursday, March 10, 2016

System Design - Review



Designs, Lessons and Advice from Building Large Distributed Systems
Make your apps do something reasonable even if not all is right
– Better to give users limited functionality than an error page

• Use higher priorities for interactive requests
Don't build infrastructure just for its own sake
• Identify common needs and address them
• Don't imagine unlikely potential needs that aren't really there

http://massivetechinterview.blogspot.com/2015/12/system-design-snake-netflix-now-now-now.html
Kilobit: data 数据设计, 不同数据的存储模型
  1. 比如用户服务可以用mysql, 查询逻辑强
  2. 电影文件就用文件存,不用数据库
1.1 Step 1. Clarify Requirements and Specs
1.2 Step 2. Sketch Out High Level Design
1.3.1 Load Balancer
1.3.2 Reverse Proxy

Stateless

MicroServices
The single responsibility principle advocates small and autonomous services that work together, so that each service can do one thing well and not block others

Service Discovery - Zookeeper
How do those services find each other? Zookeeper is a popular and centralized choice. Instances with name, address, port, etc. are registered into the path in ZooKeeper for each service. If one service does not know where to find another service, it can query Zookeeper for the location and memorize it until that location is unavailable.

Restful Web Services vs RPC
RPC is internally used by many tech companies for performance issues, but it is rather hard to debug and not flexible. So for public APIs, we tend to use HTTP APIs, and are usually following the RESTful style.
REST (Representational state transfer of resources)
Best practice of HTTP API to interact with resources.
URL only decides the location. Headers (Accept and Content-Type, etc.) decide the representation. HTTP methods(GET/POST/PUT/DELETE) decide the state transfer.
minimize the coupling between client and server (a huge number of HTTP infras on various clients, data-marshalling).
stateless and scaling out.
service partitioning feasible.
used for public API.

Data Tiers
do not save a blob, like a photo, into a relational database, and choose the right database for the right service. For example, read performance is important for follower service, therefore it makes sense to use a key-value cache. Feeds are generated as time passes by, so HBase / Cassandra’s timestamp index is a great fit for this use case. Users have relationships with other users or objects, so a relational database is our choice by default in an user profile

When we design a distributed system, trading off among CAP (consistency, availability, and partition tolerance) is almost the first thing we want to consider.
  • Consistency: all nodes see the same data at the same time
  • Availability: a guarantee that every request receives a response about whether it succeeded or failed
  • Partition tolerance: system continues to operate despite arbitrary message loss or failure of part of the system


Frameworks

Consistent Hashing
http://massivetechinterview.blogspot.com/2015/06/system-design-for-big-data-consistent.html

Redis

http://massivetechinterview.blogspot.com/2015/06/pragmatic-programming-techniques.html

Data partition
Data partitioning mechanism also need to take into considerations the data access pattern. Data that need to be accessed together should be staying in the same server. A more sophisticated approach can migrate data continuously according to data access pattern shift.

http://massivetechinterview.blogspot.com/2015/10/system-design-misc.html
  1. High Availability - Have some redundant nodes running in active/active mode.
  2. Use a load balancer if one server is not sufficient
  3. Try to put queuing systems for asynchronous consumption of offline loads.
Stateless
If servers cannot be made stateless, then the load-balancer must be made smarter.
It should route stateful requests to the proper server.
This is done by making the load balancer inspect the session-ID of each request and
matching that with the appropriate server.
Load balancer (Also called "Reverse Proxy")
Redundancy and tolerance to machine failures
Elastic load-balancers can shut-down some of the servers during non-peak hours.

http://massivetechinterview.blogspot.com/2015/06/flickrarchitecture-high-scalability.html
Test in production.
Be sensitive to the usage patterns for your type of application.
Be sensitive to the demands of exponential growth.

http://massivetechinterview.blogspot.com/2015/06/akfs-most-commonly-adopted.html
Design for Rollback
Design to Be Disabled
Design to Be Monitored
Isolate Faults
http://massivetechinterview.blogspot.com/2016/02/scalable-web-architectures-common.html
Three goals of application architecture:
Scale
HA
Performance
http://massivetechinterview.blogspot.com/2015/12/pinterest-building-scalableavailablesam.html

http://massivetechinterview.blogspot.com/2015/09/scalability-rules-50-principles-for.html

http://massivetechinterview.blogspot.com/2015/08/database-sharding-vs-partitioning.html
(user_id, update_timestamp)
The first part of that key is hashed to determine the partition, but the other columns are used as a concatenated index for sorting the data in Cassandra’s SSTables.
PARTITIONING SECONDARY INDEXES BY DOCUMENT
each partitions maintains its own secondary indexes, covering only the documents in that partition. It doesn’t care what data is stored in other partitions. a document-partitioned index is also known as a local index.

This approach to querying a partitioned database is sometimes known as scatter/gather

PARTITIONING SECONDARY INDEXES BY TERM
A global index must also be partitioned, but it can be partitioned differently from the primary key index.

The advantage of a global (term-partitioned) index over a document-partitioned index is that it can make reads more efficient: rather than doing scatter/gather over all partitions, a client only needs to make a request to the partition containing the term that it wants.
Practice
http://massivetechinterview.blogspot.com/2015/08/design-news-feed-system-medium.html

Get Second/Third Friends
http://massivetechinterview.blogspot.com/2015/12/linkedin-get-secondthird-friends.html

http://massivetechinterview.blogspot.com/2015/09/the-uber-software-architecture.html
http://highscalability.com/blog/2012/6/18/google-on-latency-tolerant-systems-making-a-predictable-whol.html
backup requests with cross server cancellation
A good solution is to have backup requests with cross server cancellation. This is baked-in to TChannel as a first class feature. A request is sent to Service B(1) along with the information that the request is also being sent to Service B(2). Then some delay later the request is sent to Service B(2). When B(1) completes the request it cancels the request on B(2). With the delay it means in the common case B(2) didn’t perform any work. But if B(1) does fail then B(2) will process the request and return a reply in a lower latency than if B(1) was tried first, a timeout occurred, and then B(2) is tried.

Separate failure detection from membership updates
• Do not rely on a single peer for failure detection

No data is replicated across data centers, as that puts a lot of constraints on availability and consistency. Uber uses the driver's phones to distribute the data. Given that the driver's phones post location updates to the server every four seconds, the server periodically replies with an encrypted state digest. If a data center fails the driver will contact a new data center to post a location update. The new data center doesn't know anything about this particular driver so it asks for the state digest and picks up from there.

http://massivetechinterview.blogspot.com/2015/10/logstash.html

Saturday, December 19, 2015

Linkedin - Get Second/Third Friends



https://engineering.linkedin.com/real-time-distributed-graph/using-set-cover-algorithm-optimize-query-latency-large-scale-distributed

Overview of Query Routing

LinkedIn's distributed graph system consists of three major subcomponents:
  1. GraphDB: a partitioned and replicated graph database.
  2. Network Cache Service (NCS): a distributed cache that stores a member's network and serves queries requiring second degree knowledge.
  3. API layer: the access point for front-ends.
A key challenge in a distributed graph system is that, when the graph is partitioned across a cluster of machines, multiple remote calls must occur during graph traversal operations. At LinkedIn, more than half the graph queries are for calculating distance between a member and a list of member IDs up to three degrees. In general, anywhere you see a distance icon on the LinkedIn site, there's a graph distance query at the backend.
NCS, the caching layer, calculates and stores a member's second-degree set. With this cache, graph distance queries originating from a member can be converted to set intersections, avoiding further remote calls. For example, if we have member X's second degree calculated, to decide whether member Y is three degree apart from member X, we can simply fetch Y's connections and intersect X's second degree cache with Y's first degree connections.
NCS, the caching layer, calculates and stores a member's second-degree set. With this cache, graph distance queries originating from a member can be converted to set intersections, avoiding further remote calls. For example, if we have member X's second degree calculated, to decide whether member Y is three degree apart from member X, we can simply fetch Y's connections and intersect X's second degree cache with Y's first degree connections.
As someone who was at LinkedIn as we scaled back most search results from including 3rd degree effects to only 1st and 2nd degree effects, I can attest to the computation intensive nature of this.  Currently LinkedIn will show you if a given individual is in your 3rd degree or not (this is easy, because each user's 1st and 2nd degree connections are typically cached somewhere, so this gives you out to the degree of connection between those users if it is 4th or less), but will *never* do something like "grab all of my third degree connections".

I will guess that most likely due to the size of Facebook's graph, it is simply not the case that both the 1st and 2nd degree of most users is kept in memory.  If only your friends are available, if my friends and yours don't overlap, checking whether we're 3rd, or 4th, requires some significant additional computation.


“已知一个函数,输入用户ID,可以返回该用户的所有友好(degree 1 friends),按好友ID从小到大排序。要求实现函数来输出返回一个用户的所有好友的好友(degree 2friends), 以及 degree 3 friends。

这题是想考什么呢?单是3级朋友,是不是BFS就可以解决了?是还要考distributedsystem怎样存取用户信息吗?

论文的关键好像是说,当需要在db搜索(不是cache)的时候,如何很快找到所有的set,这个就是apply set coverage algorithm,尽量争取在一台机器上找到,而不是多台机器。但是它后面有些优化,好像很简单的办法就可以降低latency,这部分没看懂。【 在 kkuser (kkuser!) 的大作中提到: 】
: 学习了下这个链接还看了下相关的paper,似乎有了一些概念。
: 貌似是说建立一个network cache service,里面存有所有member的一层和二层的关系: ,这样的话,求一个member的三层关系,只需要one remote call for each 2nd
: connector。不知道这样理解对不对。
: 另外有个疑问,这个NCS,是个只用其memory的cluster吗?这个cluster可以装的下所: 有用户以及每个用户所有的二层关系吗?
: 一个machine没什么, 像FB那样的, gazillion machine来存你的ID-》 friend
list
: 信息, 你怎没办? machine 之间传来床去?
: bandwidth怎没办? 如果一个user, 像justin bieber那样, 有gazillion个
friends
: , 你怎没办?

Operating System Misc



http://www.zrzahid.com/process-threads-and-synchronization/
Given n, print numbers from zero to n sequentially using two threads such that one thread prints only odds and other prints only evens.
Process
Process is an execution stream in the context of a particular process state. By Execution Stream we mean a sequence of instructions executed sequentially i.e. only one thing happens at a time. By Process State we mean everything that can affect, or be affected by, the process: code, data, call stack, open files, network connections, etc.
A process has a self-contained execution environment. A process generally has a complete, private set of basic run-time resources e.g. process has its own memory space. Process memory is divided into four sections as shown in Figure below:
c.01_revised.eps
  • Text Section : contains compiled program code loaded when program is launched.
  • Data Section : contains global and static variables, allocated and initialized prior to executing main
  • Stack : contains local variables, function call frames. It starts from higher memory address and shrink down as elements are added.
  • Heap : used for dynamic memory allocation using for example, new, delete, malloc, free, etc.
Note that the stack and the heap start at opposite ends of the process’s free space and grow towards each other. If they should ever meet, then either a stack overflow error will occur, or else a call to new or malloc will fail due to insufficient memory available.
Thread
Thread is unit of sequential execution. In other words, Threads are multiple execution streams within a single process. Threads share process state such as memory, open files, etc. Each thread has a separate stack for procedure calls (in shared memory).
Threads are sometimes called lightweight processes. Both processes and threads provide an execution environment, but creating a new thread requires fewer resources than creating a new process.
Threads exist within a process — every process has at least one. Threads share the process’s resources, including memory and open files. This makes for efficient, but potentially problematic, communication.
Process vs Threads
A process is an execution environment provided by the operating system that has its own set of private resources such as memory, open files, etc. On the other hand, Threads live within a process and share its resources : memory, open files, etc.
A process runs independently and isolated of other processes. It cannot directly access shared data in other processes. The resources of the process, e.g. memory and CPU time, are allocated to it via the operating system.
A thread is a so called lightweight process. It has its own call stack, but can access shared data of other threads in the same process. Every thread has its own memory cache. If a thread reads shared data it stores this data in its own memory cache. A thread can re-read the shared data.
For example, a Java application runs by default in one process. Within a Java application you work with several threads to achieve parallel processing or asynchronous behavior.
Process vs Program
A program by itself is not a process. It is a static entity made up of program statement while process is a dynamic entity. Program contains the instructions to be executed by processor.
A program takes a space at single place in main memory and continues to stay there. A program does not perform any action by itself.
How Process Works?
A process lifecycle goes through some states as described in the following figure –
d.01.eps
When processes are swapped out of memory and later restored, additional information must also be stored and restored. Key among them are the program counter and the value of all program registers. kernel uses a Process Control Block (PCB) to keep track of processes as showed in following figure –
d.02.eps
  • Process State : Running, waiting, etc.
  • PIDs : Process ID, and parent process ID.
  • CPU registers and Program Counter : Information to restore swapped process out of memory.
  • Memory-Management information : page tables or segment tables.
  • I/O Status information : Devices allocated, open file tables, etc.
  • Accounting information : user and kernel CPU time consumed, account numbers, limits, etc.
The dispatcher/scheduler is the one of the most important component (a kernel process itself) in OS that runs processes/threads. it does time slicing of the processor and assign one single thread to be in running state. Then it swaps this process by saving its state and load states of another process/thread from the scheduling queue based on a scheduling criteria or algorithm such as FIFO, Round Robin, or Priority based scheduling etc and run it. This incident is known as Context Switch i.e. changing the thread currently running on a CPU by first saving the state of the old process, then loading the state of the new process.
Note that, only one process can be running on a cpu at a particular time slice of the processor. So, if a thread is executing, scheduler isn’t running. So, question is how scheduler will run again to take control and schedule next process? Answer is Traps and Interrupts.
Traps are events occurring in current thread that cause a change of control into the operating system. There are 3 ways to trap OS –
  • System call.
  • Error (illegal instruction, addressing violation, etc.).
  • Page fault.
Interrupts are events occurring outside the current thread that cause a state switch into the operating system, for example completion of am I/O, expiration of a timer, keyboard events etc. The context switch process can be described pictorially as follows –
d.03.eps
Process Creation
  • Load code and data into memory.
  • Create and initialize process control block.
  • Create first thread with call stack.
  • Provide initial values for “saved state” for the thread
  • Make process known to dispatcher; dispatcher “resumes” to beginning of process.
IPC : Inter Process Communication
Processes are often seen as synonymous with programs or applications. However, what the user sees as a single application may in fact be a set of cooperating processes. To facilitate communication between processes, most operating systems support Inter Process Communication (IPC) resources, such as message queues, pipes and sockets. IPC is used not just for communication between processes on the same system, but processes on different systems.
Print
Read more in here.
Concurrency and Synchronization
When multiple threads access shared resources such that some of them modify a resource and others are reading that resource then we will face all sorts of data consistency problem due to synchronization issues among threads. For example, if thread A reads shared data which is later changed by thread B and thread A is unaware of this change. Let’s first define some terms before jumping into details –
  • Synchronization : using atomic operations to ensure correct operation of cooperating threads.
  • Critical section : a section of code, or collection of operations, in which only one thread may be executing at a given time. E.g. shopping.
  • Mutual exclusion : mechanisms used to create critical sections (ensure that only one thread is doing certain things at a given time).
However, synchronization can introduce thread contention, which occurs when two or more threads try to access the same resource simultaneously and cause the Java runtime to execute one or more threads more slowly, or even suspend their execution. Starvation and livelock are forms of thread contention.
  • Starvation : Starvation describes a situation where a thread is unable to gain regular access to shared resources and is unable to make progress. This happens when shared resources are made unavailable for long periods by “greedy” threads. For example, suppose an object provides a synchronized method that often takes a long time to return. If one thread invokes this method frequently, other threads that also need frequent synchronized access to the same object will often be blocked.
  • Livelock : A thread often acts in response to the action of another thread. If the other thread’s action is also a response to the action of another thread, then livelock may result. As with deadlock, livelocked threads are unable to make further progress. However, the threads are not blocked — they are simply too busy responding to each other to resume work. This is comparable to two people attempting to pass each other in a corridor: Alphonse moves to his left to let Gaston pass, while Gaston moves to his right to let Alphonse pass. Seeing that they are still blocking each other, Alphone moves to his right, while Gaston moves to his left. They’re still blocking each other.
Typically, mutual exclusion achieved with a locking mechanism: prevent others from doing something. For example, before shopping, leave a note on the refrigerator: don’t shop if there is a note. We can lock an object that can only be owned by a single thread at any given time. Basic operations on a lock:
  • Acquire: mark the lock as owned by the current thread; if some other thread already owns the lock then first wait until the lock is free. Lock typically includes a queue to keep track of multiple waiting threads.
  • Release: mark the lock as free (it must currently be owned by the calling thread).
Synchronization mechanisms need more than just mutual exclusion; also need a way to wait for another thread to do something (e.g., wait for a character to be added to the buffer). We can achieve this by using Condition variables.
Condition variables are used to wait for a particular condition to become true (e.g. characters in buffer).
  • _wait(condition, lock): release lock, put thread to sleep until condition is signaled; when thread wakes up again, re-acquire lock before returning.
  • signal(condition, lock): if any threads are waiting on condition, wake up one of them. Caller must hold lock, which must be the same as the lock used in the wait call.
  • broadcast(condition, lock): same as signal, except wake up all waiting threads.
Note: after signal, signaling thread keeps lock, waking thread goes on the queue waiting for the lock.
Warning: when a thread wakes up after condition_wait there is no guarantee that the desired condition still exists: another thread might have snuck in.
When locks and condition variables are used together like this, the result is called aMonitor. Monitor is a collection of procedures manipulating a shared data structure.
  • One lock that must be held whenever accessing the shared data (typically each procedure acquires the lock at the very beginning and releases the lock before returning).
  • One or more condition variables used for waiting.
Semaphore vs Mutex
Mutex is a key to a toilet. One person can have the key – occupy the toilet – at the time. When finished, the person gives (frees) the key to the next person in the queue.
Formally, mutexes are typically used to serialise access to a section of re-entrant code that cannot be executed concurrently by more than one thread. A mutex object only allows one thread into a controlled section, forcing other threads which attempt to gain access to that section to wait until the first thread has exited from that section.
Semaphore is the number of free identical toilet keys. Example, say we have four toilets with identical locks and keys. The semaphore count – the count of keys – is set to 4 at beginning (all four toilets are free), then the count value is decremented as people are coming in. If all toilets are full, ie. there are no free keys left, the semaphore count is 0. Now, when eq. one person leaves the toilet, semaphore is increased to 1 (one free key), and given to the next person in the queue.
Formally, a semaphore restricts the number of simultaneous users of a shared resource up to a maximum number. Threads can request access to the resource (decrementing the semaphore), and can signal that they have finished using the resource (incrementing the semaphore).
Implement Locks
Uniprocessor solution –
  • Acquire Lock : disable interrupts.
  • put thread to sleep if lock not available:
  • Hardware provides some sort of atomic read-modify-write instruction, which can be used to build higher-level synchronization operations such as locks. For example, aswap: atomically read memory value and replace it with a given value: returns old value.
struct lock {
    int locked;
    struct queue q;
    int sync;         /* Normally 0. */
};

void lock_acquire(struct lock *l) {
    Disable interrupts;
    while (aswap(&l->sync, 1) {
        /* Do nothing */
    }
    if (!l->locked) {
        l->locked = 1;
        l->sync = 0;
    } else {
        queue_add(&l->q, thread_current());
        l->sync = 0;
        thread_block();
    }
    Enable interrupts;
}

void lock_release(struct lock *l) {
    Disable interrupts;
    while (aswap(&l->sync, 1) {
        /* Do nothing */
    }
    if (queue_empty(&l->q) {
        l->locked = 0;
    } else {
        thread_unblock(queue_remove(&l->q));
    }
    l->sync = 0;
    Enable interrupts;
}
Threads and Concurrency in Java
In the Java virtual machine, every object and class is logically associated with a monitor. To implement the mutual exclusion capability of monitors, a lock (sometimes called a mutex) is associated with each object and class. This is called a semaphore.
If one thread owns a lock on some data, then no others can obtain that lock until the thread that owns the lock releases it. It would be not convenient if we need to write a semaphore all the time when we do multi-threading programming. Luckily, we don’t need to since JVM does that for us automatically.
To claim a monitor region which means data not accessible by more than one thread, Java provide synchronized statements and synchronized methods. Once the code is embedded with synchronized keyword, it is a monitor region. The locks are implemented in the background automatically by JVM.
Each object/class is associated with a Monitor. To enable collaboration of different threads, Java provide wait() and notify() to suspend a thread and to wake up another thread that are waiting on the object respectively. These methods can only be invoked within a synchronized statement or synchronized method. The reason is that if a method does not require mutual exclusion, there is no need to monitor or collaborate between threads, every thread can access that method freely.
The synchronized keyword in Java ensures:
  • that only a single thread can execute a block of code at the same time
  • that each thread entering a synchronized block of code sees the effects of all previous modifications that were guarded by the same lock
Synchronization is necessary for mutually exclusive access to blocks of and for reliable communication between threads.
We can use the synchronized keyword for the definition of a method. This would ensure that only one thread can enter this method at the same time. Another threads which is calling this method would wait until the first threads leaves this method.
public synchronized void method() {
  //critical codes
} 
We can also use the synchronized keyword to protect blocks of code within a method. This block is guarded by a key, which can be either a string or an object. This key is called the lock. All code which is protected by the same lock can only be executed by one thread at the same time
public void method() {
  //un-critical codes

   synchronized (this) {
     //wait for resource
     //critical codes
   }
   //notify all

   //un-critical codes
} 
notify() vs notifyAll()
  • wait() tells the calling thread to give up the monitor and go to sleep until some other thread enters the same monitor and calls notify( ).
  • notify() wakes up the first thread that called wait() on the same object.
In some cases, all waiting threads can take useful action once the wait finishes. An example would be a set of threads waiting for a certain task to finish; once the task has finished, all waiting threads can continue with their business. In such a case you would use notifyAll() to wake up all waiting threads at the same time.
Another case, for example mutually exclusive locking, only one of the waiting threads can do something useful after being notified (in this case acquire the lock). In such a case, you would rather use notify(). Properly implemented, you could use notifyAll() in this situation as well, but you would unnecessarily wake threads that can’t do anything anyway.
Thread vs Runnable
There are two way to create threads in Java – implementing a Runnable interface or by extending Thread class.
1. Provide a Runnable object. The Runnable interface defines a single method, run, meant to contain the code executed in the thread. The Runnable object is passed to the Thread constructor, as in the HelloRunnable example:
public class HelloRunnable implements Runnable {

    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }

}
2. Subclass Thread. The Thread class itself implements Runnable, though its run method does nothing. An application can subclass Thread, providing its own implementation of run, as in the HelloThread example:
public class HelloThread extends Thread {

    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new HelloThread()).start();
    }

}
Which one to use when?
  • Java doesn’t support multiple inheritance, which means we can only extend one class in Java. So if we extended Thread class then we can’t extend or inherit another class. So in situations where a child class in a hierarchy needs to be run as thread then we need to implement Runnable interface.
  • If we are not making any modification on Thread than use Runnable interface instead.
  • Runnable interface represent a Task which can be executed by either plain Thread or Executors or any other means. so logical separation of Task as Runnable than Thread is good design decision.
  • Separating task as Runnable means we can reuse the task and also has liberty to execute it from different means. since you can not restart a Thread once it completes. again Runnable vs Thread for task, Runnable is winner.
  • Java designer recognizes this and that’s why Executors accept Runnable as Task and they have worker thread which executes those task.
  • Inheriting all Thread methods are additional overhead just for representing a Task which can can be done easily with Runnable.
Interrupts and Join
An interrupt is an indication to a thread that it should stop what it is doing and do something else. It’s up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate. This is the usage emphasized in this lesson.
A thread sends an interrupt by invoking interrupt on the Thread object for the thread to be interrupted. For the interrupt mechanism to work correctly, the interrupted thread must support its own interruption. In java we can catch InterruptedException to catch the interrupt –
for (int i = 0; i < importantInfo.length; i++) {
    // Pause for 4 seconds
    try {
        Thread.sleep(4000);
    } catch (InterruptedException e) {
        // We've been interrupted: no more messages.
        return;
    }
    // Print a message
    System.out.println(importantInfo[i]);
}
Many methods that throw InterruptedException, such as sleep, are designed to cancel their current operation and return immediately when an interrupt is received.
What if a thread goes a long time without invoking a method that throws InterruptedException? Then it must periodically invoke Thread.interrupted, which returns true if an interrupt has been received.
for (int i = 0; i < inputs.length; i++) {
    heavyCrunch(inputs[i]);
    if (Thread.interrupted()) {
        // We've been interrupted: no more crunching.
        throw new InterruptedException();
    }
}
The interrupt mechanism is implemented using an internal flag known as the interrupt status. Invoking Thread.interrupt sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted, interrupt status is cleared. The non-static isInterrupted method, which is used by one thread to query the interrupt status of another, does not change the interrupt status flag.
By convention, any method that exits by throwing an InterruptedException clears interrupt status when it does so. However, it's always possible that interrupt status will immediately be set again, by another thread invoking interrupt.
The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,
t.join();
causes the current thread to pause execution until t's thread terminates. Overloads of join allow the programmer to specify a waiting period. However, as with sleep, join is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.
Like sleep, join responds to an interrupt by exiting with an InterruptedException.
Volatile and Immutable
If a variable is declared with the volatile keyword then it is guaranteed that any thread that reads the field will see the most recently written value. The volatile keyword will not perform any mutual exclusive lock on the variable.
Another simplest way to avoid problems with concurrency is to share only immutabledata between threads. Immutable data is data which cannot changed. In order to make a class immutable we need to design the class such that -
  • all its fields are final
  • the class is declared as final
  • this reference is not allowed to be used during construction
  • any fields which refer to mutable data objects are private
  • it doesn't have no setter method
  • they are never directly returned of otherwise exposed to a caller
  • if they are changed internally in the class this change is not visible and has no effect outside of the class
An immutable class may have some mutable data which is uses to manages its state but from the outside this class nor any attribute of this class can get changed.
For all mutable fields, e.g. Arrays, that are passed from the outside to the class during the construction phase, the class needs to make a defensive-copy of the elements to make sure that no other object from the outside still can change the data.
https://dzone.com/articles/performance-impact-io
影响IO密集型应用性能的因素
In a typical scenario, when data is written to a file, it is first written to a memory area reserved as the page cache. The page holding the newly written data is considered dirty. After a period of time per the kernel IO policy, the kernel flushes the dirty data to the device queue to persist to hard disk. Once the data gets to the queue, the rest is mechanical: The device driver reads the IO requests, then spins, seeks and writes to the physical disk block where the file is. The journal file is written first if enabled, then the actual file.
In a recent discussion with a few other engineers, an idea of disabling file system journaling came up to improve disk write latency. While this is certainly true because it is one less disk operation per write, the actual time gained is negligible because the journal file is in the same block as the file to be written. The benefits of having the journal file to restore the disk from a crash far outweighs the little latency saved.
The scenario gets more complicated when the JVM’s garbage collector is in the middle of cleaning the heap and is caught in the kernel-busily-flushing moment. Some GC events are stop-the-world (STW) event that require all the application threads to pause in order to reach a safe state such that the objects can be moved around in the heap. If one of application threads is trying to write while the kernel is busy flushing, this thread will get stuck behind the flushing job and won’t be able to respond to the summon to pause. This would cause a ripple effect: The busy disk blocks the write thread, the thread prolonged the GC pause, and the pause makes the application appear non-responsive. This problem can be identified in the GC log when there is long STW pause with little CPU time spent in “usr” and “sys”, correlated with disk activity.

At the device configuration level, storing the frequently accessed files on different devices could also help to avoid the problem of a single congested device queue. Alternatively, if applicable, having multiple sets of RAID1 would yield more device queues, which is better than having a single volume with all the disks which only has one device queue.
If cost is not prohibitive, consider upgrading to SSD which has 6 to 7 times the bandwidth of the spinning disk. The caveats for SSD, however, is its occasional needs to do data compaction. The  of data compaction could be bad.
always avoid unnecessary disk access.

http://www.geeksforgeeks.org/what-exactly-spooling-is-all-about/
SPOOL is an acronym for simultaneous peripheral operations on-line.  It is a kind of buffering mechanism or a process in which data is temporarily held to be used and executed by a device, program or the system. Data is sent to and stored in memory or other volatile storage until the program or computer requests it for execution.
Spooling works like a typical request queue where data, instructions and processes from multiple sources are accumulated for execution later on. Generally, it is maintained on computer’s physical memory, buffers or the I/O device-specific interrupts. The spool is processed in FIFO manner i.e. whatever first instruction is there in the queue will be popped and executed.

1) The most common can be found in I/O devices like keyboard printers and mouse. For example, In printer, the documents/files that are sent to the printer are first stored in the memory or the printer spooler. Once the printer is ready, it fetches the data from the spool and prints it.
Even experienced a situation when suddenly for some seconds your mouse or keyboard stops working? Meanwhile, we usually click again and again here and there on the screen to check if its working or not. When it actually starts working, what and wherever we pressed during its hang state gets executed very fast because all the instructions got stored in the respective device’s spool.
2) A batch processing system uses spooling to maintain a queue of ready-to-run jobs which can be started as soon as the system has the resources to process them.
3) Spooling is capable of overlapping I/O operation for one job with processor operations for another job. i.e. multiple processes can write documents to a print queue without waiting and resume with their work.
4) E-mail: an email is delivered by a MTA (Mail Transfer Agent) to a temporary storage area where it waits to be picked up by the MA (Mail User Agent)

http://www.geeksforgeeks.org/sleep-mode-vs-hibernate-mode-computer/
Sleep
  • RAM status: When the computer is turned into sleep mode, it saves the current context in the RAM and it makes sure that the RAM is on. So it consumes less power to keep the RAM powered on. It takes less time (two seconds) to on the system by simply pressing power button and we return to the unsaved work.
  • Time Duration: Sleep mode is useful if you want to stop working for a short period of time.
Hibernate
  • RAM status: Hibernate is similar to shutting down the computer. It saves the context in the hard drive and as soon as the computer is on, it loads the data from hard drive to the RAM and so it takes more time than sleep but less than the time taken to on the computer when it was shutdown. It do not consume any power because there is no need to power on the RAM.
  • Time duration: Prefer using this mode if you won’t be using your system for an extended period of time. The settings can be changed in the system, such that the system turns into Hibernate mode or sleep mode when the power button is pressed.

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts