cfor


License

License

GroupId

GroupId

io.github.metarank
ArtifactId

ArtifactId

cfor_2.12
Last Version

Last Version

0.2
Release Date

Release Date

Type

Type

jar
Description

Description

cfor
cfor
Project URL

Project URL

https://github.com/metarank/cfor
Project Organization

Project Organization

io.github.metarank
Source Code Management

Source Code Management

https://github.com/metarank/cfor

Download cfor_2.12

How to add to project

<!-- https://jarcasting.com/artifacts/io.github.metarank/cfor_2.12/ -->
<dependency>
    <groupId>io.github.metarank</groupId>
    <artifactId>cfor_2.12</artifactId>
    <version>0.2</version>
</dependency>
// https://jarcasting.com/artifacts/io.github.metarank/cfor_2.12/
implementation 'io.github.metarank:cfor_2.12:0.2'
// https://jarcasting.com/artifacts/io.github.metarank/cfor_2.12/
implementation ("io.github.metarank:cfor_2.12:0.2")
'io.github.metarank:cfor_2.12:jar:0.2'
<dependency org="io.github.metarank" name="cfor_2.12" rev="0.2">
  <artifact name="cfor_2.12" type="jar" />
</dependency>
@Grapes(
@Grab(group='io.github.metarank', module='cfor_2.12', version='0.2')
)
libraryDependencies += "io.github.metarank" % "cfor_2.12" % "0.2"
[io.github.metarank/cfor_2.12 "0.2"]

Dependencies

compile (2)

Group / Artifact Type Version
org.scala-lang : scala-library jar 2.12.13
org.scala-lang : scala-reflect jar 2.12.13

test (1)

Group / Artifact Type Version
org.scalatest : scalatest_2.12 jar 3.2.6

Project Modules

There are no modules declared in this project.

cfor: fast java-style for and foreach loop in scala

CI Status Maven Central License: Apache 2

This tiny project is a cfor macro inspired by the one originally implemented in Typelevel spire library. Supports Scala 2.12 and 2.13 and has zero dependencies.

Example

An index loop:

import io.github.metarank.cfor._
def sum(values: Array[Int]) = {
  var result = 0
  cfor(0)(_ < values.length, _ + 1) { i => result += values(i) }
  result
}

A foreach loop:

import io.github.metarank.cfor._
def sum(values: Array[Int]) = {
  var result = 0
  cfor(values) { result += _ }
  result
}

A range loop:

import io.github.metarank.cfor._
def sum(values: Array[Int]) = {
  var result = 0
  cfor(0 to values.length by 2) { i => result += values(i) }
  result
}

All these cfor calls are expanded into a while loop with no boxing, no extra allocations and with the same performance as a pure java for loop.

Installation

Available on maven central for scala 2.12 and 2.13

libraryDependencies += "io.github.metarank" %% "cfor" % "0.2"

Purpose

In Java, there is a well-known for-loop:

public int sum(int[] values) {
    int result = 0;
    for (int i=0; i<values.length; i++) {
        result += values[i];
    }
    return result;
}

It can be even simplified with a foreach loop:

public int sum(int[] values) {
        int result = 0;
        for (int value: values) {
            result += value;
        }
        return result;
}

But the for construct in Scala is not the same thing as in Java and can do much more. The most straigthforward solution will be to have something like this:

// foreach style
def sumForeach(values: Array[Int]) = {
  var sum = 0
  for { value <- values } { sum += value }
  sum
}

// scala collections style
def sum(values: Array[Int]) = values.sum

// old good while loop
def sumWhile(values: Array[Int]) = {
  var sum = 0
  var i = 0
  while(i < values.length) {
    sum += values(i)
    i += 1
  }
  sum
}

But nice syntax comes with a price: performance and memory allocations:

[info] Benchmark       length)  Mode  Cnt     Score     Error  Units
[info] scalaForSum        1000  avgt   10  1783.558 ±  47.969  ns/op
[info] scalaSum           1000  avgt   10  5143.344 ± 242.712  ns/op
[info] scalaWhileSum      1000  avgt   10   253.662 ±   2.407  ns/op
[info] Benchmark                               (length)  Mode  Cnt      Score      Error   Units
[info] scalaForSum:·gc.alloc.rate.norm            1000  avgt   10     32.001 ±    0.001    B/op
[info] scalaSum:·gc.alloc.rate.norm               1000  avgt   10  12352.002 ±    0.001    B/op
[info] scalaWhileSum:·gc.alloc.rate.norm          1000  avgt   10     ≈ 10⁻⁴               B/op

So if you write a performance-critical code, then you only can use while loops, which are much more verbose that Java for loops.

Here comes a cfor: a yet another syntax sugar macro which expands to a while loop:

def sum(values: Array[Int]) = {
  var result = 0
  cfor(values) { result += _ }
  result
}

It has zero extra allocations and exactly the same performance as a while/for loop.

Performance

The benchmark is done on Scala 2.13.5, AdoptOpenJDK 11.0.10 x64.

[info] Benchmark                                 (length)  Mode  Cnt      Score     Error   Units
[info] cforForeachSum                                1000  avgt   30    246.620 ±   0.836   ns/op
[info] cforSum                                       1000  avgt   30    248.275 ±   1.001   ns/op
[info] scalaCollectionsSum                           1000  avgt   30   5058.811 ± 139.946   ns/op
[info] scalaForeachSum                               1000  avgt   30   1769.533 ±  42.385   ns/op
[info] scalaWhileSum                                 1000  avgt   30    247.857 ±   0.358   ns/op

[info] cforForeachSum:·gc.alloc.rate.norm            1000  avgt   30     ≈ 10⁻³              B/op
[info] cforSum:·gc.alloc.rate.norm                   1000  avgt   30     ≈ 10⁻³              B/op
[info] scalaCollectionsSum:·gc.alloc.rate.norm       1000  avgt   30  11813.341 ±  35.878    B/op
[info] scalaForeachSum:·gc.alloc.rate.norm           1000  avgt   30     32.003 ±   0.004    B/op
[info] scalaWhileSum:·gc.alloc.rate.norm             1000  avgt   30     ≈ 10⁻³              B/op

License

This project is released under the Apache 2.0 license, as specified in the LICENSE file.

Versions

Version
0.2
0.1