duangsuse::Echo
774 subscribers
4.43K photos
135 videos
583 files
6.73K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): a19a0b
Download Telegram
duangsuse::Echo
华为公布的方舟编译器到底对安卓软件生态会有多大影响? - weishu的回答 - 知乎 https://www.zhihu.com/question/319688949/answer/648358786 我觉得华为应该研发出一种新『HuaMIPS』架构,推出限量款芯片,这样就不管目标机器平台,可以直接编译成机器码到处发布,直接执行了 😸 呵呵呵。
说到底,某些 #Android 开发者还是真对性能的误区太大了,以为『It's fast』就是一切

一般来讲,真正需要高性能计算的部分,比如 canvas, opengl,都是无 GC,C/C++ 之类的实现的,可能连 shared_ptr Rc 智能指针都舍不得用
C、C++ 的编译 native code 实现,GCC、LLVM 都是经过的编译器框架,足以生成让人满意的优化代码

而你们写的程序逻辑,别往自己脸上贴金了,对所谓的性能,都没啥太大需求

真正有需求的敏感点,必要的时候,很多人都直接用 JNI 实现或者并行化了(当然 JNI 本身调用检查也有不少开销,所以一般是大批量任务和内存敏感任务),只有傻瓜才觉得应用逻辑的『性能』提升可以改变『生态环境』

至于应用使用 JVM 平台函数库的性能,比如说,人工神经网络,再文本解析器,你当真以为普通的编译优化能对这个『一次一字符』的东西有啥,客户端要绝对速度,是不是给闪电侠用的手机?为啥不开发个 NeonJSON 出来?

我到是很想看看,华为的工程师如何弄出可以汇编出 ARM, x86 等一大堆流行支持架构都能解释『超性能机器代码』的汇编器,或者 dirty 一点,把安装包和更新补丁再加上个几兆 😉

如果真的有这样的黑科技,我真的要为蛤为五体投地啊。可怜也是做电子电路出生的,居然现在弄这种事情?

后面的知乎回答也说了,猾为的优化,主要还是一些『非高级分析编译优化』领域的事情,有一个『IO 密集模式切换优化』

至于 GPU Turbo,我来看就是 GPGPU 并行化技术,如果配合修改算法的话自然有很大提升,当真自大了?不知道这并不是首创?论文在哪里?

其他的优化,诸如视图缓存什么的,和编译原理没太大关系,主要还是 runtime

绘制策略、系统服务架构、异步、事件 IO
说到底还是系统图形动画架构的事情,和编译什么方舟啊圆粥的有啥关系?

什么 IO 计算分析,也不能说是多么高大上的程序分析,可以看看这个

猾为这瞎说能力,我也是佩服了,Android 业界没谁了呃。 🤪
CnEn.java
1.9 KB
#Java #algorithm 顺手写了个加空格垃圾线性算法... (跑路
CnEn.class
2.1 KB
具体的算法比较偷懒,开始的时候我用 java.io.Writer#write(int) 被扼了,这厮居然只取 int 的 low 16 bit 当一个 int 输出,后来我觉悟了,才学会 append 输出两个字符....
duangsuse::Echo
具体的算法比较偷懒,开始的时候我用 java.io.Writer#write(int) 被扼了,这厮居然只取 int 的 low 16 bit 当一个 int 输出,后来我觉悟了,才学会 append 输出两个字符....
第一次把 intchar 用(Unicode UTF-32),而且没有用 java.util.Scanner,而且没有莫名其妙拆分 int 为四个 byte 再去创建新 String 输出(有很严重的 GC 开销的,逃逸分析栈上分配除外),算是『轻量级逻辑』,开心(虽然本来就没有必要...)

