sprintf vs strcpy - 使用更多内存和快速复制还是几乎没有内存和慢速复制?
sprintf vs strcpy - use more memory and fast copy or almost no memory and slow copy?
我正在编写延迟关键型应用程序(自制高频交易系统)。我有这样的代码,它只是将 uint64 转换为字符串:
// TODO: cache sprintf, use strcpy? measure?
sprintf(dest, "%" PRIu64, divRes.quot);
这里 divRes.quot
是保证在 1 到 1 000 000 之间的整数。所以我可以预分配(相当大的)数组和 "cache" 每个值。然后我可以执行 strcpy(dest, cache[divRes.quot]).
乍一看肯定快得多,因为 strcpy
肯定比 sprintf
快得多。但是请注意,我使用的是巨大的数组,几乎肯定无法将其完全加载到 CPU 缓存中。所以第二种方法几乎肯定会进入主内存。虽然在第一种方法中我很可能会留在 CPU 缓存中(甚至可能在最快的 L1 缓存中?!)
所以平均来说什么会更快:
- CPU 缓存中的慢速函数
- 访问主内存的快速功能?
我认为这取决于一个函数比另一个函数快多少以及 CPU 缓存访问比主内存访问快多少。
我想写一个真正的测试是很难的。因为在实际应用程序中,整个系统负载会有所不同,因此 cache/memory 使用情况会有所不同,这可能会发生巨大的变化。
请注意我不关心可读性、维护等,我只需要速度。
要使 table 查找工作正常,您必须经常这样做(在具有大缓存的 CPU 上)对于大多数 table 大部分时间都在缓存中。 table 占用大约 7 兆字节的内存,所以除非缓存 相当 大,并且您一次要转换数百万个数字,所以大部分访问都是缓存,这几乎肯定是净亏损。
根据我的计算,使用正常除法(~5 个除法 + 6 个加法)转换单个数字可能需要大约 100 个时钟。从主内存读取通常需要大约 200 个处理器时钟,因此您需要大约 50% 的缓存命中率才能收支平衡。
就我个人而言,我怀疑我会使用这两种方法中的任何一种。相反,我可能会做一个混合动力车。我将数字除以 1000,然后进行两次 table 查找(一个是股息,另一个是余数)。
优点是这样可以将 table 的大小减少到 4 KB 左右,并将每个 table 条目的使用量增加大约 1000 倍。假设您要转换至少一个一次有几百个(左右)随机分布的数字,您可以指望接近 100% 的缓存命中率。有了高缓存命中率,我们可以计划一个分区加上缓存中的两个负载,总共大约 25 个时钟,或者大约是我们对简单转换的预期速度的 4 倍。
我正在编写延迟关键型应用程序(自制高频交易系统)。我有这样的代码,它只是将 uint64 转换为字符串:
// TODO: cache sprintf, use strcpy? measure?
sprintf(dest, "%" PRIu64, divRes.quot);
这里 divRes.quot
是保证在 1 到 1 000 000 之间的整数。所以我可以预分配(相当大的)数组和 "cache" 每个值。然后我可以执行 strcpy(dest, cache[divRes.quot]).
乍一看肯定快得多,因为 strcpy
肯定比 sprintf
快得多。但是请注意,我使用的是巨大的数组,几乎肯定无法将其完全加载到 CPU 缓存中。所以第二种方法几乎肯定会进入主内存。虽然在第一种方法中我很可能会留在 CPU 缓存中(甚至可能在最快的 L1 缓存中?!)
所以平均来说什么会更快:
- CPU 缓存中的慢速函数
- 访问主内存的快速功能?
我认为这取决于一个函数比另一个函数快多少以及 CPU 缓存访问比主内存访问快多少。
我想写一个真正的测试是很难的。因为在实际应用程序中,整个系统负载会有所不同,因此 cache/memory 使用情况会有所不同,这可能会发生巨大的变化。
请注意我不关心可读性、维护等,我只需要速度。
要使 table 查找工作正常,您必须经常这样做(在具有大缓存的 CPU 上)对于大多数 table 大部分时间都在缓存中。 table 占用大约 7 兆字节的内存,所以除非缓存 相当 大,并且您一次要转换数百万个数字,所以大部分访问都是缓存,这几乎肯定是净亏损。
根据我的计算,使用正常除法(~5 个除法 + 6 个加法)转换单个数字可能需要大约 100 个时钟。从主内存读取通常需要大约 200 个处理器时钟,因此您需要大约 50% 的缓存命中率才能收支平衡。
就我个人而言,我怀疑我会使用这两种方法中的任何一种。相反,我可能会做一个混合动力车。我将数字除以 1000,然后进行两次 table 查找(一个是股息,另一个是余数)。
优点是这样可以将 table 的大小减少到 4 KB 左右,并将每个 table 条目的使用量增加大约 1000 倍。假设您要转换至少一个一次有几百个(左右)随机分布的数字,您可以指望接近 100% 的缓存命中率。有了高缓存命中率,我们可以计划一个分区加上缓存中的两个负载,总共大约 25 个时钟,或者大约是我们对简单转换的预期速度的 4 倍。