MoreRSS

site iconSkywind | 林伟修改

有多个 10K+ 开源项目。程序员,1992年(小学)开始写代码,至今乐此不彼。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Skywind | 林伟的 RSS 预览

博客迁移服务商

2025-10-24 00:04:59

我的博客使用的 wordpress 一直架设在 bluehost.cn 上,差不多十多年了,前两天突然就不能访问了,后台也登录不进去,一查 bluehost.cn 主页,原来停止服务了,很多和我一样在上面运营十多年的网站都是说停就停,然后完全不提供数据备份的时间,连个电话都不给你打一个,就是邮件垃圾箱里提前几天给你发了一封告知邮件,人家 linode 之类的欠费停止你服务后,任然给你机会备份走,结果它完全不给。

所以没有啥完全稳定会一直存在的服务商,幸亏我机器上有备份,调查了以下几家虚拟主机提供商,他们的业务发展情况及稳定性,最后迁移 bluehost.com 了,帖子和评论都恢复了,但是页面访问统计没恢复到,因为页面访问统计插件是把数据存在别的 mysql 表里的,但之前用的备份插件又是只导出 wordpress 标准表格,所以包括访问统计,upvote 之类的数据全部清零了,无所谓了,还好访问量不算啥重要的东西,希望这次 bluehost.com 能多用两年。

之前我用过 bluehost.com 的,指示图 bluehost.cn 服务器在香港,国内访问会快点后面才选他,看来太小众的还是有问题。这次 bluehost.com 的服务器在亚利桑那,虽然远点,但国内访问起来,只要不下载,看个页面什么的也还算顺利。

有人问我为何不用静态页面?发布到 github pages 上那种,因为我这个 wordpress 博客以及个人 wiki 只依赖一家服务商,但是 github pages 页面需要依赖 github,评论需要依赖 gihtub issues+插件,计数器又需要依赖别的什么服务,依赖的服务太多了,上网那么多年我从不相信有什么服务可以一直持续下去的,所以依赖自然也少越好。

除了服务外,依赖的项目也是越少越好,之前那些 github pages 使用的基于 issues 的插件突然作妖,要收费,不收费就给你插广告,闹得天怒人怨,你还没办法,历史评论是博客相当重要的数据,依赖 github issues 作为评论数据存储的机制还有个致命方案是你还不能方便的导入导出,你会被绑死在 github issues 这个方案上,完全无法掌握自己的数据,不说 github 停止服务,一旦它一朝更改 api 规则(跟 twitter 一样)就只有哭了。

最后 wordpress 的功能真的很强大,插件生态也非常丰富,我也比较熟悉,有啥需求大概都能搞得定。

Loading

The post 博客迁移服务商 appeared first on Skywind Inside.

微信比 Telegram 落后在哪里?

2025-09-30 17:50:00

不怕不识货,就怕货比货,以下七点帮你了解差距究竟有多大:

第一:微信桌面 Qt 版引用了来自 Telegram 的 GPL 代码,对二进制 grep 一下字符串 desktop-app 就可以看到。有人找相关负责人提了一年了,结果连删掉字符串都懒得删。

第二:微信这么喜欢抄 Telegram 却不多抄点好的,比如微信群历史服务器只能保存两周,图片更短,当时没下载的图片几天后想看看就发现被服务器清除了,而 tg 全球十亿用户,消息存服务器四五年都不会删;

第三:什么年代了,微信附件大小最大 100M(不知改了没?)tg 随便存 10G 附件,一存好几年!一年前群里存的视频现在都找的到;

第四:微信那么挣钱的产品,结果给你搞个朋友圈合照都看不清脸的究极图片压缩,号称节省存储空间,好意思么?语音质量被压缩的经常听不清楚,飞书和钉钉的语音都比它清晰一万倍;