全部使用 GNU nano 和 javap, Oracle JavaSE https://docs.oracle.com/javase/8/docs/api/ 完成
Main.java
1.4 KB
#Java Java 的 Swing 控件 布局 Pane 什么的有点麻烦... Dimen 知道但是 Rigid 的一些参数不知道... 今天到这里
duangsuse::Echo
Main.java
不如 Android Container 们方便(
Android 的 Widgets 可以直接 match_parent 什么的,非常方便...
duangsuse::Echo
#zhihu #recommended #China #school https://www.zhihu.com/question/320967006 🤣和呵呵呵哈哈哈
其实就 996.ICU 来说,我非常支持他们运动。 #China #Low

加班加点对身体不好,在现代的中国,维权本来就是正当的(正如之前的奔驰轿车车盖维权和超市找零维权一样)

况且某些公司的管理层,居然要求人去无薪加班加点地敲代码,这就是折磨人。

对于专业水平来说,不同的人会有差异,但是对于工薪和工作时间来说,不应该对所谓的『码农』有歧视,他们也是职员!
#week 那么我懒得再说其他的消息了... (基本想看的看完了,欸你们更新怎么这么慢?)

现在我说说我这周的设计(和之前的 Cat 程序设计语言),并且预期能在下午六点之前完成 Parserc 包(包括 RangeMap、SourceManager、BinaryStreamProcessor 和 TextCombinator)
写的太多不知从何说起...
时间还是不够啊

我不是把时间给虵(shé 🐸)了... QAQ

就先说绝句、二进制和字符流 Parser 框架 这些最令人 excited 的玩意

Parser 框架我还打算自己手写这些算法:

TrieTree (只需要 Trie,因为只是字符流嘛,Radix 树还不如自己手动 maybe 匹配解析... 虽然那样好像没有优化了)

RangeMap (到学校里想了会,发现还真不是那么简单的,滚动歌词什么的还真是可以简单点,但是 RangeMap 可以启用二分查找技术和区间碰撞检测,进而可以实现 NestedRageMap)
LinearRangeMap (线性查找的 RangeMap,充当演示,时间复杂度捉鸡)
HashRangeMap (空间开销捉鸡,而且不能给不可枚举的类型创建映射,比如 Rational Number)

BsearchRangeMap (这才是主流)
CollisionRangeMap (支持非边缘碰撞检测的 Bsearch)
FuzzyRangeMap (支持简单碰撞替换情况的 CollisionRangeMap,不过没有额外的碰撞类型计算)
NestedRangeMap (可以用来开发 IDE 的 CollisionRangeMap)

VersionizedMap (可以实现 Dynamic Scoping)
CollisionVersionizedMap (基于碰撞检测的 VersionizedMap)
HashCollisionVersionizedMap (HashMap,Hashtable 实现)

MultiMap (K -> Multiply Value 的 Map)
ReverseIndexedMultiMap (同上,还自动维护反向索引)

还有这些辅助的东西
MixedArrayLinkList (就是类似猾为那种『IO 热区检测优化』... 不过就是两种模式『Link 插入模式』『Array 访问模式』切换而已)
Pair, Range
ArrayMap
MixedArrayHashMap
RandomReadStream (完全保存输入流的所有已读字节,随时可以 mark / reset,即使输入流不让 Mark / reset)
StringView (覆盖 get, length, subsequence 方法的 CharSequence)


Cat 因为连一个解析器框架都没有不好定义语法呢(虽然已经有语法和语义稿了,足足写了五面多)
ApkPool 因为 GeekSpec 什么的技术都还没有准备好,就不能说了,不过我觉得如果有时间可以谈谈 Java 类的『行为 hashCode』是否可行
duangsuse::Echo
写的太多不知从何说起... 时间还是不够啊 我不是把时间给虵(shé 🐸)了... QAQ 就先说绝句、二进制和字符流 Parser 框架 这些最令人 excited 的玩意 Parser 框架我还打算自己手写这些算法: TrieTree (只需要 Trie,因为只是字符流嘛,Radix 树还不如自己手动 maybe 匹配解析... 虽然那样好像没有优化了) RangeMap (到学校里想了会,发现还真不是那么简单的,滚动歌词什么的还真是可以简单点,但是 RangeMap 可以启用二分查找技术和区间碰撞检测,进而可以实现…
RangeMap 其实是相当有用的数据结构,前几天我亲自算过的,二分查找算法优化可以实现!

所谓一个 RangeMap,有插入、删除、查询三个基本操作,就是给你一对 Comparable(Range)、以及他们的映射 Value,进行插入后,查询 i1 <= x < i2,或者说 x in range 就能获得之前的 Value

比如:

val offsetLineMap = RangeMap()

val str = "abc\n123\n!@#"

var startLine = 0
for ((i, c) in str.withIndex) when (c) {
'\n' -> { offsetLineMap[startLine..i] = str[startLine..i]; startLine = i+1; }
else -> {}
}

然后我们就可以利用 RangeMap,在 O(log2 n) 的时间复杂度(而不是 O(n) 辣鸡线性复杂度下)完成 offset -> line 的索引

offsetLineMap[0] -> "abc"
offsetLineMap[1] -> "abc"
offsetLineMap[2] -> "abc"
offsetLineMap[3] -> "123"
offsetLineMap[5] -> "123"
offsetLineMap[6] -> "!@#"
offsetLineMap[9] -> IndexOutOfBounds

当我们有了 NestRangeMap 这种逆天的数据结构(和普通 BsearchRangeMap 比更类似 HashMap 和 MultiMap 比)后,还能快速实现类似 IDEA 的辅助功能,给可能有嵌套结构的 AST(抽象语法树,JB 也有一个类似的 PSI,程序结构接口)加上索引

这样,只要我们能拿到 TextField 的光标 offset,就能做类似
+ 拿到光标下的语法嵌套结构,能够知道语法路径在哪

这样的事情 🤪

这种功能很重要吗?的确很重要啊,如果你用线性查找这种省事但不讨好的算法,或者不考虑 Range 的碰撞,是绝对没有这种福利的

然而 NestedRangeMap 就可以实现这种操作,让人为之一振

val documentOffsetNesting = NestedRangeMap()

val document = KommonMarks.parseString
("""
# H1
_a_ __b__ *c*
## H2
[name](url:)
""".trim())

/* Add pairs to nested range by document marker offsets */
documentOffsetNesting[0] => HeaderNode(children = [TextNode("H1")], n = 1)
documentOffsetNesting[5] => [DocumentSection(name = "H1", n = 1), TextDisplayNode(mode = Italic, text = "a")]
documentOffsetNesting[19] => HeaderNode(children = [TextNode("H2")], n = 2)

看到 documentOffsetNesting[5] 的返回值了吗?它能够提示某个 Markdown 元素的路径,是在哪个标签里

对于 NestedRangeMap 来说,它会自动处理任何的区间碰撞(被分成五个大类型),将碰撞的部分替换成一个列表

val rm = NestedRangeMap()
rm[0...10] = "0 to 10"
rm[2..6] = "2 to 5" // "Full Overwrite" collision from index 2 to 6

rm[0] => "0 to 10"
rm[2] => ["0 to 10", "2 to 5"]

程序员在写 SourceManager 的时候,就只需要把某个语法结构的 startOffset 和 endOffset 记录一下就好了,NestedRangeMap 可以自动完成代码路径索引创建任务
duangsuse::Echo
写的太多不知从何说起... 时间还是不够啊 我不是把时间给虵(shé 🐸)了... QAQ 就先说绝句、二进制和字符流 Parser 框架 这些最令人 excited 的玩意 Parser 框架我还打算自己手写这些算法: TrieTree (只需要 Trie,因为只是字符流嘛,Radix 树还不如自己手动 maybe 匹配解析... 虽然那样好像没有优化了) RangeMap (到学校里想了会,发现还真不是那么简单的,滚动歌词什么的还真是可以简单点,但是 RangeMap 可以启用二分查找技术和区间碰撞检测,进而可以实现…
MultiMap 呢? 🤔

比如说,

很多时候,我们可能想写一些类似『CodeStyle』这样的程序,
更近一步,我们(可能)希望 IDE 有这样的功能:实时检查 CodeStyle,一个空格都不放过,并且能自动修复

我们可能希望,解析器能做最少的工作(比如,如果程序结构实际上没有被修改,修改的只是空格或者说字面量)
在不需要的时候,尽可能只做增量解析

我们可能希望,能知道程序代码在哪里有一些注释,有些内联文档

我们可能希望,能够通过某一个位置的 '{' 字符找到对应的 '}' 字符位置(比如,代码高亮的时候就用的到)
怎么找妥帖?应该先找到字符对应的语法结构,再找到这个语法结构的 '}' 字符

我们可能希望,把排错信息存储在一个『其他的』地方,而不是 AST 结构类本身里面(就是使用 SourceManager 存储 SourceLocation)
而且不仅仅这样,我们甚至还希望能够通过 SourceLocation 拿到某种语法结构,来实现索引功能(虽然 RangeMap 也可以实现,不过这种虽然没啥可行性,性能还更高)(没有可行性是因为对于 val n = 1 这种,用 SourceLocation 反向索引要求你只能选择一个『字符位置』索引到正确的结果,比如 val'v' 字索引到了,光标指到后面的 '1' 的时候就啥都找不到)

这些通通需要 Reverse index,有些再使用 RangeMap 就可以优雅地实现了(因为 AST 一般只会保留程序语义相关的内容,文档什么的都是忽视的,反向索引才行)


比如说上面的空格查找功能和 Marker highlight 功能

空格查找其实还真没有那么容易实现,不过靠 SourceManager 其实也不难,只是不容易『优雅』地实现

比如说,考虑以下简单形式化语法:

let whiteSpace = [ \n\r\t]
let parser = terms

nl = [\n\r]
terms = term*

term =
<def> name <=> value <nl>

name =
[A-Za-z0-9_]+

value =
string | number | boolean | nil

string = <"> .* <">
number = [0-9\.]+
boolean = <true> | <false>
nil = <nil>

这是一种描述方法,其中,每个重复的『终结符』都会留意之后的『终结符』是否匹配,如果匹配则自己结束匹配

比如
string = <"> .* <">

其中的 .* 自动先行判断后面的 <"> 是否 match,如果不 match 自己才能继续下一个,否则返回

假设我们有如下示例程序

def num = 1
def str = "Hello, world"
def boo = true
def null = nil

现在有一个人名叫小明,是个辣鸡程序员,非常喜欢捣乱,就把代码弄成了这个 B 样 😠

def
num = 1
def str
= "Hello, world"
def boo =
true
def
null = nil

这 TMD 还能看!
傻逼的人的选择必须是自己一个空格一个换行的删除,『手工』修复这段代码的缩进
聪明人的选择可能是 CodeStyle 自动 Format,可是,这是小明自己写的『编程语言』,你找不到支持这语言的格式化器
更笨点的人的选择可能是弄一个 DumpVisitor,手动格式化代码,但是很可惜,小明用的 IDE 非常脑残,它不推荐使用这种方式(直接 setCode(String)),一定要你手动生成修改代码的方案才行

我们现在的问题是如何生成一个修改列表(只有“在某个 offset 删除 n 个字符”这一个操作)
帮助小明的同事修改好这个程序 😉
duangsuse::Echo
MultiMap 呢? 🤔 比如说, 很多时候,我们可能想写一些类似『CodeStyle』这样的程序, 更近一步,我们(可能)希望 IDE 有这样的功能:实时检查 CodeStyle,一个空格都不放过,并且能自动修复 我们可能希望,解析器能做最少的工作(比如,如果程序结构实际上没有被修改,修改的只是空格或者说字面量) 在不需要的时候,尽可能只做增量解析 我们可能希望,能知道程序代码在哪里有一些注释,有些内联文档 我们可能希望,能够通过某一个位置的 '{' 字符找到对应的 '}' 字符位置(比如,代码高亮的时候就用的到)…
解决小明可怜同事的问题,首先我们必须得解析这段代码,但是解析的时候还不能忽略空格!

用伪代码,我们这么设计解析器(输入就是字符流,Scanner less parsing):

terms :: Parser [Definition]
terms = many term

wsP = satisfy $ `elem` " \n\r\t"
nameP = satisfy $ \x -> x == '_' || x `elem` 'A'..'a'
valueP
= keywordP "nil"
++ booleanP
++ numberP
++ stringP

term :: Parser Definition
term = do
keywordP "def"
wsDefName <- wsP
name <- nameP
wsNameEq <- wsP
keywordP "="
wsEqValue <- wsP
value = valueP
wsValueEnd <- wsP
newlineP
return $ Definition name value

这... 我不是来教大家写 Parser Combinator 的,也不是来传道 Haskell 的(我连 Monad 的什么 MZero 啊 MPlus 啊都不知道,感觉就是顺序 Application 的一种方式,勉强一知半解范畴论)

所以我们来看看小明同事的机制解决方案:

小明的同事解析出了可怜小明编写的可怜代码,下面是一部分:

definitions !! 0 == Definition "num" (Number 1)
definitions !! 1 == Definition "str" (String "Hello, world")

可是,机制的同事注意到了,这里没有给出任何的空格信息?那怎么判断呢?

于是还记得上面的 wsXxx = wsP 吗?

wsP :: Parser String

它会返回记录空格的,于是机制的同事利用了多年的 Haskell Monad 经验,单子化了生成算法,引入了优美的线性... 反向索引,这里实现大概就是『把所有的 Value 拿去 collect 对应的 key,然后再 put 回来』
或者直接『对每一个 key,都 putAll(values, key)』

于是就有了
getAll spaces $ definitions !! 0 == [wsDefName "\n", wsNameEq " ", wsEqValue " ", wsValueEnd ""]

小明同事检查了一下空格:
filter (not . (flip elem) " ") [wsDefName "\n", wsNameEq " ", wsEqValue " ", wsValueEnd ""]
=> [wsDefName "\n", wsEqValue " "]

0 不是空格,应该删除!
1 比预期的 1 空格多了 2-1=1 空格!

在两个 ws 的起始位置删除 1 个字符就万事大吉了,今晚吃鸡(喷

这下小明同事解决问题了,只要我们使用 MultiMap 存储空格、文档之类的信息,就可以做到检查代码风格这种小事情