惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
罗磊的独立博客
爱范儿
爱范儿
Apple Machine Learning Research
Apple Machine Learning Research
S
Schneier on Security
V
Vulnerabilities – Threatpost
T
Tenable Blog
博客园 - 【当耐特】
博客园 - 聂微东
I
Intezer
S
Securelist
C
Cisco Blogs
腾讯CDC
T
The Exploit Database - CXSecurity.com
NISL@THU
NISL@THU
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
Security Archives - TechRepublic
Security Archives - TechRepublic
D
Darknet – Hacking Tools, Hacker News & Cyber Security
美团技术团队
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
AWS News Blog
AWS News Blog
A
About on SuperTechFans
Last Week in AI
Last Week in AI
Cloudbric
Cloudbric
S
Secure Thoughts
PCI Perspectives
PCI Perspectives
The Last Watchdog
The Last Watchdog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog RSS Feed
Recorded Future
Recorded Future
大猫的无限游戏
大猫的无限游戏
C
CERT Recently Published Vulnerability Notes
酷 壳 – CoolShell
酷 壳 – CoolShell
G
Google Developers Blog
The Register - Security
The Register - Security
博客园 - 叶小钗
博客园 - 司徒正美
博客园_首页
Google DeepMind News
Google DeepMind News
Hacker News: Ask HN
Hacker News: Ask HN
Spread Privacy
Spread Privacy
雷峰网
雷峰网
I
InfoQ
The Hacker News
The Hacker News
T
Threat Research - Cisco Blogs
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Schneier on Security
Schneier on Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

Speculative Branches

Exponentials in 3 Instructions Perfect Random Floating-Point Numbers A Cryptographically Secret Santa Time Programming for Lawyers and Jurors Five Nine Problems The Computer Architecture of AI (in 2024) The Knight Capital Disaster Abstraction is Expensive Contemplating Randomness Introduction to Micro-Optimization Rest in Peace, Optane Use One Big Server The Most Useful Statistical Test You Didn't Learn in School What Happened with FPGA Acceleration? Teach Your Kids Bridge Fixed Point Arithmetic You (Probably) Shouldn't use a Lookup Table Who Controls a DAO? Racing the Hardware: 8-bit Division The Meaning of Speed Performance Numbers Worth Knowing Constant-time Fibonacci Less-than-linear Fibonacci First Post
Python is Like Assembly
2022-03-06 · via Speculative Branches

Python and Assembly have one thing in common: as a professional software engineer, they are both languages that you probably should know how to read, but be terrified to write. These languages seem to be (and are) at opposite ends of the spectrum: One is almost machine code, and the other is almost a scripting language. One is beginner-friendly and the other is seen as hostile to experts. One is viciously versatile with tons of libraries and ports, and the other is ridiculously limited in its capabilities. However, when you are creating production software, both are the wrong tool for the job.

Python: Easy to Write, Hard to Read

You probably agree with me that you shouldn't be writing assembly code, but Python is much more contentious. Many people argue that Python is bad to use in production because it is slow, and that you will get better performance using something else. However, in most of the cases where I have seen Python make it to production, Python is not slow: the Python code is often gluing together some NumPy or TensorFlow functions, which are written in optimized C. Python users who care a lot about speed and need to run actual Python can reach for Cython or Pyston to solve their speed problems. The problem with Python isn't its speed.

The problem is that Python is a language that is optimized for writeability. Dynamic typing, playing it fast and loose with scoping, and syntactic whitespace are all nice features when you want to churn out a lot of code quickly. Having every error show up a runtime is not a big deal if you are expecting to be the person running the code you wrote. For that reason, it is a very popular language: you can feel very "productive" when you are churning out Python. However, if you are working on a large codebase or an old codebase, wortking with Python is a huge headache, especially when you need the code to run 24/7 at thousands of QPS.

Type information is immensely helpful to readers and maintainers, and guarantees about what type a variable can hold solve a lot of headaches. Many Python programs are not written with a lot of type discipline: variables can often hold many different types, and when you modify a program, you need to consider all possible types. Also, it can often be unclear how to access certain variables due to the odd scoping. This is actually pretty similar to assembly code: a Python variable is like an architectural register - it's hard to know what's inside without reading a lot of code.

An added consequence of all of the dynamism is that programs don't really know what a variable is and how to manipulate it until runtime, making it very hard to write helpful IDEs and analyzers similar to what you get with C++, Rust, Go, Java, or almost any other language. Those runtime errors also have a nasty habit of showing up only once code gets to production even when they would ordinarily be caught by a tool in another language.

For small code snippets, one-offs, and pseudocode, Python is an invaluable tool. For production, reach for something else.

Assembly Code doesn't do its Job

When people think of high-performance code, they often think of hand-optimized assembly. In limited circumstances, this is true. Nobody has ever argued that assembly code is readable or easy to maintain, so for our purposes, I am going to focus on the performance arguments, and why you should almost never reach for assembly when performance is critical.

More often than not, writing assembly language (and even C code) can cause you to miss algorithmic solutions to speed problems in favor of brute micro-optimization. Here are a few examples to try in C and assembly:

  • Write a function that produces the sum of every number from 1 to 10000
  • Write a function that divides a number by 57

For the first example, you probably wrote a loop in both languages. However, if you plug your C code into compiler explorer, you will find that clang will give you a closed-form solution when you compile it in C, resulting in a huge speedup. For the second example, you may have used a DIV instruction, or you may have known that you can do this with a MUL and a magic number. If you used a DIV instruction, clang will beat you again: it can compute the magic number and turn that into a multiplication. If you used a MUL instruction, congratulations on knowing the trick, but you had to compute the magic number yourself, didn't you? More subtly, hand tuning will often result in bad register allocation and code that doesn't play well with other code.

Also, there are a few huge readability problems with assembly code. One of these problems is that variables tend to exist as registers and memory locations, which can be ephemeral. The contents of a register are not immediately obvious without considering the previous code. Macros can go a long way toward code readability, but they do not take you all the way to the point where you have variable and function names names. This rhymes with one of the reasons why Python is hard to maintain: it's hard to know what your variables actually could be, and there is no verification of their state.

This is why you don't want to write assembly code directly: doing the work of a compiler yourself is hard, and the compiler has a good global view. In cases where you might want to reach for assembly to do something that is not native to a higher-level language, like computing a CRC32C or a carryless multiplication, there is almost always an intrinsic function you can use, so you never have to accept writing assembly.

Fully optimized assembly is UGLY, as it should be in order to go fast, but ugly code is hard to work with. Instead of writing (slow) human-readable assembly, let the compiler do the work. For critical sections, read the output of the assembler and tweak with intrinsics and hints when the compiler doesn't find the right code. There is almost nothing that needs fully handwritten assembly these days, but there is still a lot of assembly that needs to be read.

Readability is Paramount

All long-running software projects have one thing in common: the code is read a lot more times than it is written. When you decide what language to use on a project, readability should be a key factor in any piece of code that is more than a one-off. In other words, the main audience for the code you write is not a computer, but other programmers.

Python is a fantastic scripting language and a decent language for prototyping new applications, but unfortunately, it is a language designed to be written, not read. Like assembly languages, Python is a conversation between a programmer and a computer, and is ill-equipped for communication with other programmers at scale and over time.

If you must use Python in production, there are lots of attempts to improve the readability of Python by adding static typing and type annotations. These tools will likely be valuable, but they do not get you all the way. For now, I will continue to use Python as a scripting language and a tool for small one-offs, but I recommend that projects planning for longevity or significant scale start in something else.