Sunday, 17 March 2019

Tăng tốc Django API Service tới 90% với cacheops

1. Code chạy đúng là được rồi mà?

Code chạy đúng thôi là chưa đủ, luôn phải tối ưu thêm về mặt tốc độ.

Bạn có thể viết code đẹp như tranh, nhưng nếu một request của khách mất đến 10s để xử lý, thì 99% là khách hàng của bạn sẽ bỏ đi ngay lập tức (trừ khi bạn là trang .gov hoặc khi bạn cần đăng ký tín chỉ).

Ok, bạn tiếp tục "outsource" những phần xử lý nặng nề cho Celery hoặc RQ để khách đỡ phải nhìn màn hình trắng. Nhưng kể cả vậy thì nó cũng không làm vấn đề biến mất. Nó chỉ giúp khách của bạn biết rằng ở bên dưới bạn vẫn đang vật lộn xử lý chứ không "chết hẳn". Thay vì màn hình trắng thì giờ là hình xoay xoay.

Và lúc đó, bạn cần phải "tối ưu" lại code của mình rồi.

Và sau khi vò đầu bứt tai ngồi đoán, imort pdb; pdb.set_trace() loạn xạ, thêm các kiểu đo đếm thời gian, bạn bó tay. Vì đó không phải cách bạn nên làm để tối ưu code.

2. Analysis

Đầu tiên, ta cần phải phân tíchđo lường. Bạn có thể ngồi đoán mò cả ngày xem code bị nghẽn ở đâu, hoặc dùng một cách khoa học hơn để biết phải sửa chỗ nào.

Và việc đó gọi là `profilling` code. Python có sẵn thư viện để profile, đó là cProfile. Còn Django thì có django-debug-toolbar và Django Rest Framework có django-silk.

Django Silk
Khi bạn truy cập 1 API, Silk sẽ profile lại các SQL và thời gian thực hiện, số lần thực hiện, được gọi ở dòng nào, hàm nào v.v.. Và nó còn lưu lại rất nhiều các thông tin hữu ích khác nữa liên quan đến request đó.

Django silk lưu lại các SQL query



và cả chỗ được gọi nữa
(Lưu ý: bạn chỉ nên setup ở môi trường dev hoặc local, vì thời gian profile và lưu lại vào db nhiều khi còn lâu hơn thời gian code thực thi)

3. Tối ưu code

Sau khi bạn biết được query/đoạn code nào bị lặp nhiều nhất, hay chạy lâu nhất, sai logic hay không, thì bạn có thể chỉnh lại và xem có tăng được chút hiệu quả nào không (thường là sẽ có, không ít thì nhiều).

Ví dụ như trường hợp của mình, sau khi phân tích và tối ưu hoá lại, kết quả là khá đáng kể.

phần màu cam nhỏ nhỏ là thời gian của Django Silk khi profilling.
210 query là đã tối ưu 1 lần, trước đó khoảng gần 300 query!
Nhưng thế vẫn là chưa đủ. Vì sẽ đến 1 thời điểm mà việc bạn tối ưu code, lại dẫn đến việc tăng thời gian thực hiện, hoặc việc thực hiện thay đổi sẽ tốn nhiều thời gian và công sức, dẫn đến khả năng gặp lỗi logic. Sự đánh đổi ở đây lớn hơn lợi ích nó mang lại, vì vậy không đáng để làm.

Tỷ như ở đây mình tối ưu được số query, nhưng lại làm tăng thời gian thực hiện lên.


Vì thế bạn nên tối ưu theo một hướng khác nữa, đó là Cache.

4. Cache

Cache có nghĩa là thay vì mỗi lần client request, bạn lại thực hiện lại đầy đủ các bước và logic, thì bạn sẽ lưu lại kết quả của request này, lần sau ai đó request đến thì bạn chỉ việc ung dung lấy ra và đưa lại cho họ mà không cần phải tính toán lại. Điều này làm giảm tải rất nhiều cho server và tăng một tốc độ đáng kể. (Và thường sẽ sử dụng Redis hay Memcache để lưu)

