你知道你的電腦 1 秒鐘能做多少事情嗎?
讓我們來看看你有多么了解電腦!所有這些程序的數(shù)值都是可變的。你的任務(wù)是:在程序花費(fèi)1秒運(yùn)行之前猜測它的大概值。
你并不需要猜出一個(gè)精確值:選擇范圍在1和10億之間。你只要能猜出正確的數(shù)量級(jí),就算正確!下面是一些注意事項(xiàng):
-
如果答案是38,000,那么你選擇10,000或100,000,我們就認(rèn)為都是正確答案。誤差只要在10倍范圍內(nèi)就ok:)
-
我們知道不同的計(jì)算機(jī)有不同的磁盤、網(wǎng)絡(luò)和CPU速度!我們會(huì)告訴運(yùn)行10次/秒和10萬次/秒的代碼之間的差別。更新的電腦不會(huì)讓你的代碼運(yùn)行速度快1000倍:)
-
也就是說,所有這一切都是運(yùn)行在一臺(tái)新的擁有一個(gè)快速的SSD和一個(gè)湊合的網(wǎng)絡(luò)連接的筆記本電腦上的。 C代碼用gcc -O2編譯。
祝你好運(yùn)!
歡迎來到第一個(gè)程序!這一個(gè)只是讓你練練手的:1秒能完成多少循環(huán)? (結(jié)果可能比你想象得更多?。?/p>
猜猜下面的程序每秒執(zhí)行多少次循環(huán):
- #include <stdlib.h>
- // Number to guess: How many iterations of
- // this loop can we go through in a second?
- int main(int argc, char **argv) {
- int NUMBER, i, s;
- NUMBER = atoi(argv[1]);
- for (s = i = 0; i < NUMBER; ++i) {
- s += 1;
- }
- return 0;
- }
準(zhǔn)確答案:550,000,000
猜猜下面的程序每秒執(zhí)行多少次循環(huán):
- #!/usr/bin/env python
- # Number to guess: How many iterations of an
- # empty loop can we go through in a second?
- def f(NUMBER):
- for _ in xrange(NUMBER):
- pass
- import sys
- f(int(sys.argv[1]))
當(dāng)我看著代碼的時(shí)候,我想的是1毫秒完成多少次——我以為是微不足道的,但事實(shí)是,即使是Python,你也可以在1毫秒的時(shí)間內(nèi)執(zhí)行68,000次空循環(huán)迭代。
下面讓我們來探討一個(gè)更接近現(xiàn)實(shí)的用例。在Python中字典幾乎是無處不在的,那么在1秒時(shí)間內(nèi)我們可以用Python添加多少元素呢?
然后再來看一個(gè)更復(fù)雜的操作——使用Python的內(nèi)置HTTP請(qǐng)求解析器來解析請(qǐng)求。
猜猜下面的程序每秒執(zhí)行多少次循環(huán):
- #!/usr/bin/env python
- # Number to guess: How many entries can
- # we add to a dictionary in a second?
- # Note: we take `i % 1000` to control
- # the size of the dictionary
- def f(NUMBER):
- d = {}
- for i in xrange(NUMBER):
- d[i % 1000] = i
- import sys
- f(int(sys.argv[1]))
- 準(zhǔn)確答案:11,000,000
猜猜下面的程序每秒處理多少次HTTP請(qǐng)求:
- #!/usr/bin/env python
- # Number to guess: How many HTTP requests
- # can we parse in a second?
- from BaseHTTPServer import BaseHTTPRequestHandler
- from StringIO import StringIO
- class HTTPRequest(BaseHTTPRequestHandler):
- def __init__(self, request_text):
- self.rfile = StringIO(request_text)
- self.raw_requestline = self.rfile.readline()
- self.error_code = self.error_message = None
- self.parse_request()
- def send_error(self, code, message):
- self.error_code = code
- self.error_message = message
- request_text = """GET / HTTP/1.1
- Host: localhost:8001
- Connection: keep-alive
- Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
- Upgrade-Insecure-Requests: 1
- User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36
- Accept-Encoding: gzip, deflate, sdch
- Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
- """
- def f(NUMBER):
- for _ in range(NUMBER):
- HTTPRequest(request_text)
- import sys
- f(int(sys.argv[1]))
- 準(zhǔn)確答案:25,000
我們每秒可以解析25,000個(gè)小的HTTP請(qǐng)求!有一件事我要在這里指出的是,這里請(qǐng)求解析的代碼是用純Python編寫的,而不是C。
接下來,我們要試試下載網(wǎng)頁與運(yùn)行Python腳本!提示:少于1億:)
猜猜下面的程序每秒可以完成多少次HTTP請(qǐng)求:
- #!/usr/bin/env python
- # Number to guess: How many times can we
- # download google.com in a second?
- from urllib2 import urlopen
- def f(NUMBER):
- for _ in xrange(NUMBER):
- r = urlopen("http://google.com")
- r.read()
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:4
猜猜下面的程序每秒可以執(zhí)行多少次循環(huán):
- #!/bin/bash
- # Number to guess: How many times can we start
- # the Python interpreter in a second?
- NUMBER=$1
- for i in $(seq $NUMBER); do
- python -c '';
- done
準(zhǔn)確答案:77
啟動(dòng)程序?qū)嶋H上昂貴在其本身,而不是啟動(dòng)Python。如果我們只是運(yùn)行/bin/true,那么1秒能做500次,所以看起來運(yùn)行任何程序只需要 大約1毫秒時(shí)間。當(dāng)然,下載網(wǎng)頁的快慢很大程度上取決于網(wǎng)頁大小,網(wǎng)絡(luò)連接速度,以及服務(wù)器間的距離,不過今天我們不談網(wǎng)絡(luò)性能。我的一個(gè)朋友說,高性能 的網(wǎng)絡(luò)完成網(wǎng)絡(luò)往返甚至可能只要250納秒(?。。。?,但這是在計(jì)算機(jī)位置更相鄰,硬件更好的情況下。
1秒時(shí)間能夠在磁盤中寫入多少字節(jié)?我們都知道寫到內(nèi)存中時(shí)速度會(huì)更快,但是究竟會(huì)快多少呢?對(duì)了,下面的代碼運(yùn)行在帶有SSD的計(jì)算機(jī)上。
猜猜下面的程序每秒可以寫入多少字節(jié)數(shù)據(jù):
- #!/usr/bin/env python
- # Number to guess: How many bytes can we write
- # to an output file in a second?
- # Note: we make sure everything is sync'd to disk
- # before exiting
- import tempfile
- import os
- CHUNK_SIZE = 1000000
- s = "a" * CHUNK_SIZE
- def cleanup(f, name):
- f.flush()
- os.fsync(f.fileno())
- f.close()
- try:
- os.remove(name)
- except:
- pass
- def f(NUMBER):
- name = './out'
- f = open(name, 'w')
- bytes_written = 0
- while bytes_written < NUMBER:
- f.write(s)
- bytes_written += CHUNK_SIZE
- cleanup(f, name)
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:342,000,000
猜猜下面的程序每秒可以寫入多少字節(jié)數(shù)據(jù):
- #!/usr/bin/env python
- # Number to guess: How many bytes can we write
- # to a string in memory in a second?
- import cStringIO
- CHUNK_SIZE = 1000000
- s = "a" * CHUNK_SIZE
- def f(NUMBER):
- output = cStringIO.StringIO()
- bytes_written = 0
- while bytes_written < NUMBER:
- output.write(s)
- bytes_written += CHUNK_SIZE
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:2,000,000,000
下面輪到文件了!有時(shí)候,運(yùn)行一個(gè)大型的grep之后,它可以永恒跑下去。在1秒時(shí)間內(nèi),grep可以搜索多少字節(jié)?
請(qǐng)注意,在這么做的時(shí)候,grep正在讀取的字節(jié)已經(jīng)在內(nèi)存中。
文件列表同樣需要時(shí)間!1秒能列出多少文件?
猜猜下面的程序每秒可以搜索多少字節(jié)的數(shù)據(jù):
- #!/bin/bash
- # Number to guess: How many bytes can `grep`
- # search, unsuccessfully, in a second?
- # Note: the bytes are in memory
- NUMBER=$1
- cat /dev/zero | head -c $NUMBER | grep blah
- exit 0
- 準(zhǔn)確答案:2,000,000,000
- 猜猜下面的程序每秒可以列出多少文件:
- #!/bin/bash
- # Number to guess: How many files can `find` list in a second?
- # Note: the files will be in the filesystem cache.
- find / -name '*' 2> /dev/null | head -n $1 > /dev/null
準(zhǔn)確答案:325,000
序列化是一個(gè)普遍要花費(fèi)大量時(shí)間的地方,讓人很蛋疼,特別是如果你反復(fù)結(jié)束序列化/反序列化相同數(shù)據(jù)的時(shí)候。這里有幾個(gè)基準(zhǔn):轉(zhuǎn)換64K大小的JSON格式數(shù)據(jù),與同樣大小的msgpack格式數(shù)據(jù)。
猜猜下面的程序每秒可以執(zhí)行多少次循環(huán):
- #!/usr/bin/env python
- # Number to guess: How many times can we parse
- # 64K of JSON in a second?
- import json
- with open('./setup/protobuf/message.json') as f:
- message = f.read()
- def f(NUMBER):
- for _ in xrange(NUMBER):
- json.loads(message)
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:449
猜猜下面的程序每秒可以執(zhí)行多少次循環(huán):
- #!/usr/bin/env python
- # Number to guess: How many times can we parse
- # 46K of msgpack data in a second?
- import msgpack
- with open('./setup/protobuf/message.msgpack') as f:
- message = f.read()
- def f(NUMBER):
- for _ in xrange(NUMBER):
- msgpack.unpackb(message)
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:4,000
數(shù)據(jù)庫。沒有任何類似于PostgreSQL花里胡哨的東西,我們做了2份有1000萬行數(shù)據(jù)的SQLite表,一個(gè)是有索引的,另一個(gè)是未建索引的。
猜猜下面的程序每秒可以執(zhí)行多少次查詢:
- #!/usr/bin/env python
- # Number to guess: How many times can we
- # select a row from an **indexed** table with
- # 10,000,000 rows?
- import sqlite3
- conn = sqlite3.connect('./indexed_db.sqlite')
- c = conn.cursor()
- def f(NUMBER):
- query = "select * from my_table where key = %d" % 5
- for i in xrange(NUMBER):
- c.execute(query)
- c.fetchall()
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:53,000
猜猜下面的程序每秒執(zhí)行多少次查詢:
- #!/usr/bin/env python
- # Number to guess: How many times can we
- # select a row from an **unindexed** table with
- # 10,000,000 rows?
- import sqlite3
- conn = sqlite3.connect('./unindexed_db.sqlite')
- c = conn.cursor()
- def f(NUMBER):
- query = "select * from my_table where key = %d" % 5
- for i in xrange(NUMBER):
- c.execute(query)
- c.fetchall()
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:2
下面要說Hash算法!在這里,我們將比較MD5和bcrypt。用MD5你在1秒時(shí)間內(nèi)可以哈希到相當(dāng)多的東西,而用bcrypt則不能。
猜猜下面的程序每秒可以哈希多少字節(jié)的數(shù)據(jù):
- #!/usr/bin/env python
- # Number to guess: How many bytes can we md5sum in a second?
- import hashlib
- CHUNK_SIZE = 10000
- s = 'a' * CHUNK_SIZE
- def f(NUMBER):
- bytes_hashed = 0
- h = hashlib.md5()
- while bytes_hashed < NUMBER:
- h.update(s)
- bytes_hashed += CHUNK_SIZE
- h.digest()
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:455,000,000
猜猜下面的程序每秒可以哈希多少字節(jié)的密碼:
- #!/usr/bin/env python
- # Number to guess: How many passwords
- # can we bcrypt in a second?
- import bcrypt
- password = 'a' * 100
- def f(NUMBER):
- for _ in xrange(NUMBER):
- bcrypt.hashpw(password, bcrypt.gensalt())
- import sys
- f(int(sys.argv[1]))
準(zhǔn)確答案:3
接下來,我們要說一說內(nèi)存訪問。 現(xiàn)在的CPU有L1和L2緩存,這比主內(nèi)存訪問速度更快。這意味著,循序訪問內(nèi)存通常比不按順序訪問內(nèi)存能提供更快的代碼。
猜猜下面的程序每秒可以向內(nèi)存寫入多少字節(jié)數(shù)據(jù):
- #include <stdlib.h>
- #include <stdio.h>
- // Number to guess: How big of an array (in bytes)
- // can we allocate and fill in a second?
- // this is intentionally more complicated than it needs to be
- // so that it matches the out-of-order version
- int main(int argc, char **argv) {
- int NUMBER, i;
- NUMBER = atoi(argv[1]);
- char* array = malloc(NUMBER);
- int j = 1;
- for (i = 0; i < NUMBER; ++i) {
- j = j * 2;
- if (j > NUMBER) {
- j = j - NUMBER;
- }
- array[i] = j;
- }
- printf("%d", array[NUMBER / 7]);
- // so that -O2 doesn't optimize out the loop
- return 0;
- }
準(zhǔn)確答案:376,000,000
猜猜下面的程序每秒可以向內(nèi)存寫入多少字節(jié)數(shù)據(jù):
- #include <stdlib.h>
- #include <stdio.h>
- // Number to guess: How big of an array (in bytes)
- // can we allocate and fill with 5s in a second?
- // The catch: We do it out of order instead of in order.
- int main(int argc, char **argv) {
- int NUMBER, i;
- NUMBER = atoi(argv[1]);
- char* array = malloc(NUMBER);
- int j = 1;
- for (i = 0; i < NUMBER; ++i) {
- j = j * 2;
- if (j > NUMBER) {
- j = j - NUMBER;
- }
- array[j] = j;
- }
- printf("%d", array[NUMBER / 7]);
- // so that -O2 doesn't optimize out the loop
- return 0;
- }
準(zhǔn)確答案:68,000,000
歡迎大家去試一試,給我們留下寶貴的意見。
譯文鏈接:http://www.codeceo.com/article/1-second-your-computer-do.html
英文原文:DO YOU KNOW HOW MUCH YOUR COMPUTER CAN DO IN A SECOND?