Showing posts with label Optimization. Show all posts
Showing posts with label Optimization. Show all posts

Friday, December 11, 2015

Web Site Optimization



http://blog.me115.com/2013/01/276
    进行优化前,关键是剖析当前的web性能,找到性能瓶颈,从而确定最需改进的地方;如果精力有限,首先将精力放在能明显提升性能的改进点上;
高性能网站建设指南》提出了一个性能黄金法则:
只有10%-20%的最终用户响应时间花在了下载HTML文档上;其余的80%-90%的时间花在了下载页面中的所有组件上。

第一步:后台优化,启用页面缓存;
实验站点首页后台逻辑并不复杂,不超过10个Sql查询,通过查看时间线,本站在获取HTML文档时,花费的时间不到总响应时间的20%,优化之前没有使用缓存,所有的数据都是从数据库读取,这里,我们使用静态页面缓存,将首页整个页面完全的存放在缓存中(关于YII静态页面缓存的使用,参考这里);
第二步,DNS域名解析加速:
DNS解析是用户访问站点的第一步,在此之前,你的网站无法做任何事情;
站点的DNS解析时间不应该超过500ms,如果站点原始DNS解析时间过长,就该考虑考虑使用第三方解析加速服务;

第三步:使用CDN加速;
采用第三方CDN加速,时间缩短到2.1s;从下图中看到主要的耗时在于并行下载的个数有些低,如果能够提升并行下载量的个数,那么整体加载时间就会降低;
注:个人建议,启用CDN最好放在最后一步,等将站点本身的优化都做完了之后,再启用CDN可以明显的看到优化效果。(开启CDN后,由于有CDN缓存的原因,观测站点的本身的优化就不是很方便了);

第四步,采用多台服务器提高并行加载量:
原理:一个浏览器对与同一域名的并行下载的个数默认是2个, HTTP.1.0中规定的是4个。这样,我们可以使用不同的域名来提升下载的速度;
观察上图中的下载数量,第一次并行下载的个数是4个,初始认为是浏览器对于同一个域名来源的下载所限导致;于是考虑将部分静态文件分别放在不同的服务器上;通过把css和js放在不同服务器上;结果并不理想,发现并未提高速度。
想到在哪曾看到过,浏览器必须得把放在页头的css和js下载完成了之后才会开始下载其它的静态组件;