Nhưng nếu database của bạn thay đổi, mà bạn không xử lý việc đó để update cache, thì khả năng rất cao là dù bạn nhanh, nhưng bạn sẽ sai. Vì thế bạn nên dùng một thư viện để quản lý việc cache, trừ khi đó là những chỗ rất đặc biệt phải tự code.

Django có sẵn cả một thư viện giúp bạn làm việc đó. Nhưng nếu bạn dùng Django Rest, bạn nên ngó qua thư viện django-cacheops (django-cacheops chỉ support Redis).

Thư viện này giúp chúng ta cache lại các queryset, theo dõi sự thay đổi của database để không bị sai sót (nhưng bạn vẫn cần cực kỳ cẩn thận và theo dõi, test lại đầy đủ các case để cân nhắc chỗ nào cần cache, chỗ nào không).


Yep, sau khi setup xong, lần đầu chạy số query đã giảm đáng kể (nhờ cache lại những model ít thay đổi như User, Config v.v..). Lần tiếp theo chạy, số query giảm về còn 0!

Feeling so good!

5. Kết luận

Trước khi tối ưu, mỗi lần user request đến API trên, bên mình mất đến 7 - 8s(!!!) cho một lần load. Còn hiện giờ, thời gian load đã giảm còn khoảng 0.3s. Một sự thoả mãn không hề nhẹ.

Dĩ nhiên, vẫn phải rà soát và test lại từng chỗ một để đảm bảo tính chuẩn xác, và có những đoạn code phải bỏ cache đi vì tần suất thay đổi quá lớn, nhưng dù sao về mặt tổng quát thì tất cả các API đều được tăng tốc độ (vì số lượng query giảm đi rất nhiều).

Còn nhiều thứ để tối ưu hơn nữa, nhưng tạm thời thế cũng ổn rồi. Bạn có thể xem thêm cách phân tích dữ liệu profile trả về qua khoá học của Peter Norvig, hoặc google, đọc docs.

Nếu quan tâm đến performance của Python, bạn có thể đọc thêm High Performance Python.
Hoặc sách High Performance Django tại đây: https://mega.nz/#!YBEGwCYT!KmNWDmu7i2EJ-1CXT7ZtdEcaJV6m7JE3ZWvB95N4utc

Đỗ Anh Tú

Monday, 24 December 2018

Chia sẻ file nhạc/ảnh/video trong mạng nội bộ (UPnP/DLNA)

Thời điểm này, mỗi gia đình đều có vài thiết bị "thông minh", TiVi, điện thoại, laptop, máy chơi game PlayStation... mà dữ liệu nhạc / film có thể nằm mỗi cái ở một chỗ. Có nhiều cách để chia sẻ file giữa các thiết bị này với nhau mà người ta đã làm cả chục năm nay rồi.

Một cái tên trông không đẹp cho lắm nhưng đã dùng thì rất tiện: UPnP (Universal Plug and Play)

Universal Plug and Play is a set of networking protocols that permits networked devices, such as personal computers, printers, Internet gateways, Wi-Fi access points and mobile devices to seamlessly discover each other's presence on the network and establish functional network services for data sharing, communications, and entertainment.

UPnP là một bộ giao thức mạng cho phép các thiết bị trong mạng tự khám phá ra nhau và chia sẻ dữ liệu.
Giao thức mạng tức là như HTTP, FTP ...

DLNA (Digital Living Network Alliance) là tên một "guideline"/bộ hướng dẫn, bao gồm nhiều thứ và có cả UPnP.

Cài đặt:
sudo apt install -y minidlna

(UPnP có sẵn trên Windows, khỏi cài đặt gì)

Saturday, 22 December 2018

Làm full tất cả CPU bằng 1 câu lệnh

Cách nào để làm CPU chạy full load ?

