最近研究 python 的一个小失落

2016 年 8 月 1 日
 SlipStupig

python 有一个很-O 选项我一直很好奇这个选项是干嘛的, help 写的是:

 -O     : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x
-OO    : remove doc-strings in addition to the -O optimizations

python 优化选项可以产生更小的 bytecode 文件,我试着选了一下,确实小了一点,但是对性能提升并没有什么用,后来看官方邮件是这么回复的:


> Py_NoSiteFlag = 1...assuming you don't need to load site.py 
>
>     for example:</br>
>
> extern int Py_OptimizeFlag;
> extern int Py_NoSiteFlag;
> ...
> if( !Py_IsInitialized() ) {
>     Py_OptimizeFlag = 2;
>     Py_NoSiteFlag = 1;
>     Py_Initialize();

最后结论是 python 优化选项并没有什么用,想加速换 pypy

33566 次点击
所在节点    Python
268 条回复
gowk
2016 年 8 月 15 日
哈哈,这都能撕起来,西乔的『神秘的程序员』诚不欺我也!
FrankHB
2016 年 8 月 15 日
@wizardforcel 是,光提性能,垫背的多了。然而你提的这里除了 js 哪个都没有那么作,显然相对比较老实,应用领域比较固定。
js 自己有另外的大波槽点所以在这里不需要怎么强调。
另外你对语言实现的理解确实有些拙计。
分析运算符不需要是验证 grammatical rules 的 parser 的工作——的确“复杂性都在后面”但是对付起来比改 parser 方便太多了(即便绝对工作量不一定小);
复杂度需要考虑,但即便是设计实现 DSL ,通常也不会是需要最优先考虑的事项;
写死运算符要通用容易增加维护成本,不仅体现在语法上,而且影响语义规则的复杂性;
对于通用目的语言,即便只考虑语法,按你大大咧咧的策略,一不小心很容易没法“这个位置就应该是个 token ,不是就不合法”(典型反面教材: C 、 C++、 Haskell ……)。

@serial 其实实际项目确实很多时候不需要写 BNF ……简单的文法都自己手撸 parser 了。
虽然我在项目里钦定语言 spec 的时候还是加了点 formal grammar ,但也得结合项目需求而不是直接套用 BNF 。
而 Lisp (严格来说,仅指使用 S-expression 为基础 syntax 的 dialects )在这里更简单之一正是因为不需要 BNF ,随便什么 CFG 都能应付,简化成纯 S-expr 直接都不需要归纳 grammar 无脑 LL 就行了。
FrankHB
2016 年 8 月 15 日
@serial 你对编译器理解也是有问题的。编译器输入的源语言不见得就得是文本流的形式,比如可以是已经 parse 过的 AST ,这种情况就不需要处理 delimiter (要处理也是代码里自己实现的逻辑)。
大部分主流语言的确要求源代码是文本,但既然考虑 Lisp 就没法直接这样断言了。
2225377fjs
2016 年 8 月 15 日
@appleorchard2000 说的挺好的,不过个人感觉,对于 Python 语言而言,就不要去考虑并行多线程了,虽然看起来是挺美好的,但是,对于绝大多数程序员来说,根本就不能实现正确的并行程序,例如 Java 程序员,其实绝大多数也都是在业务逻辑而已。。。 Python 直接就不支持并行多线程也挺好的,对于大多数系统,业务层好好的设计,用相对独立的模块来实现,减少业务模块之间的交互,这才是正道。。。 linux 身就更强调多进程的结构来解决任务。。


@serial Python 真没你说的那么不堪,起码我在的公司就大规模的使用 Python 来实现后端系统,而且还是游戏后端。。。至于你说的分布式啥的, Python 不是没有,而是你不知道,而且 Python 圈子这方面的东西确实少,跟 Java 没法比。。。每秒几千次请求啥的 Python 做不到。。?有点太看不起 Python 啊。。。。

Python 慢是慢在它本身是一个完全对象化的语言,这点比 Java 做的还要纯粹,而且有没有 jit ,不过话说回来,绝大多数应用场景还没有需要来拼语言速度的,如果最后发现语言速度是问题,多半是采用的技术方案有问题,而不应该让语言来背这个锅。。
wizardforcel
2016 年 8 月 15 日
@serial 代码拿好不谢。

tokenRE = /^[A-Za-z_]\w{0,31}/;
numeralRE = /^\d+/;

function matchAndReturn(str, re) {
var r = str.match(re);
return r? r[0]: null;
}

isToken = s => matchAndReturn(s, tokenRE);
isNumeral = s => matchAndReturn(s, numeralRE);

function parse(expr) {
var r = parseExpr(expr, 0);
return r? r[0]: null;
}

function skipBlank(expr, at) {
while(expr[at] === ' ')
at++;
return at;
}

function parseExpr(expr, at) {
at = skipBlank(expr, at);

//literal
var r = isNumeral(expr.substr(at));
if(r) {
at += r.length;
return [parseInt(r), at];
}

//call
return parseCall(expr, at);

}

function parseCall(expr, at) {
var arr = [];

//operator
at = skipBlank(expr, at);
var r = isToken(expr.substr(at));
if(!r) return null;
arr.push(r);
at += r.length;


//'('
at = skipBlank(expr, at);
if(expr[at] != '(')
return null;
at += 1;

//operand 1
at = skipBlank(expr, at);
r = parseExpr(expr, at);
if(!r) return null;
at = r[1];
arr.push(r[0]);

//other operands
while(true) {
//','
at = skipBlank(expr, at);
if(expr[at] != ',')
break;
at += 1;

//operand
at = skipBlank(expr, at);
r = parseExpr(expr, at);
if(!r) return null;
at = r[1];
arr.push(r[0]);
}

//')'
at = skipBlank(expr, at);
if(expr[at] != ')')
return null;
at += 1;

return [arr, at];
}
wizardforcel
2016 年 8 月 15 日
@serial

你不就认为'('这个东西能“定界”嘛??我问你定界是为了什么??那好,我让规则尽量少,不是 literal 就是调用,还需要这个定界符干什么?

你也知道 token 解析需要时间,你怎么不说你的 literal 解析还需要时间呢?而且规模一多我就不信你的表达式不抽象成各种过程,到时候+-*/就不够用了,你还得用 token 都命名,这是一百步笑五十步??(手动 doge )

滚回去做你的工程吧,别忘了下次来的时候带上 IO 的跑分。

@FrankHB 这个讨论是有前提的,也就是“整个程序是一个表达式”,显然通用语言有各种语句,不符合前提。

既然是一个表达式,那么就可以针对性地优化,即完全使用调用表达式把优先级捋直,从而使机器可读。由于它不是 literal 就是 call ,那么我就判断完 literal 就可以钦定。

我的 BNF 也是为了不使用 parser 就能简略表明我的意图。现在看起来也不必要了。
serial
2016 年 8 月 15 日
@FrankHB

拜托,你搞清楚编译器由什么组成?首先,第一步,你要解析。解析的目标是什么,是一大堆文本。更通俗点,是一大堆字符串。你怎么在字符串中解析出对象?没有 delimiter 你以什么作为参照解析?
serial
2016 年 8 月 15 日
@wizardforcel

1. console.log(parse("(* (+ (- 1 2) 3) 4)")) 打印出来是 null

2. tokenRE = /^[A-Za-z_]\w{0,31}/; isToken = s => matchAndReturn(s, tokenRE); 使用 JavaScript 内置的正则表达式解析器,要点脸可否,你用别人的 delimiter 解析器,然后说你的扫描器不需要 delimiter

Do you 要点脸 ok ? 你的代码就是一坨 shi ,出来大大的 null 。
serial
2016 年 8 月 15 日
@wizardforcel

我再写个文件,然后把我写的 parse 解析器封装下,跟别人说:“看啊,我的扫描器不需要 delimiter ,里边没有任何判断”。就像你这样?是不是个傻逼?
serial
2016 年 8 月 15 日
@wizardforcel

把哥写的代码复制到 node 控制台,好好体会。你,太嫩。
wizardforcel
2016 年 8 月 15 日
@serial

懒得理你了。

1. 我的代码解析“ mul(add(sub(1, 2), 3), 4)”,不解析你的 sb 前缀表达式

2. 你没用正则? istoken 和 isnumeral 很容易拿 while 来写出来,我就不多说了

3. 你的 literal 怎么解析的??还不是用循环判断非空白字符??那跟我判断字母和数字有什么区别??可笑。


实际情况就是,你的 isblank 和我的 istoken 复杂度差不多,自己好好想想,别他娘的整天自以为是。
wizardforcel
2016 年 8 月 15 日
@serial

你的表达式括号之前是不用判断,后面是不是还得判断非空白字符??跟我判断字母数字有啥区别??我只不过是把括号和第一个操作数换了个地方,你这弱智就理解不了了。

我就是看不惯前缀表达式,跟你这傻逼说道说道,没想到你丫就是故意来找茬的。 你他娘的没用正则??双标狗爱滚多远滚多远。
FrankHB
2016 年 8 月 15 日
@wizardforcel “显然通用语言有各种语句,不符合前提”显然是错的。语句是 Fortran 发明的玩意儿。典型的 Lisp 就不管你什么语句。实际上语句完全可以通过表达式求值规则等效出来,没有语句的设计明显还更通用一点。
另外你搞混了太多东西。干掉或者不干掉优先级纯粹是语法上的选择,而是不是能钦定规约得看语义规则怎么设计,后者至少不要指望用 BNF 能搞定。如果你知道怎么给出形式语义以使之和语法的形式一致,你就不该纠结 literal 或者“语句”才对。
@serial “解析的目标是什么,是一大堆文本”这个显然也是错的。之前提过了,一般意义上的 Lisp 就不要求输入是文本,这不表示 Lisp 不能编译。还有纯位图的语言呢。连 tokenize 都不必要的情形谈何 parse 。
当然,你可以像 FSF 一样争辩定义“源代码”的“人类可读”的具体形式是指的什么,不过人家是有政治目的的。我不了解技术上存在什么必要理由去缩小“编译器”的外延。
serial
2016 年 8 月 16 日
@wizardforcel

用 JavaScript 解释器内置的正则表达式,然后说自己的扫描器不需要 delimiter ,这充分说明你 TM 压根就不懂编译原理。

说了半天,跟一个小白浪费时间。
serial
2016 年 8 月 16 日
@FrankHB

你傻呀,还是傻呀?我请问,你解析什么东西? 不是字符流,是什么? 就连最简单的 0 1 也 TM 是二进制流。

麻烦你先搞懂计算机的常识。

另外,你用别人解析出来的 AST ? 别人都解析出 AST ,还关你个屁事?然后你再去写个扫描器扫描人家的 AST ?一点常识都没有,好不好。
serial
2016 年 8 月 16 日
@wizardforcel

睁开你的狗眼看清楚,什么是正则表达式。傻逼玩意。正则表达式里边有没有 delimiter ?草拟妈的逼,有没有 delimiter ???

自己用着别人的 delimiter ,瞎着眼睛说没有 delimiter ,你是你妈的逼呀?全局扫描用正则表达式,还用你写啊!!!

吗隔壁不会写扫描器,就别 TMB 装,把你爹写的代码打印下来贴在你家里好好 TMB 学习学习,别 TMB 出来丢你爹的人。
serial
2016 年 8 月 16 日
@FrankHB

纯位图你妹啊,计算机知道什么是纯位图? 你连起码的计算二进制原理都没搞懂,大学还没别业呢?

哥告诉你个常识:纯位图是 TM 人类写的解析器 --- 解析一个文本到协议规范的二进制流。计算机只认 TM 的二进制 0 1 , understand ?
wizardforcel
2016 年 8 月 16 日
bombless
2016 年 8 月 16 日
想起来 ruby 有好几个项目想让 ruby 快起来,最终还是没啥卵用
我觉得你们还是要学习一个,给这些开源工具多做贡献,不多试试怎么知道必然会失败呢(
serial
2016 年 8 月 16 日
@wizardforcel

不要正则么? 再来个 lambda 表达式? 给你爹跪舔:

var text = "(* (+ (- 1 2) 3) (lambda (x y) (* x y)))";
var at = 0;
var ch = text[at];

function next() {
if (at++ >= text.length) {
throw new Error("text tail out");
}
ch = text[at];
}

function space() {
while (ch === '\n' || ch === '\t' || ch === ' ') {
next();
}
}

function word() {
var result = "";
while (ch !== '\n' && ch !== '\t' && ch !== ' ' && ch !== ")") {
result += ch;
next();
}
if (at < text.length - 1) {
next();
}
return result;
}

function parse() {
space();
if (ch !== "(") {
return word();
} else {
next();
return [parse(), parse(), parse()];
}
}

var ret = parse();
console.log("%j", ret);

// ["*",["+",["-","1","2"],"3"],["lambda",["x","y",["*","x","y"]],""]]

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://study.congcong.us/t/296233

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX