Chips and Cheese
Grace Hopper, Nvidia’s Halfway APU
#ChipAndCheese
Telegraph | source
(author: clamchowder)
Grace Hopper, Nvidia’s Halfway APU
#ChipAndCheese
Telegraph | source
(author: clamchowder)
杰哥的{运维,编程,调板子}小笔记
在 Surface Laptop 7 上运行 Debian Linux¶
背景¶
最近借到一台 Surface Laptop 7 可以拿来折腾,它用的是高通 Snapdragon X Elite 处理器,跑的是 Windows on Arm 系统。但作为 Linux 用户,肯定不满足于 WSL,而要裸机上安装 Linux。由于这个机器太新,所以安装的过程遇到了很多坎坷。
上游进展¶
目前 X Elite 处理器的上游支持已经逐步完善,但是还是需要很新的内核,也就是最近才合并了 X Elite 的两个笔记本的 device tree 支持进内核。我用的是 v6.11-rc1-43-g94ede2a3e913 版本的内核,目前可以正常显示,Wi-Fi 正常,USB Type-C 口正常工作(键盘,鼠标,有线网都可以通过 USB 接到电脑上),内置的键盘、触摸板和触摸屏不工作。希望后续可以获得更好的硬件支持。
折腾过程¶
高通和 Linaro 在去年的时候推出了一个实验性的 Debian Installer Image:https://git.codelinaro.org/linaro/qcomlt/demos/debian-12-installer-image,它针对的设备是高通自己的 CRD 设备,和 Surface Laptop 7 不同。自然,把这个 image 写到 U 盘里并启动是不行的。
需要注意的是,Surface Laptop 7 默认安装了 Windows,并且开启了安全启动,而我们自己编译的 Linux 内核自然是过不了安全启动的,所以要去固件关闭安全启动。由于 Windows 的 Bitlocker 默认是打开的,请先保证你可以获取 Bitlocker recovery key,不然之后可能进不去 Windows 系统了。安装双系统前,记得在 Windows 里准备好分区表,空间不够的话,可以在线缩小 NTFS。
进入固件的方法:按住音量上键开机。开机后,可以看到 Surface UEFI 的界面,可以调启动顺序,也可以关闭安全启动。为了安装方便,建议把 USB Storage 放到第一个。
接着就开始启动 U 盘里的 Debian Installer Image 了。启动以后,可以看到进入了 grub shell,目测是 grub 找不到自己的配置文件,可以在 (hd1,msdos1)/boot/grub 下面找到。但是这个 image 的 device tree 和 kernel 都比较老,直接启动会发现,Debian Install 进去了,但是内置键盘和外置 USB 键盘都不工作,于是没法进行进一步的安装。
这时候,在网上搜索了一下已有的在 X Elite 上运行 Linux 的尝试,发现有人在 ASUS 的 X Elite 笔记本上装好了(来源),我就试着用 ASUS 对应型号笔记本的 device tree 去启动,依然不行,经过了解后(感谢 @imbushuo),得知 Surface 的内置键盘等外设需要通过 SAM 访问,需要额外的配置,目前不确定能否通过 device tree 启用。
但很快也发现有人在 Surface Laptop 7 上跑起来了(来源),我发邮件问了这个作者,作者说他用的是外置的键盘,内置的键盘也不工作。放大观察作者录的视频,发现用的是最新的 master 分支的 Linux 内核,并且用的就是 CRD 的 device tree。到这里就比较有思路了:自己编译一个内核,然后用 x1e80100-crd.dtb 作为 device tree。
于是魔改了 Debian Installer Image:替换掉 linux 内核,换成自己编译的最新版,解开 initrd,把里面的 kernel modules 也换成新内核的版本,再把新的 x1e80100-crd.dtb 复制上去,再用 grub 启动新内核 + 新 initrd + 新 Device Tree,发现 USB 外接键盘工作了!虽然只有 Type-C 工作,但是也足够完成剩下的工作了。
不过在安装 Debian 的时候,还遇到了小插曲:glibc 版本不够新,估计是 Linaro 的 Image 太老了。于是我从新的 debian arm64 里复制了 libc.so.6 和 ld-linux-aarch64.so.1,覆盖掉 initrd 里的旧版本,这样就好了。
安装完以后,安装的系统里的内核是 debian 的最新内核,但是不够新,于是又老传统:手动 arch-chroot 进新的 sysroot,安装新的内核。也可以像 Linaro 仓库里指出的那样,直接替换 Debian Installer Image 里的 deb,但是我发现我打的 deb 太大了(毕竟 defconfig),放不进文件系统,只好最后自己手动装。
最后在 grub 配置里添加 devicetree 加载命令,再从 Debian Installer Image 的 grub 配置偷 linux cmdline,最终是 grub 配置是这个样子:
这样搞完,Debian 系统就正常起来了!
本文也发到了 Reddit 上:https://www.reddit.com/r/SurfaceLinux/comments/1efmyb3/managed_to_install_baremetal_linux_on_snapdragon/
source
在 Surface Laptop 7 上运行 Debian Linux¶
背景¶
最近借到一台 Surface Laptop 7 可以拿来折腾,它用的是高通 Snapdragon X Elite 处理器,跑的是 Windows on Arm 系统。但作为 Linux 用户,肯定不满足于 WSL,而要裸机上安装 Linux。由于这个机器太新,所以安装的过程遇到了很多坎坷。
上游进展¶
目前 X Elite 处理器的上游支持已经逐步完善,但是还是需要很新的内核,也就是最近才合并了 X Elite 的两个笔记本的 device tree 支持进内核。我用的是 v6.11-rc1-43-g94ede2a3e913 版本的内核,目前可以正常显示,Wi-Fi 正常,USB Type-C 口正常工作(键盘,鼠标,有线网都可以通过 USB 接到电脑上),内置的键盘、触摸板和触摸屏不工作。希望后续可以获得更好的硬件支持。
折腾过程¶
高通和 Linaro 在去年的时候推出了一个实验性的 Debian Installer Image:https://git.codelinaro.org/linaro/qcomlt/demos/debian-12-installer-image,它针对的设备是高通自己的 CRD 设备,和 Surface Laptop 7 不同。自然,把这个 image 写到 U 盘里并启动是不行的。
需要注意的是,Surface Laptop 7 默认安装了 Windows,并且开启了安全启动,而我们自己编译的 Linux 内核自然是过不了安全启动的,所以要去固件关闭安全启动。由于 Windows 的 Bitlocker 默认是打开的,请先保证你可以获取 Bitlocker recovery key,不然之后可能进不去 Windows 系统了。安装双系统前,记得在 Windows 里准备好分区表,空间不够的话,可以在线缩小 NTFS。
进入固件的方法:按住音量上键开机。开机后,可以看到 Surface UEFI 的界面,可以调启动顺序,也可以关闭安全启动。为了安装方便,建议把 USB Storage 放到第一个。
接着就开始启动 U 盘里的 Debian Installer Image 了。启动以后,可以看到进入了 grub shell,目测是 grub 找不到自己的配置文件,可以在 (hd1,msdos1)/boot/grub 下面找到。但是这个 image 的 device tree 和 kernel 都比较老,直接启动会发现,Debian Install 进去了,但是内置键盘和外置 USB 键盘都不工作,于是没法进行进一步的安装。
这时候,在网上搜索了一下已有的在 X Elite 上运行 Linux 的尝试,发现有人在 ASUS 的 X Elite 笔记本上装好了(来源),我就试着用 ASUS 对应型号笔记本的 device tree 去启动,依然不行,经过了解后(感谢 @imbushuo),得知 Surface 的内置键盘等外设需要通过 SAM 访问,需要额外的配置,目前不确定能否通过 device tree 启用。
但很快也发现有人在 Surface Laptop 7 上跑起来了(来源),我发邮件问了这个作者,作者说他用的是外置的键盘,内置的键盘也不工作。放大观察作者录的视频,发现用的是最新的 master 分支的 Linux 内核,并且用的就是 CRD 的 device tree。到这里就比较有思路了:自己编译一个内核,然后用 x1e80100-crd.dtb 作为 device tree。
于是魔改了 Debian Installer Image:替换掉 linux 内核,换成自己编译的最新版,解开 initrd,把里面的 kernel modules 也换成新内核的版本,再把新的 x1e80100-crd.dtb 复制上去,再用 grub 启动新内核 + 新 initrd + 新 Device Tree,发现 USB 外接键盘工作了!虽然只有 Type-C 工作,但是也足够完成剩下的工作了。
不过在安装 Debian 的时候,还遇到了小插曲:glibc 版本不够新,估计是 Linaro 的 Image 太老了。于是我从新的 debian arm64 里复制了 libc.so.6 和 ld-linux-aarch64.so.1,覆盖掉 initrd 里的旧版本,这样就好了。
安装完以后,安装的系统里的内核是 debian 的最新内核,但是不够新,于是又老传统:手动 arch-chroot 进新的 sysroot,安装新的内核。也可以像 Linaro 仓库里指出的那样,直接替换 Debian Installer Image 里的 deb,但是我发现我打的 deb 太大了(毕竟 defconfig),放不进文件系统,只好最后自己手动装。
最后在 grub 配置里添加 devicetree 加载命令,再从 Debian Installer Image 的 grub 配置偷 linux cmdline,最终是 grub 配置是这个样子:
devicetree /boot/x1e80100-crd.dtbecho 'Loading Linux 6.11.0-rc1-00043-g94ede2a3e913 ...'linux /boot/vmlinuz-6.11.0-rc1-00043-g94ede2a3e913 root=UUID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee ro efi=novamap pd_ignore_unused clk_ignore_unused fw_devlink=off cma=128M quietecho 'Loading initial ramdisk ...'initrd /boot/initrd.img-6.11.0-rc1-00043-g94ede2a3e913这样搞完,Debian 系统就正常起来了!
本文也发到了 Reddit 上:https://www.reddit.com/r/SurfaceLinux/comments/1efmyb3/managed_to_install_baremetal_linux_on_snapdragon/
source
Evolution of iPhone storage capacity
People who should know better often underestimate how fast our storage capacity has grown. We have been able to get 1 TB of storage on iPhones for the last three generations.
Further reading: In 2011, I predicted that the iPhone would have 1TB of storage in 2020.
source
How big are your docker images?
Docker is a standard to deploy software on the cloud. Developers start with an existing image and add their own code before deploying their systems. How big are typical uncompressed images?
Method: docker inspect -f "{{ .Size }}" docker.io/library/myimage
source
How much of your binary executable is just ASCII text?
We sometimes use binary executable which can span megabytes. I wondered: how much text is contained in these binary files? To find out, I wrote a Python script which adds up the size of all sequences of more than 16 ASCII characters in the file.
My heuristic is simple but is not quite perfect: some long sequences might not be actual text and some short ASCII strings might be missed. Nevertheless, it should be good enough to get some insight.
I downloaded macOS binaries for some popular JavaScript runtimes. I find that tens of megabytes are used for what is likely ASCII strings.
source
Chips and Cheese
Zen 5’s 2-Ahead Branch Predictor Unit: How 30 Year Old Idea Allows for New Tricks
#ChipAndCheese
Telegraph | source
Zen 5’s 2-Ahead Branch Predictor Unit: How 30 Year Old Idea Allows for New Tricks
#ChipAndCheese
Telegraph | source
近期打算搞个newsletter,主要内容为一些近期体系结构/性能优化的新闻/Reading List,更新频率不固定,但不会高于一周一次,预期可能两周到一个月一次,也许信息密度将会比tg channel高一些。内容形式将会参考Dennis Bakhvalov的Perf letter。
感兴趣的朋友可subscribe。
https://newsletter.eastonman.com/subscription/form
由于使用的邮件服务器没有reputation(新建的),确认订阅的邮件也可能进垃圾箱,如果想确保能收到件可以将noreply#eastonman.com加入白名单。如果未来的邮件进入垃圾箱,也请您帮忙移动到正常的收件箱,因为大部分邮件服务商都有learning based的垃圾邮件过滤算法,移动邮件有助于提高我邮件服务器的reputation。
感兴趣的朋友可subscribe。
https://newsletter.eastonman.com/subscription/form
由于使用的邮件服务器没有reputation(新建的),确认订阅的邮件也可能进垃圾箱,如果想确保能收到件可以将noreply#eastonman.com加入白名单。如果未来的邮件进入垃圾箱,也请您帮忙移动到正常的收件箱,因为大部分邮件服务商都有learning based的垃圾邮件过滤算法,移动邮件有助于提高我邮件服务器的reputation。
Daniel Lemire's blog
Safer code in C++ with lifetime bounds
For better performance in software, we avoid unnecessary copies. To do so, we introduce references (or pointers). An example of this ideas in C++ is the std::string_view class. As the name suggests, a std::string_view instance is merely a ‘view’: it points at some string, but it does not own or otherwise manage the underlying memory.
The downside is that we must track ownership: when the owner of the memory is gone, we should not be left holding the std::string_view instance. With modern tools, it is trivial to detect such bugs (e.g., using a sanitizer). However, it would be nicer if the compiler could tell us right away.
A few C++ compilers (Visual Studio and LLVM) support lifetime-bound annotation to help us. Let us consider an example. Suppose you would like to parse a URL (e.g., ‘https://www.google.com/path’), but you are only interested in the host (e.g. ‘www.google.com’). You might write code like so using the ada-url/ada parsing library:
This code is not generally safe. The parser will store the result of the parse inside a temporary object but you are returning an std::string_view which points at it. You have a dangling reference.
To get a recent version of LLVM/clang (18+) to warn us, we just need to annotate the function get_host like so:
And then we get a warning at compile time:
It is hardly perfect at this point in time. It does not always warn you, but progress is being made. This feature and others will help us catch errors sooner.
Credit: Thanks to Denis Yaroshevskiy for making me aware of this new compiler feature.
source
Safer code in C++ with lifetime bounds
For better performance in software, we avoid unnecessary copies. To do so, we introduce references (or pointers). An example of this ideas in C++ is the std::string_view class. As the name suggests, a std::string_view instance is merely a ‘view’: it points at some string, but it does not own or otherwise manage the underlying memory.
The downside is that we must track ownership: when the owner of the memory is gone, we should not be left holding the std::string_view instance. With modern tools, it is trivial to detect such bugs (e.g., using a sanitizer). However, it would be nicer if the compiler could tell us right away.
A few C++ compilers (Visual Studio and LLVM) support lifetime-bound annotation to help us. Let us consider an example. Suppose you would like to parse a URL (e.g., ‘https://www.google.com/path’), but you are only interested in the host (e.g. ‘www.google.com’). You might write code like so using the ada-url/ada parsing library:
std::string_view get_host(std::string_view url_string) {
auto url = ada::parse(url_string).value();
return url();
}This code is not generally safe. The parser will store the result of the parse inside a temporary object but you are returning an std::string_view which points at it. You have a dangling reference.
To get a recent version of LLVM/clang (18+) to warn us, we just need to annotate the function get_host like so:
#ifndef __has_cpp_attribute
#define ada_lifetime_bound
#elif __has_cpp_attribute(msvc::lifetimebound)
#define ada_lifetime_bound [[msvc::lifetimebound]]
#elif __has_cpp_attribute(clang::lifetimebound)
#define ada_lifetime_bound [[clang::lifetimebound]]
#elif __has_cpp_attribute(lifetimebound)
#define ada_lifetime_bound [[lifetimebound]]
#else
#define ada_lifetime_bound
#endif
...
std::string_view get_host() const noexcept ada_lifetime_bound;And then we get a warning at compile time:
fun.cpp:8:10: warning: address of stack memory associated with local variable 'url_aggregator' returned [-Wreturn-stack-address]
8 | return url_aggregator.get_host();
It is hardly perfect at this point in time. It does not always warn you, but progress is being made. This feature and others will help us catch errors sooner.
Credit: Thanks to Denis Yaroshevskiy for making me aware of this new compiler feature.
source
Chips and Cheese
Arm’s Neoverse V2, in AWS’s Graviton 4
#ChipAndCheese
Telegraph | source
(author: clamchowder)
Arm’s Neoverse V2, in AWS’s Graviton 4
#ChipAndCheese
Telegraph | source
(author: clamchowder)
Daniel Lemire's blog
Does C++ allow template specialization by concepts?
Recent versions of C++ (C++20) have a new feature: concepts. A concept in C++ is a named set of requirements that a type must satisfy. E.g., ‘act like a string’ or ‘act like a number’. When used in conjunction with templates, concepts can be quite nice. Let us look at an example. Suppose that you want to define a generic function ‘clear’ which behaves some way on strings and some other way on other types. You might use the following code:
We have a generic clear function that takes a reference to any type T as an argument. We define a concept not_string which applies to any type expect std::string. We specialize for the std::string case: template <> void clear(std::string & t) { t.clear(); }: It directly calls the clear() member function of the string to empty its contents. Next we define template void clear(T& container) requires not_string { ... }: it is a generic implementation of clear for any type T that satisfies the not_string concept. It iterates over the container and sets each element to its default value. When you call clear with a std::string, the specialized version is used, directly calling std::string::clear(). For any other type that satisfies the not_string concept (i.e., is not a std::string), the generic implementation is used. It iterates over the container and sets each element to its default value. This assumes that T is a container-like type with a value_type and iterator support.
There are easier ways to achieve this result, but it illustrates the idea.
Up until recently, I thought that the template with the ‘requires’ keyword was a template specialization, just like when you specialize the template for the std::string type.
Unfortunately, it may be more complicated. Let us repeat exactly the same code but we put the clear function in a class ‘A’:
This time, your compiler might fail with the following or the equivalent:
You might fix the issue by adding the template with the constraint to the class definition.
I find this unpleasant and inconvenient because you must put in your class declaration all of the concepts you plan on supporting. And if someone wants to support another concept, they need to change your class declaration to add it.
source
Does C++ allow template specialization by concepts?
Recent versions of C++ (C++20) have a new feature: concepts. A concept in C++ is a named set of requirements that a type must satisfy. E.g., ‘act like a string’ or ‘act like a number’. When used in conjunction with templates, concepts can be quite nice. Let us look at an example. Suppose that you want to define a generic function ‘clear’ which behaves some way on strings and some other way on other types. You might use the following code:
template <typename T>
void clear(T & t);
template <typename T>
concept not_string =
!std::is_same_v<T, std::string>;
template <>
void clear(std::string & t) {
t.clear();
}
template <class T>
void clear(T& container) requires not_string<T> {
for(auto& i : container) {
i = typename T::value_type{};
}
}
We have a generic clear function that takes a reference to any type T as an argument. We define a concept not_string which applies to any type expect std::string. We specialize for the std::string case: template <> void clear(std::string & t) { t.clear(); }: It directly calls the clear() member function of the string to empty its contents. Next we define template void clear(T& container) requires not_string { ... }: it is a generic implementation of clear for any type T that satisfies the not_string concept. It iterates over the container and sets each element to its default value. When you call clear with a std::string, the specialized version is used, directly calling std::string::clear(). For any other type that satisfies the not_string concept (i.e., is not a std::string), the generic implementation is used. It iterates over the container and sets each element to its default value. This assumes that T is a container-like type with a value_type and iterator support.
There are easier ways to achieve this result, but it illustrates the idea.
Up until recently, I thought that the template with the ‘requires’ keyword was a template specialization, just like when you specialize the template for the std::string type.
Unfortunately, it may be more complicated. Let us repeat exactly the same code but we put the clear function in a class ‘A’:
struct A {
template <typename T>
void clear(T & t);
};
template <>
void A::clear(std::string & t) {
t.clear();
}
template <class T>
void A::clear(T& container) requires not_string<T> {
for(auto& i : container) {
i = typename T::value_type{};
}
}
This time, your compiler might fail with the following or the equivalent:
out-of-line definition of 'clear' does not match any declaration in 'A'
You might fix the issue by adding the template with the constraint to the class definition.
struct A {
template <typename T>
void clear(T & t);
template <class T>
void clear(T& container) requires not_string<T>;
};
I find this unpleasant and inconvenient because you must put in your class declaration all of the concepts you plan on supporting. And if someone wants to support another concept, they need to change your class declaration to add it.
source
Chips and Cheese
Arm’s Cortex A73: Resource Limits, What are Those?
#ChipAndCheese
Telegraph | source
(author: clamchowder)
Arm’s Cortex A73: Resource Limits, What are Those?
#ChipAndCheese
Telegraph | source
(author: clamchowder)
Chips and Cheese
A Video Interview with Mike Clark, Chief Architect of Zen at AMD
#ChipAndCheese
Telegraph | source
A Video Interview with Mike Clark, Chief Architect of Zen at AMD
#ChipAndCheese
Telegraph | source