V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  secondwtq  ›  全部回复第 65 页 / 共 123 页
回复总数  2460
1 ... 61  62  63  64  65  66  67  68  69  70 ... 123  
2020-01-06 19:36:09 +08:00
回复了 PainfulJoe 创建的主题 支付宝 支付宝年账单出来了,自己年支出 12w,干了一年没攒钱
@evam
把理财也算支出意思就是买的理财就是韭菜 :)
2020-01-05 21:55:23 +08:00
回复了 Cloudmxy 创建的主题 AMD AMD 压根没这么香...装了驱动小毛病不断...翻车...
楼主啥时候装的?
我被 intel 的 Linux 驱动问题折磨好几十天了
自己看: https://bbs.archlinux.org/viewtopic.php?id=250765
2020-01-02 22:44:36 +08:00
回复了 JCZ2MkKb5S8ZX9pq 创建的主题 git 关于 git 项目拆分的精度问题
楼主的工具之间应该是没啥依赖关系的,用 monorepo 必要性不大
不过全拆了太麻烦,所以可能只能 monorepo …
2020-01-02 22:10:35 +08:00
回复了 Whsiqi 创建的主题 写周报 2020 不会再写任何一行代码
NGA 用户表示飞行员苦不苦不知道,但是前途一定是牛逼的
等做到机长就有你享受的了
2020-01-01 18:39:24 +08:00
回复了 manami 创建的主题 分享发现 不可能完成的任务:在图片上打上它的 md5 哈希值
碰撞这个理论上倒是可行,问题是发到网络上(尤其是国内网络)基本都会有一道压图,格式也可能会转。我觉得不行
2020-01-01 18:31:04 +08:00
回复了 sandman511 创建的主题 程序员 Java :如何处理空指针?
这不就是一个 Maybe Integer 解决的问题 ... 搞这么复杂
说来话长 ...

首先明确一点,语言的 spec 里一般不会关心堆 /栈这些问题。也就是说楼主最开始的问题“reference values 一定在堆上,primitive values 一定在栈上吗?”的答案是否定的。

然后说实现问题,JavaScript 是动态类型语言,虽然动态类型语言在语言本身定义和实现上和静态类型语言有很多不同,但是动态类型语言的编译器实现技术和静态类型语言没有本质上的差别。考虑静态类型语言和动态类型语言之间,最重要的区别是什么?当然是在动态类型语言中,一个变量的类型只能在运行时确定,或者说所有的 type checking 都发生在运行时。

主流的动态类型语言优化编译器实现最大的区别是引入了一套新思想:在编译的过程中逐步去除语言的动态性,并对于 hotspot,根据上下文和运行时信息对类型等信息做 speculation,对常见的 path 做 specialization,在执行这些 specialized code 之前做 check,如果和预期不符则进行 deoptimization。其实也不算新,静态类型语言做 PGO 之类的跟这个很类似。

但是无论是用解释器还是 JIT 优化编译器的方式实现动态类型语言,最后在一方面的结果是相同的:就是最后生成的代码和数据结构都可以转换成某种静态类型语言(不特指 C,因为存在结构体布局等一般是非标准的细节,另外 deoptimization 应该也不能以标准 C 来表示)。也就是说可以用静态类型语言来模拟动态类型语言——比如定义一个类型叫 any,这个类型实现了所有的操作符,同时还可以包含任意的字段。然后在运行时调用的时候检查是否合法。

然后楼主的问题就转化为:这个 any 类型应该怎么存储? any 实际相当于一个 Union Type,但是我们知道 C/C++ 里面的 union 是不 type safe 的,原因是 C/C++ 里面的 union 本身并没有存储类型信息,C/C++ 项目中所有用到 union 的地方,要么是在其他地方存储了类型信息,要么就是依赖于某种 UB。动态类型语言要想做到“在运行时进行 type checking”,就必须在存储值的同时存储对应的类型信息(否则就退化成无类型语言了),在静态类型语言里面存储类型信息的例子并不少,比如 C++ 的虚函数表就属于这类东西,同理 Java/C# 的对象会有对象头信息,甚至会把整个类型塞进去做反射用。

在这里可以对这一堆东西做个简单的区分:Java/C# 如果不考虑 boxing,它们的 Object 类型结合反射也可以用来模拟动态类型( Java 更统一一点,C# 本身有个 Dynamic 类型,还有 struct 之类的)。C++ 的虚函数 /RTTI 则是比较受限的一种形态——只限于预定义的有限的几个函数,只能在预先声明的 class hierarchy 里工作。

