CPU 节能与性能平衡优化:C-States 和 P-States 详解

4,353 阅读18分钟

介绍

CPU 如何节省电量?您将在本文中了解有关 CPU 电源管理的所有基础知识。

处理器/CPU 的设计目标是在特定负载下永远运行。由于我们几乎没有人会24x7持续使用 CPU 的所有资源进行计算,因此大多数时候 CPU 都无法以设计的最大速度运行。那么,让整个 CPU 保持满负荷运行的意义何在?这就是 CPU 电源管理的意义所在。电源管理主题涵盖的内容远不止这些,包括 RAM、GPU 等,但在本文中,我将仅向您介绍 CPU 方面的内容。

这篇文章中的一般信息适用于所有现代 CPU,但细节可能非常具体到单个 CPU(尤其是处理器系列),我使用的是 Intel® Xeon® E3-1245 v5 @ 3.50GHz,这是 Xeon E3–1200 v5 系列(以前称为 Skylake)处理器(型号 0x5e)。

本文主要参考了以下资料:

CPU 功能

在其官方产品页面上,我的 CPU 列出了以下特点:

  • 空闲状态
  • 增强型 Intel SpeedStep® 技术

您可以在页面上看到“空闲状态 (C 状态) 用于在处理器空闲时节省电量。”和“Intel SpeedStep® 技术根据处理器负载在高低电平之间同时切换电压和频率。”

您将在本文中了解这两种描述的含义。

如何降低CPU运行时的功耗?

在生产 CPU 上(因此不考虑在设计 CPU 时可以做的事情),为了节省电量,您可以做两件事。您可以:

  • 通过完全关闭子系统(核心或其他资源,如时钟或缓存)的电源(从而降低电压,将其降至零)来消除子系统的功耗

或者

  • 通过降低子系统和/或整个处理器的电压和/或频率来降低​​功耗

第一个很容易理解,如果你关闭电源,就不会消耗任何电量。

第二点需要多解释一下。集成电路(例如处理器)的功耗与频率成线性比例,与电压成二次比例。

P ~ fV²

对于那些对数字电子产品了解更多的人来说请注意:

Pcpu其实就是=Pdynamic+Psc+Pleak

在活动的 CPU 上,Pdynamic 是最重要的部分,它是我在这里提到的部分,与频率成正比,与电压成二次方。

Psc=Pshort-circuit与频率成正比。

漏极电压与电压成正比。