Khi mang em laptop bị nóng rồi tự tắt đi sửa, các anh kỹ thuật dùng cách vào youtube và tìm video 4k rồi mở fullscreen để xem...

Cách này chưa chắc đã làm full CPU, lại yêu cầu phải có mạng internet đủ nhanh.
Những options nào khác?

1. Dùng bash
while true; do true; done
2. Dùng Python
python -c 'while True: pass'


Hai cách này nghe có vẻ ổn, nhưng chúng chỉ làm full 01 CPU. Trong khi máy bạn có thể có nhiều CPU - ngay cả các Android smartphone của năm 2016 cũng đã có 4 CPU rồi.

Lấy số CPU của máy

Khái niệm CPU được hiểu theo nghĩa khác nhau trong các hòan cảnh khác nhau.
Nếu hiểu theo nghĩa: số chương trình chạy đồng thời cùng lúc (thực sự - chứ không phải giả đồng thời bằng cách chuyển nhanh qua các chương trình khác nhau chạy mỗi cái 1 tí như cách 1 CPU làm để chạy nhiều chương trình), ta có thể gõ `top` rồi bấm 1.


Trên Linux, mọi thứ đều là file , thông tin CPU nằm trong /proc/cpuinfo
$ grep 'core id' /proc/cpuinfo
core id        : 0
core id        : 0
core id        : 1
core id        : 1
getconf là câu lệnh đi kèm glibc, chuẩn POSIX tức có trên cả OSX, BSD ...
$ getconf -a | grep processor -i
_NPROCESSORS_CONF                  4
_NPROCESSORS_ONLN                  4
$ getconf _NPROCESSORS_ONLN
4

Ở đây có tất cả 4 processor (CPU).
Với 2 core, mỗi core 2 processor -> 2 x 2 = 4

$ sysctl -a | grep cpu
...
cũng sẽ hiện thông tin lần lượt cho các CPU0 1 2 3

Trên Linux, lệnh lscpu sẽ cho thông tin
chi tiết:

$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    2
Core(s) per socket:    2
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 69
Model name:            Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
Với 1 socket, với 2 core, mỗi core có 2 thread, => 1 x 2 x 2 = 4

CPUs = Threads per core X cores per socket X sockets
Vậy cách làm đúng ở đây là mở 4 cửa sổ và chạy theo cách python hoặc cách bash nói trên.
Với máy tính có 128 CPU thì chúc bạn vui vẻ nhé!

Chạy lệnh song song với xargs

xargs là câu lệnh giải quyết bài tóan chạy song song các câu lệnh ở đây.

Đầu tiên lấy số CPU online (khái niệm offline xuất hiện trên máy tính có nhiều khe cắm CPU - các máy server)
$ getconf _NPROCESSORS_ONLN
4

Tạo ra dãy số từ 1 đến 4 với lệnh seq

$ seq $(getconf _NPROCESSORS_ONLN)
1
2
3
4

Biến 4 dòng này thành 4 đầu vào cho chương trình, mỗi chương trình có nhiệm vụ chạy full CPU
seq $(getconf _NPROCESSORS_ONLN) | xargs -n1 -P$(getconf _NPROCESSORS_ONLN) yes > /dev/null
yes là câu lệnh in ra màn hình liên tục dòng chữ `y`, nó có tác dụng như 1 vòng lặp vô hạn.

export CPUS=$(getconf _NPROCESSORS_ONLN); seq $CPUS | xargs -n1 -P $CPUS python -c 'while True: pass'

Sử dụng chương trình `psensor`, nhiệt độ của các Core được hiển thị trên biểu đồ, tăng từ ~45 nhiệt độ bình thường lên 69, 70 độ C (nóng).



Các thiết bị làm mát phải vào cuộc, đảm bảo nhiệt độ không cao hơn, nếu không sẽ xảy ra cháy thiết bị - sau một vài phút ta có thể bắt đầu nghe thấy tiếng quạt CPU kêu khi nó bắt đầu hoạt động. Nếu quạt hỏng không quay, nhiệt độ sẽ tăng cao và hệ điều hành thường sẽ tắt thiết bị để đảm bảo không có cháy nổ.