Java/C#/C++ 的这些机制有一个共同的缺点就是:在实现中一般会对一些常见的类型做优化,因此语言中会出现 primitive type 和非 primitive type 的区别(此处的 primitive 不指 JavaScript 中的 primitive,比如 String 在 JavaScript 中按照标准是 primitive,但是在 Java 和 C++ 里面都只是普通的类而已),primitive type 无法直接利用这一机制,Java/C# 用 boxing 来绕过了这一限制,C++ 则需要手动实现 boxing (何况本来就是受限的)。

Haskell、OCaml、Rust、Swift 等语言则基于 Algebraic Data Type,提供了 tagged union + pattern matching。这一套机制可以直接 cover 所有的类型,同时也不碍着编译器做优化。TypeScript 的类型系统允许用户手动实现这一套机制。其弱点是在实际应用中的扩展性受限,Scala 的 case class 则是另一个有趣的结合体,学术界另有若干论文尝试解决这个扩展性的问题。

tagged union 这个扩展性问题具体说来就是:一个 tagged union 内部包含哪些选择,在声明时已经确定,不能像 Java 那样通过声明新类来添加新的类型。( OOP 另有自己的扩展性问题,这是另一个 topic,此处按下不表)这个问题带来的一个好处就是 tagged union 所占大小是可以在编译时确定的——在 C 的角度,最简的通用实现就是一个 struct 里面放了一个 union,但同时还放了一个字段表示当前使用的是 union 中的哪个类型,并且编译器帮你去做转换和检查工作。

从实现角度看,可以说 Java/C#/C++ 更偏向于 pointer dispatch,tagged union 则是 tag dispatch。这是两种基本的实现手段。在实践中,tag dispatch 可以在堆上也可以在栈上,pointer dispatch 一般和堆分配有更强的关联(但是理论上,实现动态行为的机制和内存分配的机制是正交的)。但是要特别注意的是,tagged union 的实现一般都是以 C/C++ 的 union 为基础,所以 C/C++ 的 union 可以怎么分配,tagged union 就可以怎么分配。

动态类型语言的 “any” 类型在绝对能力上则是最强的——可以包含一切类型,可以包含一切数据和操作。但是这个类型该如何实现呢?最后还是要落到上面两种实现手段上。(以上说的这些都是 type safe 的,C/C++ 的裸 union 因为不 type safe 被排除掉了)

类 Java/C# 的这种模式,在一个大家很熟悉的动态类型语言里面被应用了:就是 Python 的 CPython 解释器实现,在 CPython 中,所有的数据都存在堆里面,CPython 的解释器栈仅仅会存储对堆数据的引用,就算是 primitive type 的值也会变成 PyIntObject、PyFloatObject。这些堆中的 object 的文件头会存储类型信息,在应用某个操作符的时候调用某个函数之类的。

注意 JavaScript 也可以使用同样的方式实现。当然大家也知道 CPython 是出了名的慢(毕竟每一个操作都相当于过了一次 Java 的反射),这个现在的 JS 是没法接受的,你实现那么慢我们的垃圾代码怎么办?(什么你说优化代码?对不起我们只会写垃圾代码)那就要考虑进行优化,那这个 object 可不可以放在栈上呢?

我前面说了这个 object 相当于一个 any 类型,即所有类型的 Union。一般在 C/C++ 中,union 所占的空间由其所有类型的大小的最大值决定(还要根据对齐调整一下)。any 类型理论上包含所有类型,而用户理论上可以定义无限大的类型——结果是如果同样按照 Python 一样教条式的做法,坚持把数据全放栈上不动摇,any 类型只能无限大,不存在足够大的内存可以容纳这个类型,直接 Game Over。

解决方法就是改革开放,哦不变通一下——一般做法是由语言的实现钦定一些“primitive type”(注意此处的 primitive type 依然和 JavaScript 的 primitive 无关,而是实现角度的 primitive )使用栈分配 + tag dispatch,其他类型(包括用户定义的类型)作为“reference type”使用堆分配 + pointer dispatch。这和静态类型语言编译器针对一些 primitive type 做优化的思想是一样的。这样做的结果是,按照“union 所占的空间由其所有类型的大小的最大值决定+对齐调整”的规则,在 64 位机器上,假设钦定 bool, (unsigned) int 8/16/32/64, float, double 为 primitive type,再加上 reference type 所需的 64 位指针,这个 tagged union 的数据部分只需要 64 位就行了。前面说了单纯的 union 没用,还需要存储类型信息,那这个理论上需要 4 位来存储,实际会对齐成 ... 64 位,就是说一个值放栈里,占 128 位 /16 字节的空间。