第五步,合并脚本和样式表;
    本站首页使用了2个js和3个css。如果采用朴素复制的方式,将js和css都分别整合到一个文件中,不但操作麻烦,而且不方便后期的管理。网络上有不少合并的工具,本站采用了CSS和JS合并优化工具-minify(下载地址:http://code.google.com/p/minify/)。如果使用的YII框架,更有YII整合版(minscript Extension),简单几步的配置,就自动将页面所有的js和css文件合并;

第六步,压缩css/js/html/xml;
不同的web服务器设置方式有所差别,本站使用的Linux/apache,
在web根目录下的.htaccess文件中添加以下代码即可:
#set compress
<ifmodule mod_deflate.c>
AddOutputFilter DEFLATE html xml php js css
</ifmodule>

第六步,最大化的减少HTTP请求;
添加Expires头, 启用静态内容缓存,将jpg、gif等文件缓存;

image
附上本书的目录:
绪言A:前端性能的重要性
第1章:规则1——减少HTTP请求
第2章:规则2——使用内容发布网络
第3章:规则3——添加Expires头
第4章:规则4——压缩组件
第5章:规则5——将样式表放在顶部
第6章:规则6——将脚本放在底部
第7章:规则7——避免CSS表达式
第8章:规则8——使用外部JavaScript和CSS
第9章:规则9——减少DNS查找
第10章:规则10——精简JavaScript
第11章:规则11——避免重定向
第12章:规则12——移除重复脚本
第13章:规则13——配置ETag
第14章:规则14——使AjaX可缓存
第15章:析构十大网站
页面大小、响应时间、YSlow等级
http://blog.me115.com/2014/04/525

流量超过1Gbps

超过1Gbps时,这就达到PC路由器的极限;
Hatena使用的标准硬件,实测结果表明,其界限大致是30万包/秒;按照平均包长度为300字节换算,也就是1Gbps;而千兆以太网的界限也是1Gbps,从内核性能上来看,性能才极限也是30万包/秒;
对策:使用多个PC路由器 / 或是使用成品路由器(cisco)

同一子网超过500台主机

将500台以上主机放在一个子网内,就会出现许多问题,丢包现象频繁;
500台主机的极限,具体说是交换机的ARP表到极限;
同时,广播包的流量导致丢包;
在同一子网内放置大量主机的话,广播包也会逐渐增加,而接收广播包就会消耗CPU资源;
eg:极端情况:在主机过多的子网内,插上网线,就能观察到CPU负载上升;
对策:
网络架构的层次化:
三层架构:最小的为访问层(access area)【100-200台】、上面为分发层(Distribution Area)【1000台】、最上面为核心层(core Area)或OSPF(Open shortest path first)【全体几万台左右】

全球化 – 一个数据中心的极限

当站点具有足够的影响力时,用户就来自全球各地;跨太平洋的访问,额外的开销是巨大的;这时,一个数据中心就成为了瓶颈;
对策:使用CDN、推荐Amazon cloudfront服务器;将访问频率高的文件上传到Amazon S3(Amazon simple storage servi

Wednesday, August 19, 2015

Java - Non-Blocking Concurrent Queue Algorithm



http://grepcode.com/file_/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/concurrent/ConcurrentLinkedQueue.java/?v=source
   public boolean offer(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);

        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            if (q == null) {
                // p is last node
                if (p.casNext(null, newNode)) {
                    // Successful CAS is the linearization point
                    // for e to become an element of this queue,
                    // and for newNode to become "live".
                    if (p != t) // hop two nodes at a time
                        casTail(t, newNode);  // Failure is OK.
                    return true;
                }
                // Lost CAS race to another thread; re-read next
            }
            else if (p == q)
                // We have fallen off list.  If tail is unchanged, it
                // will also be off-list, in which case we need to
                // jump to head, from which all live nodes are always
                // reachable.  Else the new tail is a better bet.
                p = (t != (t = tail)) ? t : head;
            else
                // Check for tail updates after two hops.
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

    public E poll() {
        restartFromHead:
        for (;;) {
            for (Node<E> h = head, p = h, q;;) {
                E item = p.item;

                if (item != null && p.casItem(item, null)) {
                    // Successful CAS is the linearization point
                    // for item to be removed from this queue.
                    if (p != h) // hop two nodes at a time
                        updateHead(h, ((q = p.next) != null) ? q : p);
                    return item;
                }
                else if ((q = p.next) == null) {
                    updateHead(h, p);
                    return null;
                }
                else if (p == q)
                    continue restartFromHead;
                else
                    p = q;
            }
        }
    }

    public E peek() {
        restartFromHead:
        for (;;) {
            for (Node<E> h = head, p = h, q;;) {
                E item = p.item;
                if (item != null || (q = p.next) == null) {
                    updateHead(h, p);
                    return item;
                }
                else if (p == q)
                    continue restartFromHead;
                else
                    p = q;
            }
        }
    }

    private static class Node<E> {
        volatile E item;
        volatile Node<E> next;

        /**
         * Constructs a new node.  Uses relaxed write because item can
         * only be seen after publication via casNext.
         */
        Node(E item) {
            UNSAFE.putObject(this, itemOffset, item);
        }

        boolean casItem(E cmp, E val) {
            return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
        }

        void lazySetNext(Node<E> val) {
            UNSAFE.putOrderedObject(this, nextOffset, val);
        }

        boolean casNext(Node<E> cmp, Node<E> val) {
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

        // Unsafe mechanics

        private static final sun.misc.Unsafe UNSAFE;
        private static final long itemOffset;
        private static final long nextOffset;

        static {
            try {
                UNSAFE = sun.misc.Unsafe.getUnsafe();
                Class<?> k = Node.class;
                itemOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("item"));
                nextOffset = UNSAFE.objectFieldOffset
                    (k.getDeclaredField("next"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    }
http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html
  structure pointer_t {ptr: pointer to node_t, count: unsigned integer}
  structure node_t {value: data type, next: pointer_t}
  structure queue_t {Head: pointer_t, Tail: pointer_t}
  
  initialize(Q: pointer to queue_t)
     node = new_node()  // Allocate a free node
     node->next.ptr = NULL // Make it the only node in the linked list
     Q->Head.ptr = Q->Tail.ptr = node // Both Head and Tail point to it
  
  enqueue(Q: pointer to queue_t, value: data type)
   E1:   node = new_node() // Allocate a new node from the free list
   E2:   node->value = value // Copy enqueued value into node
   E3:   node->next.ptr = NULL // Set next pointer of node to NULL
   E4:   loop   // Keep trying until Enqueue is done
   E5:      tail = Q->Tail // Read Tail.ptr and Tail.count together
   E6:      next = tail.ptr->next // Read next ptr and count fields together
   E7:      if tail == Q->Tail // Are tail and next consistent?
               // Was Tail pointing to the last node?
   E8:         if next.ptr == NULL
                  // Try to link node at the end of the linked list
   E9:            if CAS(&tail.ptr->next, next, <node, next.count+1>)
  E10:               break // Enqueue is done.  Exit loop
  E11:            endif
  E12:         else  // Tail was not pointing to the last node
                  // Try to swing Tail to the next node
  E13:            CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
  E14:         endif
  E15:      endif
  E16:   endloop
         // Enqueue is done.  Try to swing Tail to the inserted node
  E17:   CAS(&Q->Tail, tail, <node, tail.count+1>)
  
  dequeue(Q: pointer to queue_t, pvalue: pointer to data type): boolean
   D1:   loop        // Keep trying until Dequeue is done
   D2:      head = Q->Head      // Read Head
   D3:      tail = Q->Tail      // Read Tail
   D4:      next = head.ptr->next    // Read Head.ptr->next
   D5:      if head == Q->Head      // Are head, tail, and next consistent?
   D6:         if head.ptr == tail.ptr // Is queue empty or Tail falling behind?
   D7:            if next.ptr == NULL  // Is queue empty?
   D8:               return FALSE      // Queue is empty, couldn't dequeue
   D9:            endif
                  // Tail is falling behind.  Try to advance it
  D10:            CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
  D11:         else       // No need to deal with Tail
                  // Read value before CAS
                  // Otherwise, another dequeue might free the next node
  D12:            *pvalue = next.ptr->value
                  // Try to swing Head to the next node
  D13:            if CAS(&Q->Head, head, <next.ptr, head.count+1>)
  D14:               break             // Dequeue is done.  Exit loop
  D15:            endif
  D16:         endif
  D17:      endif
  D18:   endloop
  D19:   free(head.ptr)       // It is safe now to free the old node
  D20:   return TRUE                   // Queue was not empty, dequeue succeeded


多线程队列的算法优化 | Parallel Labs



多线程队列的算法优化 | Parallel Labs
多线程队列(Concurrent Queue)的使用场合非常多,高性能服务器中的消息队列,并行算法中的Work Stealing等都离不开它。对于一个队列来说有两个最主要的动作:添加(enqueue)和删除(dequeue)节点。在一个(或多个)线程在对一个队列进行enqueue操作的同时可能会有一个(或多个)线程对这个队列进行dequeue操作。因为enqueue和dequeue都是对同一个队列里的节点进行操作,为了保证线程安全,一般在实现中都会在队列的结构体中加入一个队列锁(典型的如pthread_mutex_t q_lock),在进行enqueue和dequeue时都会先锁住这个锁以锁住整个队列然后再进行相关的操作。这样的设计如果实现的好的话一般性能就会很不错了。以链表实现的队列的结构体一般是这样的:
01
02
03
04
05
struct queue_t {
    node_t *head;
    node_t *tail;
    pthread_mutex_t q_lock;
};
但是,这其中其实有一个潜在的性能瓶颈:enqueue和dequeue操作都要锁住整个队列,这在线程少的时候可能没什么问题,但是只要线程数一多,这个锁竞争所产生的性能瓶颈就会越来越严重。那么我们可不可以想办法优化一下这个算法呢?当然可以!如果我们仔细想一想enqueue和dequeue的具体操作就会发现他们的操作其实不一定是冲突的。例如:如果所有的enqueue操作都是往队列的尾部插入新节点,而所有的dequeue操作都是从队列的头部删除节点,那么enqueue和dequeue大部分时候都是相互独立的,我们大部分时候根本不需要锁住整个队列,白白损失性能!那么一个很自然就能想到的算法优化方案就呼之欲出了:我们可以把那个队列锁拆成两个:一个队列头部锁(head lock)和一个队列尾部锁(tail lock)。这样这样的设计思路是对了,但是如果再仔细思考一下它的实现的话我们会发现其实不太容易,因为有两个特殊情况非常的tricky(难搞):第一种就是往空队列里插入第一个节点的时候,第二种就是从只剩最后一个节点的队列中删除那个"最后的果实"的时候。
为什么难搞呢?当我们向空队列中插入第一个节点的时候,我们需要同时修改队列的head和tail指针,使他们同时指向这个新插入的节点,换句话说,我们此时即需要拿到head lock又需要拿到tail lock。而另一种情况是对只剩一个节点的队列进行dequeue的时候,我们也是需要同时修改head和tail指针使他们指向NULL,亦即我们需要同时获得head和tail lock。有经验的同学会立刻发现我们进入危险区了!是什么危险呢?死锁!多线程编程中最臭名昭著的一种bug就是死锁了。例如,如果线程A在锁住了资源1后还想要获取资源2,而线程B在锁住了资源2后还想要获取资源1,这时两个线程谁都不能获得自己想要的那个资源,两个线程就死锁了。所以我们要小心奕奕的设计这个算法以避免死锁,例如保证enqueue和dequeue对head lock和tail lock的请求顺序(lock ordering)是一致的等等。但是这样设计出来的算法很容易就会包含多次的加锁/解锁操作,这些都会造成不必要的开销,尤其是在线程数很多的情况下反而可能导致性能的下降。我的亲身经历就是在32线程时这个思路设计出来的算法性能反而下降了10%左右,原因就是加锁/解锁的开销增加了。
好在有聪明人早在96年就想到了一个更妙的算法。这个算法也是用了head和tail两个锁,但是它有一个关键的地方是它在队列初始化的时候head和tail指针不为空,而是指向一个空节点。在enqueue的时候只要向队列尾部添加新节点就好了。而dequeue的情况稍微复杂点,它要返回的不是头节点,而是head->next,即头节点的下一个节点。先来看伪代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
typedef struct node_t {
    TYPE value;
    node_t *next
} NODE;
typedef struct queue_t {
    NODE *head;
    NODE *tail;
    LOCK q_h_lock;
    LOCK q_t_lock;
} Q;
initialize(Q *q) {
   node = new_node()   // Allocate a free node
   node->next = NULL   // Make it the only node in the linked list
   q->head = q->tail = node   // Both head and tail point to it
   q->q_h_lock = q->q_t_lock = FREE   // Locks are initially free
}
enqueue(Q *q, TYPE value) {
   node = new_node()       // Allocate a new node from the free list
   node->value = value     // Copy enqueued value into node
   node->next = NULL       // Set next pointer of node to NULL
   lock(&q->q_t_lock)      // Acquire t_lock in order to access Tail
      q->tail->next = node // Link node at the end of the queue
      q->tail = node       // Swing Tail to node
   unlock(&q->q_t_lock)    // Release t_lock
dequeue(Q *q, TYPE *pvalue) {
   lock(&q->q_h_lock)   // Acquire h_lock in order to access Head
      node = q->head    // Read Head
      new_head = node->next       // Read next pointer
      if new_head == NULL         // Is queue empty?
         unlock(&q->q_h_lock)     // Release h_lock before return
         return FALSE             // Queue was empty
      endif
      *pvalue = new_head->value   // Queue not empty, read value
      q->head = new_head  // Swing Head to next node
   unlock(&q->q_h_lock)   // Release h_lock
   free(node)             // Free node
   return TRUE            // Queue was not empty, dequeue succeeded
}
发现玄机了么?是的,这个算法中队列总会包含至少一个节点。dequeue每次返回的不是头节点,而是头节点的下一个节点中的数据:如果head->next不为空的话就把这个节点的数据取出来作为返回值,同时再把head指针指向这个节点,此时旧的头节点就可以被free掉了。这个在队列初始化时插入空节点的技巧使得enqueue和dequeue彻底相互独立了。但是,还有一个小地方在实现的时候需要注意:对第一个空节点的next指针的读写。想象一下,当一个线程对一个空队列进行第一次enqueue操作时刚刚运行完第25行的代码(对该空节点的next指针进行写操作);而此时另一个线程对这个队列进行第一次dequeue操作时恰好运行到第33行(对该空节点的next指针进行读操作),它们其实还是有冲突!不过,好在一般来讲next指针是32位数据,而现代的CPU已经能保证多线程程序中内存对齐了的32位数据读写操作的原子性,而一般来讲编译器会自动帮你对齐32位数据,所以这个不是问题。唯一需要注意的是我们要确保enqueue线程是先让要添加的新节点包含好数据再把新节点插入链表(也就是不能先插入空节点,再往节点中填入数据),那么dequeue线程就不会拿到空的节点。其实我们也可以把q_t_lock理解成生产者的锁,q_h_lock理解成消费者的锁,这样生产者(们)和消费者(们)的操作就相互独立了,只有在多个生产者对同一队列进行添加操作时,以及多个消费者对同一队列进行删除操作时才需要加锁以使访问互斥。
通过使用这个算法,我成功的把一个32线程程序的性能提升了11%!可见多线程中的锁竞争对性能影响之大!此算法出自一篇著名的论文:M. Michael and M. Scott. Simple, Fast, and Practical Non-Blocking and Blocking Concurren Queue Algorithms. 如果还想做更多优化的话可以参考这篇论文实现相应的Non Blocking版本的算法,性能还能有更多提升。当然了,这个算法早已被集成到java.util.concurrent里了(即LinkedBlockingQueue),其他的并行库例如Intel的TBB多半也有类似的算法,如果大家能用上现成的库的话就不要再重复造轮子了。为什么别造并行算法的轮子呢?因为高性能的并行算法实在太难正确地实现了,尤其是Non Blocking,Lock Free之类的"火箭工程"。有多难呢?Doug Lea提到java.util.concurrent中一个Non Blocking的算法的实现大概需要1年的时间,总共约500行代码。所以,对最广大的程序员来说,别去写Non Blocking, Lock Free的代码,只管用就行了,我看见网上很多的Non Blocking阿,无锁编程的算法实现啊什么的都非常地害怕,谁敢去用他们贴出来的这些代码啊?我之所以推荐这个two lock的算法是因为它的实现相对Non Blocking之类的来说容易多了,非常具备实用价值。虽然这篇论文出现的很早,但是我在看了几个开源软件中多线程队列的实现之后发现他们很多还是用的本文最开始提到的那种一个锁的算法。如果你想要实现更高性能的多线程队列的话,试试这个算法吧!
Update: 多线程队列算法有很多种,大家应根据不同的应用场合选取最优算法(例如是CPU密集型还是IO密集型)。本文所列的算法应用在这样一个多线程程序中:每个线程都拥有一个队列,每个队列可能被本线程进行dequeue操作,也可以被其他线程进行dequeue(即work stealing),线程数不超过CPU核心数,是一个典型的CPU/MEM密集型客户端单写者多读者场景。
java.util.concurrent.LinkedBlockingQueue
    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

    /**
     * Signals a waiting take. Called only from put/offer (which do not
     * otherwise ordinarily lock takeLock.)
     */
    private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
    }

    /**
     * Signals a waiting put. Called only from take/poll.
     */
    private void signalNotFull() {
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            notFull.signal();
        } finally {
            putLock.unlock();
        }
    }
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }
   public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E x = null;
        int c = -1;
        long nanos = unit.toNanos(timeout);
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

ArrayBlockingQueue
/** Main lock guarding all access */
final ReentrantLock lock;

/** Condition for waiting takes */
private final Condition notEmpty;

/** Condition for waiting puts */
private final Condition notFull;
/** The queued items */
final Object[] items;

/** items index for next take, poll, peek or remove */
int takeIndex;

/** items index for next put, offer, or add */
int putIndex;

/** Number of elements in the queue */
int count;
public boolean offer(E e, long timeout, TimeUnit unit)
    throws InterruptedException {

    checkNotNull(e);    long nanos = unit.toNanos(timeout);    final ReentrantLock lock = this.lock;    lock.lockInterruptibly();    try {
        while (count == items.length) {
            if (nanos <= 0)
                return false;            nanos = notFull.awaitNanos(nanos);        }
        enqueue(e);        return true;    } finally {
        lock.unlock();    }
}


private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;    // assert items[putIndex] == null;    final Object[] items = this.items;    items[putIndex] = x;    if (++putIndex == items.length)
        putIndex = 0;    count++;    notEmpty.signal();}

/** * Extracts element at current take position, advances, and signals. * Call only when holding lock. */private E dequeue() {
    // assert lock.getHoldCount() == 1;    // assert items[takeIndex] != null;    final Object[] items = this.items;    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];    items[takeIndex] = null;    if (++takeIndex == items.length)
        takeIndex = 0;    count--;    if (itrs != null)
        itrs.elementDequeued();    notFull.signal();    return x;}
http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html
Two-Lock Concurrent Queue Algorithm
  structure node_t {value: data type, next: pointer to node_t}
  structure queue_t {Head: pointer to node_t, Tail: pointer to node_t,
                        H_lock: lock type, T_lock: lock type}
  
  initialize(Q: pointer to queue_t)
     node = new_node()  // Allocate a free node
     node->next = NULL          // Make it the only node in the linked list
     Q->Head = Q->Tail = node // Both Head and Tail point to it
     Q->H_lock = Q->T_lock = FREE // Locks are initially free
  
  enqueue(Q: pointer to queue_t, value: data type)
     node = new_node()         // Allocate a new node from the free list
     node->value = value  // Copy enqueued value into node
     node->next = NULL          // Set next pointer of node to NULL
     lock(&Q->T_lock)  // Acquire T_lock in order to access Tail
        Q->Tail->next = node // Link node at the end of the linked list
        Q->Tail = node  // Swing Tail to node
     unlock(&Q->T_lock)  // Release T_lock
  
  dequeue(Q: pointer to queue_t, pvalue: pointer to data type): boolean
     lock(&Q->H_lock)         // Acquire H_lock in order to access Head
        node = Q->Head  // Read Head
        new_head = node->next // Read next pointer
        if new_head == NULL // Is queue empty?
           unlock(&Q->H_lock) // Release H_lock before return
           return FALSE  // Queue was empty
        endif
        *pvalue = new_head->value // Queue not empty.  Read value before release
        Q->Head = new_head // Swing Head to next node
     unlock(&Q->H_lock)  // Release H_lock
     free(node)   // Free node
     return} TRUE  // Queue was not empty, dequeue succeeded

Read full article from 多线程队列的算法优化 | Parallel Labs

Tuesday, July 21, 2015

[Design] Speed Up Webpage for Slow Connection - Shuatiblog.com



[Design] Speed Up Webpage for Slow Connection (1) - Shuatiblog.com
Suppose you are handling Amazon website and you have 10 MB size home page. Optimize the homepage for a customer who has 100 kbps internet connection.
Further he asked for the customer who has 100 mbps internet connection.
http://sixrevisions.com/web-development/site-speed-performance/

1. Defer Loading Content When Possible

2. Use External JS and CSS Files

When the user first loads your web page, the browser will cache external resources like CSS and JavaScript files. Thus, instead of inline JavaScript and CSS files, it’s best to place them in external files.
Using inline CSS also increases the rendering time of a web page; having everything defined in your main CSS file lets the browser do less work when rendering the page, since it already knows all the style rules that it needs to apply.
As a bonus, using external JavaScript and CSS files makes site maintenance easier because you only need to maintain global files instead of code scattered in multiple web pages.

3. Use Caching Systems - Server side cache

  1. Avoid Resizing Images in HTML (using HTML’s width and height attributes), for the sake of smaller file size.
  2. Optimize Image Sizes by Using the Correct File Format. Eg. JPG format often displays photographic images at smaller file sizes than PNG.
  3. Optimize the Way You Write Code. For example, instead of using (h1)(em)Your heading(em)(h1), you can easily use CSS to make your headings italics.

8. Load JavaScript at the End of Your Document

It’s best if you have your scripts loading at the end of the page rather than at the beginning. It allows for the browser to render everything before getting started with the JavaScript. This makes your web pages feel more responsive because the way JavaScript works is that it blocks anything below it from rendering until it has finished downloading. If possible, reference JavaScript right before the closing (body) tag of your HTML documents.

9. Use a Content Delivery Network (CDN)

10. Optimize Web Caching -- Client side cache

Along with using caching systems, you should create websites that utilize web caching as much as possible. Web caching is when files are cached by the web browser for later use. Things that browsers can cache include CSS files, JavaScript files, and images.
For example, you can set HTTP response headers such as Expires and Last-Modified to reduce the need of re-downloading certain files when the user comes back to your site. 

http://www.shuatiblog.com/blog/2015/02/14/speed-up-web-page-3/
http://www.shuatiblog.com/blog/2015/02/14/speed-up-web-page-4/

What hijacks your load time

1. Too Many HTTP Requests

This is the single biggest contributor to performance problems in most web pages.
  1. Concatenate scripts and stylesheets
  2. Combine images with sprites (put common images into a single large image file, then use CSS to position and selectively display the appropriate portion of the sprite image)
  3. Use fewer images, more CSS.

2. Minimal Client-side Processing

  1. Validation on client. (eg. form input)
  2. Use web standards and MVC separation, making a maintainable, accessible, future-proof and max-performance website.
    Think of the HTML as the model, the CSS as the view, and the JavaScript as the controller. This separation tends to make code more efficient and maintainable, and makes many optimization techniques much more practical to apply.
  3. Push presentation code into the client tier (eg. Charts and graphs — push raw data to the client in JSON format, and use JavaScript and CSS to create pretty graphs.)
  4. Leverage Ajax techniques (only requiring small parts of the page to change in response to user actions)

3. Low Number of Parallel Requests

Fetch a script, parse and execute it, then fetch another one… this will use up all the available connections. There are things you can do to your HTML to allow virtually any browser to make many requests at once, which has a huge impact on latency.
  1. Use browser-appropriate domain sharding
  2. Use intelligent script loading
  3. Leverage Keep-Alive (reuse the same TCP connection for multiple HTTP request/response cycles)

4. Failure to leverage browser cache / local storage

  1. Leverage local storage

5. Third-party widgets

  1. Avoid third-party widgets!
  2. Try to use widgets that provide asynchronous implementations, so their inevitably terrible performance impacts their widget without dragging down your entire UX with it.

7. Failure to Use a Global Network

Amazon S3.
Read full article from [Design] Speed Up Webpage for Slow Connection (1) - Shuatiblog.com

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