自从 Terraform resource "aws_iam_role" 不推荐使用 "inline_policy" 和 "managed_policy_arns" 以后,就尝试了用 "aws_iam_policy_attachment" 来为 iam role 指定 AWS 内置和自定义的 IAM policy。因为在官方文档 aws_iam_role 中最先看到的就是 aws_iam_policy_attachment, 其实仔细阅读该文档的话,建议是- 用 "aws_iam_role_policy" 来代替 "inline_policy"
- 用 "aws_iam_role_policy_attachment" 来代替 "managed_policy_arns"
然后在项目中为避免 inline_policy 和 managed_policy_arns 的警告而选择了通用的 aws_iam_policy_attachment, 同时原来也用的 "aws_iam_policy",而非 "aws_iam_role_policy。 所以才撞入到以前一直用 aws_iam_role(inline_policy, managed_policy_arns) + aws_iam_policy 就没出过问题。 Read More
平时随意用 Python 写的用了就丢的小工具当然没必要写什么单元测试,如果是一个要反复打磨的工具,项目,单元测试就必须重视起来了。因为简单思路写出来的代码将要应对各种未知情形,加之想要大肆的重构,有了足够的测试用例才能安心。
任何语言白盒的单元测试首先面对的就是 Mock, 不光是应对有副作用的操作,最好是隔离所有不在当前所见到的代码的任何调用,其实像print这种有副作用的代码一般是能容忍的,可能也是为何很多测试框架默认会把控制台的输出关掉。
在 Python 中,一般基本的东西自己都有,Mock 就直接用 unittest.mock,可应用于所有的单元测试框架中,如 unittest, pytest。另有一块对 uniitest.mock 的简单封装,专为 pytest 提供方便的 pytest-mock。今天先了解 unittest.mock 的使用,它的官方文档是 unittest.mock -- mock object library。 Read More
Java 代码中如果显式的用throw关键字抛出异常,那么在该分支中其后的语句不可到达,并且即使对于有返回值的函数也不必写return语句了。像下面的代码1private static int foo(int num) { 2 if (num == 0) { 3 throw new RuntimeException(""); 4 } else { 5 return num + 1; 6 } 7}
以上代码是合法的。要清洁代码的话,最后的return num + 1不必写在else条件中,这样写只是为了验证抛出异常后不必有返回值。
比如我们想对该代码进行重构,把throw语句抽取到一个方法中,以便于在该方法中集中处理错误信息,于是变成了 Read More
继续体验 Meta 开源的 Llama 模型,前篇 试用 Llama-3.1-8B-Instruct AI 模型 直接用 Python 的 Tranformers 和 PyTorch 库加载 Llama 模型进行推理。模型训练出来的精度是 float32, 加载时采用的精度是 torch.bfloat16。
注:数据类型 torch.float32, torch.bfloat16, 与 torch.float16 有不同的指数(Exponent),尾数(Fraction)宽度, 它们都有一位是符号位,所以剩下的分别为指数位和尾数位宽度, torch.float32(8, 23), torch.bfloat16(8, 7), torch.float16(5, 10)。
模型依赖于 GPU 的显存,根据经验, 采用 16 位浮点数加载模型的话,推理所需显存大小(以 GB 为单) 是模型参数量(以 10 亿计) 的两倍,如 3B 模型需要约 6G 显存。如果对模型进一步量化,如精度量化到 4 位整数,则所需显存大小降为原来的 1/4 到 1/3, 意味着 3B 模型只要 2 G 显存就能进行推理。所以我们可以把一个 3B 的模型塞到手机里去运行,如果是 1B 的模型 int4 量化后内存占用不到 1G(0.5 ~ 0.67)。
本文体验 llama.cpp 对模型进行推理,在 Hugging Face 的用户设置页面 Local Apps and Hardware, 可看到一些流行的跑模型的应用程序,分别是- 生成文本的: llama.cpp, LM Studio, Jan, Backyard AI, Jellybox, RecurseChat, Msty, Sanctum, LocalAI, vLLM, node-llama-cpp, Ollama, TGI
- 文生图的: Draw Things, DiffusionBee, Invoke, JoyFusion
- 在用 Python 写 Web 服务端代码时,用 base64.encodebytes() 函数对字符串进行编码,然后在 Java 端用 Base64.getDecoder().decode() 时无法解码,难道 base64 编码在两种语言间还有这等差异。Google 一下,得到的答案是在 Java 端要用 Base64.getMimeDecoder().decode() 函数解码。这一问题算是解决了, 不过后来又在 Python 写的 AWS Lambda 中输出
return {
以 AWS Lambda functionURL 的方式来访问,对于 body 中的小字符串是没问题,一旦 body 够大时在 Postman 或 curl 命令中无法直接展示出来,用 curl --output a.out 存成本地文件,打开后看到的是带换行的格式
"statusCode": 200,
"body": base64.encodebytes(b"short message"),
"isBase64Encoded": True
}H4sIAAZi7GYC/+19WXfcOLLmX+HxwxzXOS6b2AhiprvnyFtZt7yoJbdr6r74UEpKyq5UpjoXL/fX
Read More
D8AlkysIkCFmpo2H7pJJEBkAAsCHQMQXf/t6HX/YrO83a++P168+xcvVdDH/+yP6lOGnzEciCNHn
gD7yTufT9evpLD6L1rd/f3Q6n03nsXexXk7nN4+8F4v59fRms4zW8uNtHdjH9KkfPEXoM6JPsf8U
......
我们通常用的 Java 缓存基本可认为是扩展了 HashMap 或 ConcurrentHashMap 的实现,它们各自实现自己的缓存策略,如时间与空间的控制,生命周期管理,是否支持分布式,溢出时能否转储到磁盘。关于 Java 本地缓存的存储分为内存与磁盘,内存多数情况下指的是堆内内存(on-heap), 而介于堆内内存与文件存储之间的就是堆外内存(off-heap)- 堆内存储(on-heap): 操作最快,无需序列化,但大量数据时会影响到 GC 的效率
- 堆外存储(off-heap): 存储在 Java 进程内存但非 JVM 堆内(不在 -Xmx 指定的内存范围内),使用或保存时需进行序列化/反序列化过程(在堆内与堆外转换),但不受 GC 影响,有助于提它来 GC 的效率
- 文件存储:不仅存在序列化与反序列化过程,还带 IO 操作,所以最慢,唯一优点就是大
我们查看一下当前 Spring 支持的缓存实现, Supported Cache Providers, 列有 Generic, JCache(JSR-107), EhCache 2.x, Hazelcast, Infinispan, Couchbase, Redis, Caffeine, Simple, 这其中无一支持堆外缓存,其中的 EhCache 要付费使用 EhCache 3(Big Memory) 才能支持 off-heap。 Read More
我们在创建 Java 线程池,无论是用 Executors, ThreadPoolExecutor, 还是 Spring 的 ThreadPoolTaskExecutor, 如果不指定工作队列的大小的话,默认为 Integer.MAX_VALUE(2147483647), 基本不会把它爆满,但是在许多的任务要执行时大量 Runnable 对象的创建却足以把内存撑爆掉。所以才有必要使用一个有限大小的工作队列,如 5000, 再配上 RejectedExecutionHandler(DiscardOldestPolicy, DiscardPolicy, 或 CallerRunsPolicy)。前两种策略会主动放弃最旧最新的任务,一般不是我们想要的,CallerRunsPolicy 还能主动发挥任务提交者的计算能力,是一种不错的选择(只可能会发生工作队列太小且提交者执行的任务太忙时产生线程池一时的空闲。
所以总结起来我们可以有以下几种实现直接使用 CallerRunsPolicy
在工作队列满时有效利用提交任务的线程,不让它闲着,这种实现最简单, 像下面那样声明线程池Read More1var threadPool = new ThreadPoolExecutor(2, 5, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10), 2 new ThreadPoolExecutor.CallerRunsPolicy());
这段时间都纠缠于 Java 如何操作 PostgreSQL 数据库上,千方百计的为求得更好的性能。为此我们用上了 Batch, 或用 id = any(?) 这种更 PostgreSQL 化的数组参数操作。其实它还有更多数组方面的花样可以玩,毕竟 PostgreSQL 数据库有一种广纳百川的胸怀,总有好的新特性能在 PostgreSQL 中首先体验到。
回到之前的一篇 postgres in (?,?) 和 =any(?) 用法/性能对比,其中关于如何向查询语句中
id = any(?)占位符传入数组参数的代码是1Connection conn = datasource.getConnection(); 2 3String query = "select * from users where id = any(?)"; 4PreparedStatement pstmt = conn.prepareStatement(query); 5Object[] params = new Object[]{1, 2, 3}; 6pstmt.setArray(1, conn.createArrayOf("int", params)); 7ResultSet rs = pstmt.executeQuery();在 PreparedStatement(PgPreparedStatement) 中设置数组参数的函数是用 Read More
- 继续上一篇数据库相关操作的话题,在有大量的数据操作时(如增删改,甚至调用函数或存储过程),我们应该尽可能的采用批量化操作(先摆下结论,后面我们会看到原由)。想像一下我们要向数据库插入 10 万条记录,如果逐条插入的话,客户端与数据库之间将会有 10 万网络请求响应来回; 而假如以 1000 条记录为一个 batch, 客户端与数据库之间的网络请求响应次数将缩小到 100。 业务数据的内容总量未变,但 Batch 操作除了可重用预编译的 Statement 外还, 可避免每次请求中重复的元数据,所以从 100,000 到 100 的缩减在时效上的表现是非常可观的,有时就是 60 分钟与 1 分钟的区别(在最后面测试结果显示这一差异更为恐怖)。
当然, JDBC 的批处理功能具体还要相应驱动的支持,通过数据库连接的 conn.getMetaData().supportsBatchUpdates() 可探知是否支持批量操作。
API 方面, 在 Statement 接口中定义了如下 batch 相关的操作方法- void addBatch(String sql): 将显式的 SQL 语句编入到当前 Batch 中
- void clearBatch(): 清除当前 Batch 列表,以便于建立新的 Batch
- int[] executeBatch(): 执行当前 Batch 列表中的语句,返回每条语句受影响行数组成的数组。0 可能表示执行语句无法确知受影响的行
- long[] executeLargeBatch(): 当 Batch 中语句受影响行数可能会超过整数最大值时用这个
Java 项目需要产生单元测试及代码覆盖率的话一直都是走的 JUnit 单元测试,JaCoCo 基于测试产生测试覆盖率,然后送到 SonarQube 去展示这条路子。当然 SonarQube 还可以帮我们进行代码的静态分析。但对其中的具体使用及过程知晓的并不深,基本就是在 pom.xml 中依葫芦画瓢。本文稍加深入的理解每一步的功效与配置,以 Maven 管理的 Java 项目为例,JUnit 采用是众多旧项目仍然无法摆脱的 JUnit 4。
示例项目名称为 JaCoCoSonar, 创建一个 Calc 类,其中有 int add(int op1, int op2) 方法,为其写一个单元测试 CalcTest1public class CalcTest { 2 3 @Test 4 public void testAdd() { 5 Assert.assertEquals(3, Calc.add(1, 2)); 6 } 7}单元测试实际是被 maven-surefire-plugin 插件执行的
现在开始第一步,执行mvn test看会发生什么,执行过程中控制台显示 Read More