注意上面两段默认了一个公理,就是“union 所占的空间由其所有类型的大小的最大值决定+对齐调整”。因为前面说了“动态类型语言的编译器实现技术和静态类型语言没有本质上的差别”,而这个条件在大多数静态类型语言中都是成立的。于是才有了动态类型语言使用真·所有类型的 union 不可行->使用 primitive/reference type 做优化的结果。理论上,动态类型语言可以根据自己的特点,把值所占空间缩减到 实际数据大小+类型信息(也就是推翻以上公理),这么做的一个后果是:any 类型的大小不是无限的了(除非用户真的定义了无限大的类型),但是依然是不可确定的。“不可确定大小的类型”如何进行优化是少有先例的——比如在 C/C++ 中根本不允许使用 incomplete type 创建对象,唯一的例外是 C 里面极其受限的 VLA。

上面最后还有一个问题,就是 tagged union 实现方式中,4 位的类型信息最后会被放大成 64 位,很大一部分空间被浪费了。这个同样有一个通用的做法,就是利用各种骚操作把至少为 2*sizeof(intptr_t) 的值大小优化成 1*sizeof(intptr_t)。这个具体的做法,Andy Wingo 称为 “value representation”,并且写了一篇文章来讲这个事情( https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations )。简单来说就是:现在的 64 位平台虽然名义上有 64 位地址空间,但是实际上只用了 48 位,另外 16 位是空的,所以可以拿 1 位来区分 reference 和 primitive ( 32 位平台没这福气,但是有一些其他的机会比如语言实现可以做一个“所有内存分配都会对齐到 4/8 字节”之类的保证,这就留了两位出来),但是这样就又有了其他的限制——原来 64 位的整数,有 1 位不能用了,那没办法只能用 63 位整数。另外只有 1 位用来存储类型信息就不能区分整数和浮点——相当于只区分了 63 位整数和指针。这些又可以有其他的优化手段。值得一提的是 JavaScriptCore 的做法:因为 JS 中只有 double 一种数值类型,所以它利用浮点类型的特性,在 64 位机器上实现了一个 64 位值对 64 位指针和 64 位浮点两种类型的区分 ... 实在是高

但是绕来绕去,最后达到了一个目的:就是实现了一个固定大小的 any 类型(至于这个“固定大小”到底有多大是另一个问题,但是一般是实现为 1*sizeof(intptr_t))。我把这个叫做“uniform representation”。这个属性不仅对动态类型语言有用,对静态类型语言也有用。比如 GC 需要这项能力来区分指针和非指针。Java 通过 boxing,事实上也实现了 uniform representation,并且 Java 的泛型依赖于此来实现。同样的做法存在于 Haskell 和 OCaml 中(所以在 OCaml 中可以看到 int63 这种奇葩类型 ... OCaml 也有 int64,但是是 boxed 的)。实现 uniform representation 会方便更多动态语言特性的实现(简单的泛型可以不依赖 uniform representation,用 Monomorphization 来实现,但是 Higher Ranked Polymorphism 之类就没这么简单了)。

如果要对楼主的问题简单给一个答案的话,那就是:在具备 uniform representation 的语言实现中,被钦定的那部分 primitive 是 有可能 放在栈上的,reference type 是 一定 放在堆上的。

这个结论同样假设了一个前提:根据上文,reference type 的定义仅仅是“除开 primitive type 之外的所有类型”,在 any 类型中借助指针来表示,连 primitive 都不一定是分配在栈上,reference type 又怎么能钦定放在堆上呢?编译器能不能把一部分 reference type “优化”在栈上呢?

理论上是可行的。比如静态类型语言编译器里面有一种优化叫 Scalar Replacement of Aggregates ( SRA/SRoA ),简单来说就是满足一定条件的前提下,把一个复合类型里面的字段展开成单个的变量。在静态类型语言中这有利于进行进一步的优化,在存在 reference type 的语言中,成功的 SRoA 还可以减少内存分配、访问和 GC 压力——你想想如果一个 reference type 值里面所有的值都是 primitive type,SRoA 之后不就相当于消灭了原来的 reference type 值,全变成 primitive type 了么。

