为什么硬盘有时快有时慢_为什么有时我的代码会变慢(以及如何找出所有这些背后的原因)?...

为什么硬盘有时快有时慢

第一部分–眼见为实

我们大多数人都会在某个时候面对这个问题。 无论我们将其置于何种环境下,我们的代码(各种形式)都可以愉快地运行,然后有一天某人抱怨一个请求,可能是成千上万个请求,导致永久,超时或遗漏了SLA。

也许有很多短暂的事务从未进行过,或者GC暂停永远发生了。 有许多原因可能导致代码似乎突然变慢甚至暂停了一段时间。

在本系列博客的第1部分中,我想提供一种简单的方法来捕获来自操作系统的噪声。 那就是防止用户空间线程以连续且不受干扰的方式消耗掉那些可口的CPU周期的原因。 我们将忽略一些可能的罪魁祸首,例如NMIMCE民意测验TLB击落热节流等,因为它们可能不是严格意义上的操作系统驱动事件,将成为本系列文章中单独讨论的主题。

以下材料主要用于基于Linux的环境,根据我的经验,这些环境将成为绝大多数严肃的生产系统的基础。

已经有一些非常有用的工具可以测量os抖动,但是经过多年的学习,我了解到它们都有一定的局限性,使得它们在进行相对详细的分析时用途有限:

jHiccup –由Azul Systems的首席技术官Gil Tene编写和开源的出色工具。 主要侧重于捕获Java应用程序上下文中的抖动。 这是一个非常成熟且非常广泛的工具,具有许多有趣的选项,例如各种输出格式,能够附加到正在运行的JVM进程,执行空闲控制进程以获取要比较的基准以及非常小的占用空间(主要是由于雇用了另一个吉尔·泰恩(Gil Tene)的聪明孩子– HDR直方图。 但是,我的问题是,jHiccup不支持开箱即用的高分辨率定时(由于它捕获抖动的原因),并且时序输出不包含时间戳(但是确实包含相对时间)。

sysjitter –由SolarFlare提供和开源的微型c单元,作为其OpenOnload堆栈的一部分。 它的工作方式与jHiccup完全不同,它在测量期间燃烧所有cpus,并为每个cpu执行中观察到的停顿生成分位数摘要。 显然缺乏时序输出。

MicroJitterSampler –另一个Java实用程序(类),用于捕获Java代码遇到的抖动。 它是Peter Lawrey著名的Java-Thread-Affinity库的一部分,您中有些人可能已经很熟悉。 它不使用任何通用的直方图数据结构,因此无法为您提供观察到的延迟的良好分位数分解。 也没有以时序数据的形式提供详细的测量顺序。

JLBH – Peter Lawrey及其团队的另一个有用工具。 这是他们的编年史库的一部分。 在许多方面,它与MicroJitterSampler相似,因此也存在相同的缺点。

也许我期望过高,但是OS抖动测量工具的前景仍然much不绝。 而且,出于非常好的原因,需要一种工具,该工具将允许人们查明延迟中的特定峰值并将高精度时间戳与其相关联。

当您试图了解到底是什么导致了速度变慢时,您首先需要能够将有问题的事件与许多其他事件隔离开来,以便通过手术将罪魁祸首放大。

因此,第99.99个百分位数会告诉您问题所在,以及问题的严重程度,但这绝对不会使您更深入地了解原因或可能的原因。

为此,您将需要精确的高粒度测量,尤其是在亚毫秒级的情况下。

我正在试验基于Java的抖动采样器,通过结合在处理器上旋转的sysjitter方法并捕获任何干扰并生成丰富的高精度时间序列来观察延迟,可以实现这一目标。 我最终得出结论,使用本机代码来保证稳定,更可靠的执行配置文件更加容易,因此决定使用一些简单的C代码:

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <sched.h>
#include "jitter.h"
#include "influx.h"
#include "csv.h"
#define NANOS_IN_SEC 1000000000ul
struct timespec ts;
long long cpu_mask = -1l;
void print_usage() {
    puts("Usage:\n");
    puts("-h\tdisplay this usage message");
    puts("-c\ttarget cpu to run on (default: any)");
    puts("-d\tsampling duration in seconds (default: 60)");
    puts("-r\tjitter reporting interval in milliseconds (default: 1000)");
    puts("-t\tjitter reporting threshold in nanoseconds; delays below this value will not be reported (default: 300)");
    puts("-o\toutput results using an output plugin. Supported plugins:");
    puts("\tinflux://<host:port>\tstores results in influxDb with line protocol over UDP");
    puts("\tcsv://<file>\tstores results in a csv file");
    exit(0);
}
unsigned long long nano_time() {
    clock_gettime(CLOCK_REALTIME, &ts);
    return ts.tv_sec * NANOS_IN_SEC + ts.tv_nsec;
}
unsigned long long int capture_jitter(unsigned long long int duration, unsigned long long int granularity,
                                      unsigned long long int threshold, struct jitter *jitter) {
    unsigned long long ts = nano_time();
    unsigned long long deadline = ts + duration;
    unsigned long long next_report = ts + granularity;
    unsigned long long max = 0;
    unsigned long long idx = 0;
    sched_setaffinity(0, sizeof(long long), (const cpu_set_t *) &cpu_mask);
    while (ts < deadline) {
        unsigned long long now = nano_time();
        unsigned long long latency = now - ts;
        if (latency > max) max = latency;
        if (now > next_report) {
            if (max > threshold) jitter[idx++] = (struct jitter) {.timestamp = now, .delay = max};
            max = 0;
            next_report = now + granularity;
        }
        ts = now;
    }
    return idx;
}
int main(int argc, char* argv[]) {
    unsigned long long duration = 60 * NANOS_IN_SEC;
    unsigned long long granularity = NANOS_IN_SEC;
    unsigned long long threshold = 300;
    process_output out_function;
    int idx = 1;
    for (; idx < argc; idx++) {
        if (strcmp("-h", argv[idx]) == 0) print_usage();
        else if (strcmp("-c", argv[idx]) == 0) cpu_mask = 1 << strtol(argv[++idx], (char **)NULL, 10);
        else if (strcmp("-d", argv[idx]) == 0) duration = strtol(argv[++idx], (char **)NULL, 10) * NANOS_IN_SEC;
        else if (strcmp("-r", argv[idx]) == 0) granularity = strtol(argv[++idx], (char **)NULL, 10) * 1000000ul;
        else if (strcmp("-t", argv[idx]) == 0) threshold = strtol(argv[++idx], (char **)NULL, 10);
        else if (strcmp("-o", argv[idx]) == 0) {
            char *output = argv[++idx];
            if (strstr(output, "influx://")) out_function = init_influx(output+9);
            else if (strstr(output, "csv://")) out_function = init_csv(output+6);
        }
    }
    printf("duration: %llus\n", duration/NANOS_IN_SEC);
    printf("report interval: %llums\n", granularity/1000000);
    printf("report threshold: %lluns\n", threshold);
    struct jitter* jitter = calloc(duration/granularity, sizeof(struct jitter));
    long long data_points = capture_jitter(duration, granularity, threshold, jitter);
    out_function(data_points, jitter);
    return 0;
}

influxDb和CSV输出插件位于单独的源文件中。 整个项目可以从以下位置签出
我的gitlab仓库

与jHiccup不同,它仅捕获每个报告间隔的最大观察到的延迟,但是此工具的想法是提供具有经典时间序列特征的高分辨率数据。 具有密集的数据序列使您可以整体上对其进行计算和转换,也可以按任意时间窗口对其进行分区。

不幸的是,据我所了解的数学原理,在大多数情况下,使用一系列连续的直方图无法可靠地做到这一点,因此拥有原始数据可能是一个优势。

最终获得数据后,我们就可以使用我们最喜欢的工具(例如Grafana)(使用influxDb作为数据源)开始对其进行分析:

或R(带有CSV文件):

在下一篇文章中,我将展示如何使这些数据变得有用,而不仅仅是在我们的糖果图上看起来不错,并找出所有这些有趣的峰值背后的罪魁祸首。

请记住,抖动采样器代码未完善,缺少错误检查,可能会使用一些优化方法(例如,mmap与
MAP_HUGETLB而不是calloc),请随时提供修复/改进。

翻译自: https://www.javacodegeeks.com/2017/04/code-slow-sometimes-find-whos-behind.html

为什么硬盘有时快有时慢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值