Các option của lệnh xargs:
- n 1 : số argument gửi tới mỗi chương trình, ở đây là 1
- P : số process tối đa được chạy đồng thời - ở đây set bằng số CPU online = 4
python -c 'while True: pass' - câu lệnh được chạy, như thấy ở hình top, các chương trình này lần lượt nhận đầu vào là 1 2 3 4.

4 chương trình sau sẽ được chạy song song
python -c 'while True: pass' 1
python -c 'while True: pass' 2
python -c 'while True: pass' 3
python -c 'while True: pass' 4
Với máy có lệnh `nproc`, có thể dùng lệnh này
seq $(nproc) | xargs -n1 -P $(nproc) python -c 'while True: pass'
$ whatis nproc
nproc (1)            - print the number of processing units available
$ dpkg -S `which nproc`
coreutils: /usr/bin/nproc
 Tham khảo:
- CPU count: https://unix.stackexchange.com/a/279354

Hết.
HVN at https://pymi.vn and https://www.familug.org

Monday, 17 December 2018

Random notes

Đi làm không có nhiều thời gian để ngồi nắn nót từng bài blog, từng chủ đề từ à tớ ê.
Vậy nên để khởi động lại việc viết lách, những bài random notes sẽ note lại những thứ nhỏ bé học được gần đây mà không sắp xếp theo chủ đề nào cả.

0. Bấm gì thay TAB để auto-complete?

Ctrl i sẽ cho hiệu ứng auto-complete tương tự.

$ bind -p | grep \\C-i
"\C-i": complete
"\e\C-i": dynamic-complete-history


1. rxvt VS other terminal?

Sunday, 2 September 2018

Giảm ánh sáng xanh gây mù trên máy tính với redshift, f.lux

Các nghiên cứu khoa học gần đây cho biết ánh sáng xanh rất có hại cho mắt và có thể khiến bạn bị mù - và ảnh hưởng tới giấc ngủ (https://phys.org/news/2018-08-chemists-blue.html)

MacOS/iOS các phiên bản mới gần đây có sẵn tính năng giảm ánh sáng xanh, tăng màu đỏ để giảm sự hại mắt.
- MacOS https://support.apple.com/en-us/HT207513
- iPhone/iPad .. https://support.apple.com/en-sg/HT202613

F.lux là một trong những phần mềm tiên phong trong lĩnh vực này, có hỗ trợ các hệ điều hành phổ biến:
Linux (also available for Windows Mac iPhone/iPad Android)

redshift là một phần mềm tương tự, có thể cài bằng apt trên 16.04:
sudo apt-get install -y redshift
Chạy redshift, màn hình sẽ tự điều chỉnh màu sắc dựa theo tọa độ địa lý của bạn.


Gì cũng được, miễn có là được.

Di chuyển trên less - xem web bằng w3m

less là lệnh để xem file (chỉ xem, không sửa).
less là phiên bản cải tiến của "more", `less is more`.

Có thể bạn chưa bao giờ gõ less, nhưng nếu bạn đã gõ man, tức là bạn đang dùng less.

Trong less hỗ trợ phím chuyển trang của cả emacs lẫn vim.
Với vim, đó là Ctrl f (forward - cuốn xuống) và Ctrl u (upward - cuốn lên).
Với emacs, đó là Ctrl v và Meta v (thường là Alt-v)  - với chữ v giống dấu mũi tên xuống, và alt tạo ra hiệu ứng ngược lại với xuống - tức là lên.

Do các tổ hợp phím (key binding) emacs được dùng khắp mọi nơi trên *NIX, sử dụng Ctrl v Meta v giúp bạn di chuyển ở hầu hết các chương trình *NIX khác, có thể kể tới trình duyệt w3m.



w3m là trình duyệt trên terminal,