但是优化必须保证不影响原有语义。也就是说编译器不仅要实现 SRoA,还只能在经过分析确保安全的情况下才能进行。所有的优化都是可选的,编译器就像一个尸位素餐的用户公仆,觉得可能有问题就放弃优化——和人类的公仆不同的是,机器公仆虽然在执行时同样的懦弱,但是之前会尽量根据已有的信息做分析(对于 SRoA 来讲主要是 Escape Analysis 和 Alias Analysis )。这里就涉及到动态类型语言和静态类型语言在非技术层面设计思路的根本分歧:动态类型语言的设计者不仅不想让用户提供类型信息,也同样认为程序员不需要提供除了所谓“程序本身”之外的信息。动态类型语言的用户上行下效,通常也不屑于提供此类信息。静态类型语言则认为自由来源于合理的限制,所以很多静态类型语言并不拒绝在程序中提供显式的,有利于优化的信息,并且类型信息本身就是对优化最有价值的信息。

一来二去造成了一个结果:就是静态类型语言实现在编译时可访问到的程序信息远远大于动态类型语言。

动态类型语言的高性能实现为了填语言设计者挖的坑,就必须在没有额外信息的情况下,在运行时猜出所需要的信息。这个猜出的结果随时都可能失效,所以还得准备相应的 fallback。对动态类型语言做有效的优化,难度要比静态类型语言更大。

当然,一般认为动态类型语言有它的优势:就是如果代码写得足够好,编译器在运行时通过运行时实际统计到的信息进行优化,理论上效果会比只能使用编译时信息的静态类型语言要好。但是这个优势明明是 JIT 的优势,不是动态类型语言的优势——静态类型语言同样也可以做 JIT,同样也可以在运行时收集信息并用于优化。并且 JIT 这种形式天然受 latency 限制,不能进行特别复杂的优化,在很多场景下,如果保持其他变量不变的话,静态类型语言的 PGO 绝对效果是更好的。总的来说就是,在编译器可以利用到的信息量方面,静态类型语言严格大于动态类型语言。指望 JS 靠 compiler magic 超过 C/C++ 并不现实。

王垠前两天发了篇文章叫《我不是编译器专家》,在这篇文章中,王垠(再次)强调了业界对编译器和 PL 两个领域普遍的混淆,并且表达了对编译器技术上的藐视、对编译器人坐井观天、目中无人作风的批判。我们暂且忽略王垠三年半之前发了一篇内容相似的《我为什么不再做 PL 人》,并且把相似的罪名同样安到了 PL 圈子上的事实(不行我还是要笑),王垠所说的一些问题是确实存在的。编译器到底是夹在语言和硬件的 spec 之间,戴着镣铐跳舞的活。很多编译器费尽力气分析的结果,从语言上很简单就能解决。这是维度上的差异。新的 ECMA 标准加入的 strict mode、let、const、module、rest parameter 等特性都有尝试从语言层面改进性能的成分。

反映到这个贴子的问题上就是,在 JS 中做同样的优化会更难。V8 据称是实现了 SRoA,但是具体效果我没研究过,楼主可以自己试一试。
2019-12-28 14:07:11 +08:00
回复了 cooioobb 创建的主题 生活 以房东身份跟大家算房租。
首先,真可怕警告

--[真可怕[
@cooioobb 我回答的是“6000 一个月为什么 6000。有 3000 他不租,租 6000 的是为什么”的问题:
“上班近的房子可以租出更高的价格并且还有人租”

这对应的是 #66 中的:“同一座城市(物价、人工成本接近),有月租 3k 的房,有月租 6k 的房”
显然这多出来的钱不是在装修和电器家具上的
--]真可怕]

#81 说的租售比失调是目前某些城市租房市场的一大本质问题(虽然这个和楼主可能没啥关系),但是很明显租售比的问题不是房东的问题,更不是租客的问题。
2019-12-28 13:39:06 +08:00
回复了 cooioobb 创建的主题 生活 以房东身份跟大家算房租。
我给楼主一个实际的建议,联系你们城中村的房东,讨论以后租房不提供电器家具,稍微降房租,其他所有费用租客自负。这样你们省心又能多赚钱。
就是搞卡特尔
2019-12-28 13:35:57 +08:00
回复了 cooioobb 创建的主题 生活 以房东身份跟大家算房租。
@cooioobb
我前面说“‘相对的价值’不存在一个公认的算法”,是因为同样的东西,对不同的人,实际价值是不一样的。
比如我不打算生孩子,那么学区房的溢价对我就一文不值。
但是大多数买房的人要考虑教育问题,所以学区房在市场上就可以卖出高溢价。也就是最后的价格由市场决定(不是由成本决定,更不是由抱怨决定)。