第五:微信用起来比 Telegram 卡有很多原因,其中一个重要原因就是微信使用单消息队列,所有消息挤压在一起,每次切换到微信就要同步一半天,包括全局消息和每个群最近的几百条消息,必须要同步完才能近一步操作,几秒时间消息列表不断更新跳变,点击无响应,导致想发给老婆的话明明点了老婆头像,最后因为列表跳变发到了同事群或者小孩班级群里去了,闹出了无穷无尽的笑话;但 Telegram 之所以不卡一个重要设计就是消息使用多队列,同时不需要等消息同步完才能操作,多个消息队列可以一边同步一边操作,每次切换过去你都可以立即流畅的操作,各个群的最近消息历史会使用并行的多个消息队列在后台并行同步,根本不影响你操作;

第六:微信非常耗电,有一次我手机只有 5% 的电了,希望能撑到出租车到家,所以我开启了节能模式,想着应该没事,正在结束着不同应用时来了条信息,我手贱条件反射的点了上去,瞬间就后悔了,熟悉的微信界面弹了出来,然后一个消息同步,手机被卡住不动,界面不停更新,手机瞬间变烫,几秒时间一下给我手机干没电自动关机了;

第七:微信技术团队比较喜欢吧屁大点事情说的玄乎其玄,不就是用了一个现成的叫 amrNB 的窄带语音编码器么,还只能编解码 8khz 的音频,明明有更好的做法,却用这种老旧淘汰的东西损失很多质量换来一点空间:

居然还做了三个通宵,还是他们当时广研最有技术含量的东西,这是在进一步自我暴露么?

所以真的不要和 Telegram 比,一比你发现全是差距。

Loading

The post 微信比 Telegram 落后在哪里? appeared first on Skywind Inside.

你是在什么电脑上学编程的?

2025-05-24 00:27:00

我小学时用的 GMT-92 学习机写程序,就是 FC 6502 那一套,但比后辈小霸王强多了:右上方有磁带盒,有很多扩展软件以磁带形式发布,自己写的 BASIC 程序也可以录制到磁带上下次接着写,小霸王那种没有存储设备一关机你的代码就没有的环境是根本没法学习编程的,因为代码完全无法积累,每次重来,你永远写不了复杂的软件,而这台 GMT-92 最核心的地方就是提供磁带存储,让我可以写稍微复杂点的代码,我就抱着一本 BASIC 说明书开始抄写上面的各种例子代码,然后修改部分不停验证学编程的:

那时我想做一个空战游戏,但是我只会每次循环控制一个飞机从左边移动到右边,完全不知道该如何同时控制多架飞机,因为身边没人教,没网络,书店里没游戏开发书籍,也没处问人,只能去书店看相关的电脑书籍得知有些操作系统是有多任务功能的,可以同时运行几段程序,我心想这就是我想要的,可 GMT 根本没有多任务这个功能怎么办呢?

连续数周没想出解决方案来,上课想,吃饭想,后来脑袋里面一遍遍的过以往学习过的电脑知识,让他们俩俩互相搭配,看看有没有新的可能性,突然我想起书店里介绍多任务原理的文字来,每个任务执行一小段时间片再去执行下一个任务的时间片,切换够快就能产生同时运行的效果,然后我忽然灵机一动,是不是我需要同时控制的多架飞机也可以这样每架飞机每次动一小步,然后快速切换下一架动一小步模拟出多架飞机同时移动的效果呢?

没有多任务系统,我能不能仿照实现呢?然后脑袋里一遍遍琢磨各处细节越想越觉得没问题,然后周末一整天,上午实现了一个基础,下午不停调试,居然被我做出来了,一个简单的空战游戏,多个敌人和主角可以正常同时移动,后来我才知道,这种每次更小一小步保存状态下次继续的方式就是经典的状态机模型,游戏做出来把我同学和身边小伙伴吓了一跳,也把我爹吓了一跳,他终于觉得或许我有潜力搞这个,才在后来初中时真的下决心给我买了台正儿八经的电脑,联想 486DX 66MHz,4MB 内存,DOS 系统,我才正儿八经的有条件开始自学 C 语言和汇编。