而且,电压和频率并不是独立的,似乎呈线性关系。(async.org.uk/tech-report…

更高的(串行)性能需要更高的频率和更高的电压,因此对功耗的影响更大。

CPU 消耗的最大功率是多少?

这在很大程度上取决于处理器,但 E3–1245 v5 @ 3.50 Ghz 的热设计功耗 (TDP) 为 80 瓦。这是 CPU 可以永久维持的平均值(下图中的功率限制/PL1),通常也是冷却解决方案应设计为可靠性的值。CPU 消耗的实际功率可能会暂时更高(如下图所示为 PL2、PL3 和 PL4)。TDP 是在高复杂度(~最坏情况)负载下测量的,所有核心均以基本频率(3.50 Ghz)运行。

CPU 封装电源控制

CPU 封装电源控制

您可以在上图中看到,CPU 在 PL2 下消耗的功率最多可以超过 TDP 达 100 秒,这实际上是一个相当长的持续时间。

处理器功率状态(C 状态)与性能状态(P 状态)

降低处理器功耗的两种方法:

  • 关闭子系统
  • 电压/频率降低

分别通过使用以下方法实现:

  • C 状态
  • P 状态

C 状态描述的是第一种情况,因此它们是空闲(省电)状态。为了关闭子系统,该子系统不应运行任何程序,因此它应处于空闲状态,不执行任何操作,也不执行任何操作。因此,C 状态 x(Cx)表示 CPU 的一个或多个子系统处于空闲状态,即关闭状态。

另一方面,P 状态描述的是第二种情况,因此它们是执行(省电)状态。子系统实际上正在运行,但不需要完全发挥性能,因此其运行的电压和/或频率会降低。因此,P 状态 x,即 Px,表示它所指的子系统(例如 CPU 核心)正在以特定的(频率、电压)对运行。

由于大多数现代 CPU 在单个封装中都有多个内核,因此 C 状态进一步分为内核 C 状态(CC 状态)和封装 C 状态(PC 状态)。存在 PC 状态的原因是处理器中还有其他(共享)组件,在使用它们的所有内核关闭后,这些组件也可以关闭(例如共享缓存)。但是,作为用户或程序员,我们无法控制这些组件,因为我们不直接与封装交互,而是与各个内核交互。因此,我们只能直接影响 CC 状态,而 PC 状态则根据内核的 CC 状态受到间接影响

状态从零开始编号,如 C0、C1… 和 P0、P1… 数字越高,节省的电量越多。C0 表示不通过关闭某些设备来节省电量,因此所有设备都处于开启状态。P0 表示最大性能,因此使用的最大频率、电压和功率。

C 状态

基本 C 状态(由 ACPI 定义)包括:

  • C0:活动,CPU/核心正在执行指令。P 状态与此相关,CPU/核心可能以最大性能运行(因此处于 P0)或以较低的性能/功率运行(因此处于 P0 以外的任何状态)。
  • C1:暂停,不执行任何操作,但可以立即返回 C0。由于它不工作(但已暂停),因此 P 状态与 C1 或除 C0 之外的任何 Cx 无关。
  • C2:停止时钟,与 C1 类似,但返回到 C0 需要更长的时间。
  • C3:睡眠。它可以返回到 C0,但需要更长的时间。

许多现代 CPU 具有更多 C 状态,根据其数据表,Intel® Xeon® E3-1200 v5 系列具有 C0、C1、C1E(C1 增强型)、C2、C3、C6、C7 和 C8。C1 和 C1E 仅为 CC 状态,而 C2 仅为 PC 状态。所有其他状态均为 CC 状态和 PC 状态。

注意:由于 Intel 超线程,因此还存在线程级 C 状态。但是,单个线程只能请求 C 状态,但只有当核心进入该 C 状态时才会发生省电操作。我一般不会在这篇文章中讨论线程 C 状态和超线程。

数据表中对这些状态的描述是:

处理器 IA 核心/封装状态支持表

处理器 IA 核心/封装状态支持表

注:上面的LLC指的是Last Level Cache,也就是处理器中的共享的L3缓存。

这里有一个直观的描述:

灵活的 C 状态可选择空闲功率水平与响应能力(从软件影响到平台能效白皮书)

灵活的 C 状态可选择空闲功率水平与响应能力(从软件影响到平台能效白皮书)

使用 C 状态省电的一个非常基本的时间表是:

  • 正常运行在C0。
  • 首先,空闲核心的时钟停止。(C1)
  • 然后,刷新核心的本地缓存(L1/L2)并关闭核心电源。(C3)
  • 然后,当所有核心都断电时,包的共享缓存 (L3/LLC) 将被刷新,最后包/整个 CPU 可以 (几乎) 断电。我说几乎是因为我猜必须有某个东西通电才能返回到 C0。

您可能已经猜到了,CC 状态和 PC 状态并不是独立的,因此有些组合是不可能的。下图总结了这一点:

Intel Xeon E3–1200 v5 产品系列的处理器封装和 IA 核心 C 状态

Intel Xeon E3–1200 v5 产品系列的处理器封装和 IA 核心 C 状态

显然,如果某个核心正在运行(C0),则该包不能处于 C0 以外的状态。另一方面,如果某个核心完全断电(C8),则只要任何其他核心仍在运行,该包仍可以处于 C0。

注意:英特尔软件开发人员手册提到了子 C 状态或子状态,这意味着 C 状态(类型)实际上包含一个或多个子 C 状态。在检查 Linux 内核中的 intel_idle 驱动程序代码后,我了解到例如 C1 和 C1E 具有相同的状态类型 C1,但它们是子类型 0 和 1。

CPUID 指令可以发现 C1..C8 的八个 C 状态类型 (0..7) 的子类型数量。查看我的 CPU,cpuid 程序输出返回:

MONITOR/MWAIT (5):
      smallest monitor-line size (bytes)       = 0x40 (64)
      largest monitor-line size (bytes)        = 0x40 (64)
      enum of Monitor-MWAIT exts supported     = true
      supports intrs as break-event for MWAIT  = true
      number of C0 sub C-states using MWAIT    = 0x0 (0)
      number of C1 sub C-states using MWAIT    = 0x2 (2)
      number of C2 sub C-states using MWAIT    = 0x1 (1)
      number of C3 sub C-states using MWAIT    = 0x2 (2)
      number of C4 sub C-states using MWAIT    = 0x4 (4)
      number of C5 sub C-states using MWAIT    = 0x1 (1)
      number of C6 sub C-states using MWAIT    = 0x0 (0)
      number of C7 sub C-states using MWAIT    = 0x0 (0)

还请注意英特尔手册:“MWAIT 扩展的 C0 到 C7 状态的定义是特定于处理器的 C 状态,而不是 ACPI C 状态”。因此,不要将它们与我之前提到的 ACPI C 状态混淆,它们显然是相关的,并且之间存在映射,但它们并不完全相同。

我已经根据我的 CPU(型号 0x5e)的驱动程序源代码创建了下面的图表intel_idle。横轴标签表示 C 状态名称:特定于处理器的状态:特定于处理器的子状态,纵轴表示驱动程序源代码中的退出延迟和目标驻留值。exit_latency 用作评估此状态的实时延迟影响的指导(例如,从此状态返回到 C0 需要多长时间),目标驻留表示核心必须停留在此状态以克服进入/退出功率成本的最短时间。注意纵轴上的对数刻度,更深状态的延迟和盈亏平衡功率影响呈指数级增长。

intel_idle 驱动程序源代码中的 C 状态退出延迟和目标驻留常量

intel_idle 驱动程序源代码中的 C 状态退出延迟和目标驻留常量

注意:虽然表中有 C9 和 C10,但由于它们有 0 个子状态,因此在我的 CPU 中未使用它们。同一系列中的其他 CPU 也可能支持这些状态。

ACPI 电源状态

在介绍 P 状态之前,最好先介绍一下 ACPI 电源状态。作为用户,我们在使用计算机时通常知道这些状态。所谓的全局系统状态 (Gx 状态) 如下所列:

全局电源状态摘要(来自 ACPI 规范 v6.2)

全局电源状态摘要(来自 ACPI 规范 v6.2)

还有一种特殊的全局状态 G1/S4,即非易失性睡眠,其中系统状态保存到非易失性存储器(如磁盘)然后关闭。这使得可以像软关机状态一样消耗最少的电量,但无需重启即可从此状态返回 G0。这就是我们所知的休眠或挂起到磁盘。

然后是睡眠状态(Sx 状态)。包括 S0(无睡眠状态),共有 6 种睡眠状态。S1-S4 与 G1 一起使用,S5 是与 G2 一起使用的 Soft Off 睡眠状态。简单总结一下:

  • GO/S0:计算机正在运行,而不是休眠状态。
  • G1:睡觉
    • G1/S1:挂起时通电。系统状态被保留,因此 CPU 和 CPU 缓存仍然通电。
    • G1/S2:CPU 断电。因此 CPU 和 CPU 缓存丢失。
    • G1/S3:待机、睡眠或挂起到 RAM (STR)。系统 RAM 保持通电状态。
    • G1/S4:休眠或挂起到磁盘。所有内容都保存到非易失性存储器(如磁盘),因此 RAM 和可能整个系统都处于关闭状态。
  • G2/S5:软关机。与机械关机类似,但可以将计算机从省电模式唤醒的装置功率最小。不保存任何状态,因此需要重新启动才能返回 G0。
  • G3:机械关闭。电源单元断开连接(例如通过电源开关)。只有实时时钟 (RTC) 之类的东西在运行,因为它们有自己的小电池。显然没有保存任何状态,因此需要重新启动才能返回到 G0。

对于我的 CPU,请参见下图,上面提到的所有 C 状态都处于 ACPI G0/S0。基本上,它说的是,一旦进入任何睡眠模式 (G1),CPU(封装)就会断电。

Intel Xeon 处理器 E3-1200 v5 产品系列的处理器功率状态

Intel Xeon 处理器 E3-1200 v5 产品系列的处理器功率状态

因此支持的 ACPI 状态是:

Intel Xeon 处理器 E3–1200 v5 产品系列支持的 ACPI 系统状态

Intel Xeon 处理器 E3–1200 v5 产品系列支持的 ACPI 系统状态

ACPI G/S 状态和 CPU C 状态组合

很高兴在表格中看到所有这些组合:

Intel Xeon 处理器 E3–1200 v5 产品系列的 G、S 和 C 接口状态组合

Intel Xeon 处理器 E3–1200 v5 产品系列的 G、S 和 C 接口状态组合

在 G0/S0/C8 中,只有处理器包通电,但所有核心都断电。

在 G1(S3 或 S4)中,没有(有效的)C 状态(无 CC 无 PC),因为 CPU 已完全关闭。

对于 G3,没有 S 状态。系统不是处于休眠状态,而是机械断电,无法唤醒,必须先开机。

如何以编程方式请求更低功耗的 C 状态?

请求低功耗状态(从而将状态从 C0 更改为其他状态)的现代(但不是唯一)方法是使用 MWAIT 或 HLT 指令。这些是特权指令,用户程序无法执行它们。

MWAIT(监控等待)指示处理器在等待对指定地址范围(由另一条指令 MONITOR 设置)的写入/写入时进入优化状态(C 状态)。对于电源管理,MWAIT 与 EAX 一起使用,EAX[7:4] 位指示目标 C 状态,EAX[3:0] 指示子 C 状态。

注意:我相信目前只有 AMD 有这个功能,但也有 MONITORX/MWAITX 指令,除了监控地址范围的写入之外,还检查计时器的到期时间。这也称为定时 MWAIT。

HLT(Halt)指令停止执行,内核进入 HALT 状态,直到发生中断。这意味着内核切换到 C1 或 C1E 状态。

什么会触发 CPU 核心进入 Cx 状态?

一个简单的答案是:

  • 当发生中断或 MWAIT 指令监视的地址上发生写入事件时,在启动时进入 C0。
  • C1/C1E 与 HLT 或 MWAIT 指令一起输入。
  • 使用 MWAIT 指令进入 C3,然后 L1 和 L2 缓存刷新到 LLC,所有核心时钟停止。但是,核心保持其状态,因为它仍然通电。
  • 使用 MWAIT 指令进入 C6,然后将处理器状态保存到专用 SRAM 中,并将核心电压降至零。在此状态下,核心没有电源。退出 C6 时,处理器状态从 SRAM 恢复。
  • 对于核心来说,C7 和 C8 与 C6 相同。

再次提醒一下,我在这个答案中省略了超线程。

正如我之前在图表中展示的那样,深度 C 状态的转换具有更高的延迟和能源成本。因此,这种转换应该谨慎进行,尤其是在电池供电的设备上。

是否可以禁用 C 状态(因此始终使用 C0)?

这是可能的,但不建议。数据表(第 4.2.2 节第 64 页)中有这样一条注释:“除非启用所有低功耗空闲状态,否则无法保证长期可靠性”。因此您真的不应该禁用 C 状态。

中断如何影响处于睡眠状态的处理器/核心?

当触发中断时,必须唤醒相应的核心并将其置于 C0 状态。但是,例如,Intel Xeon E3-1200 v5 具有一项称为“电源感知中断路由 (PAIR)”的功能,它有两个好处:

  • 为了节省电量,可以将中断路由到活动核心,以免唤醒空闲核心
  • 为了提高性能,可以将中断路由到空闲 (C1) 核心,而不是已经 (高度) 工作的核心

P 状态

P 状态意味着 CPU 核心也处于 C0 状态,因为它必须通电才能执行代码。P 状态基本上允许改变 CPU 核心的电压和频率(换句话说,工作点)以降低功耗。有一组 P 状态对应于不同的工作点(电压-频率对),而 P 状态指的是一个这样的工作点。最高(频率和电压)工作点是最高性能状态,即 P0。

可以在 OS/操作系统控制(Intel SpeedStep® 技术)或硬件控制(Intel® Speed Shift 技术)模式下使用 Intel Xeon E3–1200 v5 中的 P 状态。以下有关 P 状态的信息特定于 Intel Xeon E3–1200 v5 系列,但我猜其他现代处理器也一样或类似。

操作系统控制的 P 状态

在这种方法中,OS 知道 P 状态,并且 OS 会请求特定的 P 状态。这基本上意味着选择工作频率,而电压则由处理器根据频率和其他因素自动选择。通过将 P 状态写入特定于模型的寄存器(即向 IA32_PERF_CTL 写入 16 位值)来请求 P 状态后,电压将转换为自动计算的值,并且时钟发生器 (PLL) 将锁定到请求的频率。所有核心共享相同的 P 状态,因此无法为核心单独设置它。可以从另一个特定于模型的寄存器(IA32_PERF_STATUS)读取当前 P 状态/工作点。

P 状态转换非常快,因此每秒可以进行多次转换。这与 C 状态转换有很大不同,C 状态转换具有更高的延迟和功耗成本

HW 控制的 P 状态

在这种方法中,OS 知道硬件对控制 P 状态的支持,并发出仅指定工作负载要求的请求,不请求特定的 P 状态或频率。根据 OS 给出的提示以及许多其他因素和限制,硬件会选择一个 P 状态。

我将在另一篇文章中对此进行更详细的介绍,但只是为了让您有个概念,我当前的 Linux 桌面以这种模式运行,我通过检查 IA32_PM_ENABLE 了解这一点,最高(非保证)性能级别为 39,最低性能级别为 1。这大致意味着有 39 种不同的 P 状态。目前,操作系统请求 39 作为最低和最高性能,并且优先考虑性能,这些都是因为我已禁用内核中的动态 CPU 频率变化。

关于英特尔® Turbo Boost 的说明

由于 TDP(热设计功率)是 CPU 可以承受的最大功率,当功耗低于此值且在某些其他条件下时,CPU 频率可以超过基本频率(此 CPU 从 3.50 GHz 增加到 3.90 GHz),因为这种条件不会使功耗超过 TDP。Turbo Boost 还可以在短时间内暂时将功耗增加到 PL2/Power Limit 2。可以通过向硬件提供提示来修改 Turbo Boost 的行为。

上述有关 C 状态和 P 状态的信息是否适用于移动/笔记本电脑处理器或嵌入式/手机处理器?

例如,最近的 MacBook Air 处理器 i5-5350U 主要具有我上面描述的所有功能(我不确定硬件控制的 P 状态)。我还查看了 ARM Cortex-A 文档,虽然术语不同,但看起来它的电源控制机制非常相似。

所有这些实际上是如何工作的呢,例如在 Linux 中?

我们将在另一篇文章中回答这个问题,敬请关注。

我如何监控我的 CPU?

并不是很多应用程序都可以看到这些信息,但是你可以使用例如CoreFreq

这里显示系统信息。(以下是所有输出的一部分)

$ ./corefreq-cli -s
Processor                            [Intel(R) Xeon(R) CPU E3-1245 v5 @ 3.50GHz]
|- Architecture                                                      [Skylake/S]
|- Vendor ID                                                      [GenuineIntel]
|- Microcode                                                        [       198]
|- Signature                                                            [ 06_5E]
|- Stepping                                                             [     3]
|- Online CPU                                                           [  4/4 ]
|- Base Clock                                                           [100.12]
|- Frequency            (MHz)                      Ratio
                 Min    800.94                    [   8 ]
                 Max   3504.10                    [  35 ]
|- Factory                                                              [100.00]
                       3500                       [  35 ]
|- Turbo Boost                                                          [UNLOCK]
                  1C   3904.57                    <  39 >
                  2C   3804.45                    <  38 >
                  3C   3704.33                    <  37 >
                  4C   3604.22                    <  36 >
|- Uncore                                                               [UNLOCK]
                 Min    800.94                    <   8 >
                 Max   3904.57                    <  39 >

...

Technologies:
|- System Management Mode                                   SMM-Dual       [ ON]
|- Hyper-Threading                                               HTT       [OFF]
|- SpeedStep                                                    EIST       < ON>
|- Dynamic Acceleration                                          IDA       [ ON]
|- Turbo Boost                                                 TURBO       < ON>
|- Virtualization                                                VMX       [ ON]
   |- I/O MMU                                                   VT-d       [OFF]
   |- Hypervisor                                                           [OFF]

Performance Monitoring:
|- Version                                                        PM       [  4]
|- Counters:          General                   Fixed
|                     8 x 48 bits             3 x 48 bits
|- Enhanced Halt State                                           C1E       <OFF>
|- C1 Auto Demotion                                              C1A       < ON>
|- C3 Auto Demotion                                              C3A       < ON>
|- C1 UnDemotion                                                 C1U       < ON>
|- C3 UnDemotion                                                 C3U       < ON>
|- Frequency ID control                                          FID       [OFF]
|- Voltage ID control                                            VID       [OFF]
|- P-State Hardware Coordination Feedback                MPERF/APERF       [ ON]
|- Hardware-Controlled Performance States                        HWP       [ ON]
|- Hardware Duty Cycling                                         HDC       [ ON]
|- Package C-State
   |- Configuration Control                                   CONFIG   [   LOCK]
   |- Lowest C-State                                           LIMIT   [      0]
   |- I/O MWAIT Redirection                                  IOMWAIT   [Disable]
   |- Max C-State Inclusion                                    RANGE   [      0]
|- MWAIT States:    C0    C1    C2    C3    C4    C5    C6    C7
|                    0     2     1     2     4     1     0     0
|- Core Cycles                                                         [Present]
|- Instructions Retired                                                [Present]
|- Reference Cycles                                                    [Present]
|- Last Level Cache References                                         [Present]
|- Last Level Cache Misses                                             [Present]
|- Branch Instructions Retired                                         [Present]
|- Branch Mispredicts Retired                                          [Present]

Power & Thermal Monitoring:
|- Clock Modulation                                             ODCM   <Disable>
   |- DutyCycle                                                        <  6.25%>
|- Power Management                                         PWR MGMT   [   LOCK]
   |- Energy Policy                                        Bias Hint   [      0]
|- Junction Temperature                                        TjMax   [  0:100]
|- Digital Thermal Sensor                                        DTS   [Present]
|- Power Limit Notification                                      PLN   [Present]
|- Package Thermal Management                                    PTM   [Present]
|- Thermal Monitor 1                                         TM1|TTP   [ Enable]
|- Thermal Monitor 2                                         TM2|HTC   [Present]
|- Units
   |- Power                                               watt   [  0.125000000]
   |- Energy                                             joule   [  0.000061035]
   |- Window                                            second   [  0.000976562]

这里显示有关内核的信息,包括空闲驱动程序:(以下是所有输出的一部分)

$ ./corefreq-cli -k
Linux:
|- Release                                                   [4.15.0-45-generic]
|- Version                         [#48-Ubuntu SMP Tue Jan 29 16:28:13 UTC 2019]
|- Machine                                                              [x86_64]
...
Idle driver                                                        [@intel_idle]
   |- State:          POLL    C1      C1E     C3      C6      C7s     C8
   |- Power:          -1      0       0       0       0       0       0
   |- Latency:        0       2       10      70      85      124     200
   |- Residency:      0       2       20      100     200     800     800

这里监控包:

$ ./corefreq-cli -g
		Cycles		State(%)
PC02	        1121802850	  32.49
PC03	        1298328500	  37.83
PC06	                 0	   0.00
PC07	                 0	   0.00
PC08	                 0	   0.00
PC09	                 0	   0.00
PC10	                 0	   0.00
PTSC	        3503877892
UNCORE	            150231

这里监控(核心)C 状态的计数器:

$ ./corefreq-cli -c
CPU Freq(MHz) Ratio  Turbo  C0(%)  C1(%)  C3(%)  C6(%)  C7(%)  Min TMP:TS  Max
#00  355.67 ( 3.55)  10.15  10.28  26.43   0.04  11.49  51.77  41 / 45:55 / 56
#01  355.64 ( 3.55)  10.15  10.38  19.21   0.68  15.44  54.28  42 / 45:55 / 55
#02  389.95 ( 3.89)  11.13  11.35  15.67   0.16  18.17  54.65  40 / 43:57 / 54
#03  365.38 ( 3.65)  10.43  10.61  19.77   0.18  13.93  55.51  40 / 43:57 / 54

    Averages:        Turbo  C0(%)  C1(%)  C3(%)  C6(%)  C7(%)    TjMax:    Pkg:
                     10.46  10.66  20.27   0.27  14.76  54.05     100 C    46 C

这里监测功率和电压:

$ ./corefreq-cli -V
CPU Freq(MHz) VID  Vcore
#00  130.70     0  0.0000
#01  120.08     0  0.0000
#02  124.18     0  0.0000
#03  103.46  9784  1.1943

              Package        Cores          Uncore         Memory
Energy(J):   13.415222168    2.248596191    0.000000000    0.951416016
Power(W) :   26.830444336    4.497192383    0.000000000    1.902832031