租房的时候也存在同样的问题,比如企业聚集地周围的房子会偏贵一点,因为有许多人看重“上班近”这一属性支撑起了价格。
但是一个不在意上班近的租客,发现这房子除了上班近之外也没啥别的好处,那么就可能不会租这个房子,转而选择 同价格更远面积更大的房子 或 更远价格更低其他都相似 的房子。
这都不碍着上班近的房子可以租出更高的价格并且还有人租。
@yimity
“getElementBy 获取到的是动态的,即 你增加了 dom,再获取还是可以获取到你最新增加的的,你删掉了 dom,再获取的时候,之前获取到的就不在了,就是每次都是实时从 dom 树里面获取。
而 querySelector 是引用,即获取一次后面增加的 dom,不会获取到了,删掉了 dom, 在代码中还是能够用(但是会报错)。”

求依据:标准,源码,或示例
你说的可能是 getElementByClassName/TagName 和 querySelectorAll
楼主这个好像没这个区别
2019-12-28 13:18:58 +08:00
回复了 cooioobb 创建的主题 生活 以房东身份跟大家算房租。
@qinxi 本来想写 19260817,感觉太离谱了点
不过不是不可能哦
2019-12-28 12:38:21 +08:00
回复了 cooioobb 创建的主题 生活 以房东身份跟大家算房租。
@cooioobb 6k 的房子装修一定就比 3k 的好么?
另外“相对的价值”不存在一个公认的算法。
2019-12-28 12:23:26 +08:00
回复了 cooioobb 创建的主题 生活 以房东身份跟大家算房租。
这帖子逻辑是有问题的 ...
资产价格最终由市场决定,没有一个“合理”的价格。楼主说现在一套月净赚 1300,从市场的角度是合理的,如果市场发生变化,一套月净赚 2510 或者净赚 251 甚至净亏都是合理的。所以不存在“真实租金”太“便宜”或者太“贵”一说。
房东会寻求利益最大化(尽量涨租金),而给平均租金设限的是平均工资,就算租客不要求这些东西,最后租金还是不会比现在低多少。
“盖房不用钱”——错,楼主一套房月净赚 1300。还是说楼主想一年回本三年血赚?

考虑这样一个问题:同一座城市(物价、人工成本接近),有月租 3k 的房,有月租 6k 的房,难道说月租 6k 的房每个月贵的 3k 都是用在租客“额外要求”的东西上?
@grewer 不能拿 GitHub 做例子,据我观察 GitHub 是最激进的站点之一,我 Mac 上的老版 Chrome,GitHub 不能加载最新 commit,branch 的菜单点不开。另外 YouTube 不能看。

其他网站没出过问题,说明 GitHub 和 Google 可能确实对浏览器要求比较高。
2019-12-28 08:55:01 +08:00
回复了 22yune 创建的主题 程序员 计算机中有哪些不做 trade-off,鱼与熊掌兼得的解决方案?
没有
#8 难道是楼主的小号 ...

用 querySelector + 字符串拼接意味着可以 query 的不只 id ... 在输入的字符串后面可以加其他选择器

@ysc3839 选择器只用解析一遍,有缓存
但是用来做同样的工作,前者是要稍微慢一点(因为确实包了一层)
2019-12-27 20:02:12 +08:00
回复了 xiaoyanbot 创建的主题 分享发现 JS/Node 改变世界,单文件静态编译 Node 项目: pkg 和 nexe
改变世界有两种,第一种是让世界变得更好,第二种是让世界变得更差
2019-12-25 23:48:27 +08:00
回复了 lewis89 创建的主题 随想 90 后的中年危机随想与幻想
90 后暂时叫四分之一人生危机比较合适一点
2019-12-25 21:08:26 +08:00
回复了 DavidG 创建的主题 I Am A 我曾经是一个 ATM 维护工程师。大家有啥想问的嘛
我想问楼主是怎么找到这个节点的 ...
1 ... 61  62  63  64  65  66  67  68  69  70 ... 123  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5621 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 39ms · UTC 03:14 · PVG 11:14 · LAX 19:14 · JFK 22:14
Developed with CodeLauncher
♥ Do have faith in what you're doing.