Loading

The post 你是在什么电脑上学编程的? appeared first on Skywind Inside.

Raylib 这种立即模式的图形引擎如何呢?

2025-04-29 21:11:00

最近 raylib 比较火,又是移植 web 又是移植掌机,看了眼 raylib 的例子代码,知道它为什么代码量那么少了,它甚至连一个显示对象树/场景树都没有实现,就是直接调用各种 draw 函数,和 ImGUI 一样属于 “立即模式”,这种模式做点简单的东西很舒服,对象一多一复杂就会比较麻烦:

那么这种每帧控制自己绘制的立即模式有啥问题呢?

第一:程序简单的时候好像也没区别,程序复杂了性能会急剧降低;

第二:正统图形引擎,都是维护一颗显示对象树/场景树,使用者将模型添加到书节点上,然后引擎能根据摄像机位置,高效剔除不需要显示的东西,raylib 这种立即模式相当于将大量工作,比如显示对象剔除,坐标变换等全部一古脑的推给用户,图形简单时可以不做剔除,但场景稍微复杂点不做剔除性能就会急剧下降;

第三:需要显示的对象多了以后,如果两帧之间大部分对象并没有改变,那么使用显示对象树可以基本不管,而立即模式下即便大量物体没改变也需要全部重新过一遍,大量数据重复传输到显存;

第四:对象多了以后,对脚本不友好,脚本调用 C 接口本来就缓慢,传统引擎脚本只需要维护两帧之间真正改变的对象,连动画这些自动的东西一旦开始都不用管,但立即模式下,所有对象每帧都需要用脚本操作一遍,开销巨大;

立即模式用来做 UI 问题不大,游戏界面本身没多复杂,但有序图形实用性就不太高了,所以 raylib 这样的立即模式图形引擎,更适合作为教学工具,而不是用于实际项目。

Loading

The post Raylib 这种立即模式的图形引擎如何呢? appeared first on Skywind Inside.

GCC 利用未定义行为进行优化正确么?

2025-01-19 00:37:00

说实话,编译器是否该利用 Undefined Behavior 进行优化目前都还是一个争议话题,主要是 gcc 开了个坏头,不予余力的在默认参数下利用 UB 来优化,举个例子,C 语言里带符号整数溢出是未定义行为,编译器应该假设它实际上以某种方式定义了:

int foo(unsigned char c) {
    int value = 2147483600;
    value += c;
    if (value < 2147483600) 
        bar();
    return value;
}

但利用这个 UB 进行优化的编译器会认为,既然 x 不会是负数,那么 value < 2147483600 就永远不会发生,所以整个 if 语句以及后面的 bar() 调用将可以被忽略,变成:

int foo(unsigned char c) {
    int value = 2147483600;
    value += c;
    return value;
}

这其实是一种很危险的做法,因为 C 语言可以跨越各种 CPU 架构编程,当年标准定义时,CPU 架构的差异比今天还大,在处理上面这类问题时,即便在今天,不同的架构结果并不一定相同,比如有的平台用补码表示负数,所以溢出了就会变成 -2147483648,而碰到反码或者原码表示负数的架构下,溢出了可能就变成 0 或者其它,所以一些事情根本就没法具体定义,必须留给具体编译器具体平台去处理。

Undefined Behavior 并不是说代码这么写是错的,相反他们都是语法正确的代码,真是错的应该就编译错误了,而是标准留个编译器实现者以自由,不去做限制,让他们根据实际平台,根据自己实现情况自行选择实现方式,而某些编译器实现选择利用它来进行优化了,那么如果本来就想利用特定平台的特性完成某些特定功能时,所以这类代码将没法写了。

关于这个问题,有个 X 友说的很准确,这里贴下译文(原文贴后面):

亲爱的 C 程序员们,既然你们似乎无法阅读关于你们编程语言的文档,让我为你们解释一下什么是未定义行为,更重要的是它不是什么。

引用 C 标准:

未定义行为:本国际标准未做任何强制要求的行为。(Undefined behavior: behavior for which this International Standard imposes no requirements)

这意味着标准并没有规定某些表达式该如何表现,编译器的创造者们可以自由选择他们想要的行为,例如:

  • 使编译失败
  • 删除有问题的表达式并继续编译
  • 发送给你的前任一条“我想你了”的消息
  • 将你的票投给一个 XX 主义政党
  • 甚至定义该行为(以平台特定的方式或非特定方式)

即,编译器的职责是合理处理未定义行为的情况。C 标准并没有对编译器作者提出挑战,这不是 “尽情发挥,给我惊喜”,它仅仅是对某些表达式没有施加限制,因为给予编译器作者自由选择其解决方案是合情合理的,因为这可能依赖于平台。

未定义行为并不是关于“被禁止的表达式”,它仅仅是语法上正确的 C 代码,而 C 标准对此并不关心。

到目前为止,这一切都是合理的。

在处理未定义行为时,编译器作者有多种选择:

  • 偏向一致的语义
  • 偏向性能
  • 偏向实现的简单性

正确的答案总是 “默认优先语义而非性能”。可以有破坏语义的优化,但这些优化应该通过适当的编译选项来启用,以便那些知道自己在做什么的人(或至少在继续破坏代码之前,编译器会得到明确的同意)使用。

(点击 more/continue 继续阅读)

让编译器默认具有一致的语义不仅减少了各种开发人员(包括经验丰富的开发人员)引入的错误,还极大地改善了开发人员的用户体验,因为这样可以让他们更好地推理代码,毫无畏惧地进行重构,并且总体上不必时刻担心优化器会搞砸他们的代码。

至于 “但您必须支持多个平台” 的论点,如果 GCC 的开发者想要这样,他们就会让整数溢出在编译时的行为与运行时的行为相同。也就是说,如果在运行时是环绕运算,那么在编译时也是环绕运算。或者,他们可以简单地拒绝承诺任何特定的整数溢出策略,而只是不过度尝试优化涉及算术运算的表达式。

不幸的是,GCC 的开发者和他们的社区一样愚蠢,他们不仅没有合理地定义未定义行为,反而假装这一切都与禁止表达式有关,并默认启用他们那些让语义破裂的优化。“看,妈的,代码快了5%,却带来了 5% 的更多 bug,搞得全世界的基础设施和生产代码都乱了!”

至于他们在整数溢出问题上的选择有多愚蠢,我们会另行讨论,但这则推文的要点并不是某个具体优化的糟糕,而是 GCC 将 C标准中的未定义行为作为借口来搞砸自己编译器的整体方法,这真是让人难以置信——这是只有少数人才能理解的全面退化。

任何有不同看法的人都应该被视为愚蠢,应该被限制接触任何计算机,保持50米的安全距离。

原文:https://x.com/effectfully/status/1876231418357092534

这里还有个 原文长截图

Loading

The post GCC 利用未定义行为进行优化正确么? appeared first on Skywind Inside.

异步事件模型的 Self-pipe trick

2024-11-04 11:41:00

异步事件模型中有一个重要问题是,当你的 select/poll 循环陷入等待时,没有办法被另外一个线程被唤醒,这导致了一系列问题:

1)在没有 pselect/ppoll 的系统上,信号无法中断 select/poll 等待,得不到即时处理;
2)另一个线程投递过来的消息,由于 select/poll 等待,无法得到即时处理;
3)调短 select/poll 的超时时间也无济于事,poll 的超时精度最低 1ms,粗糙的程序可能影响不大,但精细的程序却很难接受这个超时;
4)有的系统上即便你传了 1ms 进去,可能会等待出 15ms 也很正常。

比如主线程告诉网络线程要发送一个数据,网络线程还在 select/poll 那里空等待,根本没有机会知道自己自己的消息队列里来了新消息;或者多个 select/poll 循环放在不同线程里,当一个 accept 了一个新连接想转移给另一个时,没有办法通知另一个醒来即时处理。

解决这个问题的方法就叫做 self-pipe trick,顾名思义,就是创建一个匿名管道,或者 socketpair,把它加入 select/poll 中,然后另外一个线程想要唤醒它的话,就是往这个管道或者 socketpair 里写一个字节就行了。

类似 java 的 nio 里的 selector 里面的 notify() 函数,允许其他线程调用这个函数来唤醒等待中的一个 selector。

具体实现有几点要注意,首先是使用 notify() 唤醒,不用每次调用 notify() 都往管道/socketpair 里写一个字节,可以加锁检测,没写过才写,写过就不用写了:

// notify select/poll to wake up
void poller_notify(CPoller *poller) {
    IMUTEX_LOCK(&poller->lock_pipe);
    if (poller->pipe_written == 0) {
        char dummy = 1;
        int hr = 0;
    #ifdef __unix
        hr = write(poller->pipe_writer_fd, &dummy, 1);
    #else
        hr = send(poller->pipe_writer_fd, &dummy, 1);
    #endif
        if (hr == 1) {
            poller->pipe_written = 1;
        }
    }
    IMUTEX_UNLOCK(&poller->lock_pipe);
}

大概类似这样,在非 Windows 下面把 pipe() 创建的两个管道中的其中一个放到 select/poll 中,所以用 write(),而 Windows 下的 select 不支持放入管道,只支持套接字,所以把两个相互连接的套接字里其中一个放入 select。

两个配对的管道命名为 reader/writer,加入 select 的是 reader,而唤醒时是向 writer 写一个字节,并且判断,如果写过就不再写了,避免不停 notify 导致管道爆掉,阻塞线程。

而作为网络线程的 select/poll 等待,每次被唤醒时,甭管有没有网络数据,都去做一次管道复位:

static void poller_pipe_reset(CPoller *poller) {
    IMUTEX_LOCK(&poller->lock_pipe);
    if (poller->pipe_written != 0) {
        char dummy = 0;
        int hr;
    #if __unix
        hr = read(poller->pipe_reader_fd, &dummy, 1);
    #else
        hr = recv(poller->pipe_reader_fd, &dummy, 1);
    #endif
        if (hr == 1) {
            poller->pipe_written = 0
        }
    }
    IMUTEX_UNLOCK(&poller->lock_pipe);
}

每次 select/poll 醒来,都调用一下这个 poller_pipe_reset(),这样确保管道里的数据被清空后,就可以复位 pipe_written 标志了。

让后紧接着,处理完所有网络事件,就检查自己内部应用层的消息队列是否有其他消息投递过来,再去处理这些事件去;而其他线程想给这个线程发消息,也很简单,消息队列里塞一条,然后调用一下 notify(),把该线程唤醒,让他可以马上去检查自己的消息队列。

主循环大概这样:

while (is_running) {
    // 1)调用 select/poll 等待网络事件,超时设置成 1ms-10ms;
    // 2)醒来后先处理所有网络事件;
    // 3)如果和上次等待之间超过 1毫秒,则马上处理所有时钟超时事件;
    // 4)检查自己消息队列,并处理新到来的事件。
}

差不多就是这样。

PS:有人说用 eventfd 也能实现类似效果,没错,但不能跨平台,只有 Linux 特有,而且还有一些坑,但 self-pipe trick 是跨平台的通用解决方案,不管你用 Windows / FreeBSD / Linux / Solaris 都可以使用这个功能。

Loading

The post 异步事件模型的 Self-pipe trick appeared first on Skywind Inside.