# Performance of splitting string and parsing numbers

**URL:** <https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161>\
**Category:** Performance\
**Created:** [December 26, 2022, 7:19pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161 "2022-12-26T19:19:58Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [December 26, 2022, 7:19pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/1 "2022-12-26T19:19:58Z")

</div>

Given a string of numbers that are grouped in blocks, I want to parse the numbers and find the total of each block, and then return the largest value from the totals. Each block is separated by two newline chars, and each number is separate a single newline char e.g.

```julia-auto
data = "1\n2\n\n3\n4"

```

My solution is extremely simple:

```julia-auto
function solve(data)
    maximum(eachsplit(data, "\n\n")) do block
        sum(parse(Int64, num) for num in eachsplit(block, "\n"))
    end
end

```

# The Problem

But, it’s _slow_. Here’s the test result from my dataset (255 blocks, 2000 numbers overall):

```julia-auto
julia> @btime solve($data)
  309.959 μs (3757 allocations: 234.77 KiB)

```

The exact same algorithm in Rust is 10x faster:

```rust
pub fn solve() -> i64 {
    return include_str!("../input.txt")
        .split("\n\n")
        .map(|e| e.lines().map(|c| c.parse::<i64>().unwrap()).sum::<i64>())
        .max()
        .unwrap();
}

```

Rust Criterion benchmark result as follows:

```julia-auto
solve time: [30.432 µs 30.455 µs 30.481 µs]

```

# An iterative solution

Of course, we Julians can always make our code run fast! So I rewrote the code to solve the problem manually without any iterators/eachsplit functions:

```julia
function solve2(data)
    buf = IOBuffer(data)
    largest = 0
    total = 0
    for line in eachline(buf)
        if line == "" # in between two newline characters
            # Update largest number and reset for next block
            largest = max(largest, total)
            total = 0
            continue
        end
        val = parse(Int, line)
        total += val
    end
    # handle last one
    largest = max(largest, total)
    return largest
end

```

And the result is better but not too great yet since it’s still off by 4x from the Rust benchmark:

```julia-auto
julia> @btime solve2($data)
  120.917 μs (4520 allocations: 335.45 KiB)

```

If I just use the regular `split` function rather than creating an IOBuffer and use `eachline`, then the number of allocations is greatly smaller but the runtime is comparable.

```julia-auto
function solve3(str)
    largest = 0
    total = 0
    for line in split(str, "\n")
        if line == "" # in between two newline characters
            # Update largest number and reset for next block
            largest = max(largest, total)
            total = 0
            continue
        end
        val = parse(Int, line)
        total += val
    end
    # handle last one
    largest = max(largest, total)
    return largest
end

```

```julia-auto
julia> @btime solve3($data)
  125.000 μs (7 allocations: 160.94 KiB)

```

# Too good to be true

Interestingly, if I create and pass an `IOBuffer` separately, then the runtime is greatly improved 1,000x.

```julia-auto
function solve1(buf)
    largest = 0
    total = 0
    for line in eachline(buf)
        if line == "" # in between two newline characters
            # Update largest number and reset for next block
            largest = max(largest, total)
            total = 0
            continue
        end
        val = parse(Int, line)
        total += val
    end
    # handle last one
    largest = max(largest, total)
    return largest
end

```

```julia-auto
julia> @btime solve1($buf) setup=(seekstart($buf))
  138.951 ns (5 allocations: 383 bytes)

```

That seems too good to be true… And, it does not add up. Just creating an `IOBuffer` wouldn’t take 120 μs. Maybe I’m not measuring it correctly? I don’t really trust this result.

# Optimization ideas?

The final question is how to make it run as fast as Rust. I would be fine if it’s just off by 50% but there’s a 4x difference at this point.

You can find the test data here: [https://gist.githubusercontent.com/tk3369/566642a9fb13a8f705c7822ce14a8a16/raw/ccdfed4f63b58a75431677a4cd2b73a351e084c2/gistfile1.txt](https://gist.githubusercontent.com/tk3369/566642a9fb13a8f705c7822ce14a8a16/raw/ccdfed4f63b58a75431677a4cd2b73a351e084c2/gistfile1.txt)

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [December 27, 2022, 8:33am UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/2 "2022-12-27T08:33:02Z")

</div>

> [@tk3369](#):
>
> ```julia
> function solve2(data)
> buf = IOBuffer(input())
> 
> ```

Was this supposed to be `data`?

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [December 27, 2022, 8:35am UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/3 "2022-12-27T08:35:02Z")

</div>

Sorry, copy/paste error. `data` is just a string. The IOBuffer is created from `data` rather than `input()`.

@Elrod I’ve updated the post (again) 🙂

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [December 27, 2022, 2:28pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/5 "2022-12-27T14:28:37Z")

</div>

> [@tk3369](#):
>
> Maybe I’m not measuring it correctly?

I believe so, you’re missing `evals=1` to the benchmark settings:

```julia
julia> io = IOBuffer(input());

julia> @btime solve1($io) setup=(seekstart($io))
  141.788 ns (5 allocations: 385 bytes)
75501

julia> @btime solve1($io) setup=(seekstart($io)) evals=1
  122.917 μs (4518 allocations: 335.32 KiB)
75501

```

If I’m reading the output of [`@profview`](https://github.com/timholy/ProfileView.jl) correctly, all time in your first `solve` function is spent in type inference.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [December 27, 2022, 3:02pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/6 "2022-12-27T15:02:20Z")

</div>

> [@tk3369](#):
>
> The exact same algorithm in Rust is 10x faster:
> 
> ```julia
> pub fn solve() -> i64 {
> return include_str!("../input.txt")
> .split("\n\n")
> .map(|e| e.lines().map(|c| c.parse::<i64>().unwrap()).sum::<i64>())
> .max()
> .unwrap();
> }
> 
> ```

I’m not a Rust expert, but are you sure this is doing anything at runtime? If I put this code into a program

```rust
pub fn solve() -> i64 {
    return include_str!("/tmp/input.txt")
        .split("\n\n")
        .map(|e| e.lines().map(|c| c.parse::<i64>().unwrap()).sum::<i64>())
        .max()
        .unwrap();
}

fn main() {
   println!("{}", solve());
}

```

after I compile this program, whenever I run it I get always the same result, even if I edit the file input file, adding some garbage to make parsing fail or some very large numbers to change the result. Or it’s only `include_str!` that is inlined at compile-time and the rest runs at runtime?

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [December 27, 2022, 3:25pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/7 "2022-12-27T15:25:56Z")

</div>

I was curious and played around a little bit.

I noticed that a significant amount of time is spent on iterating over `eachsplit` or `split` in general.

It’s really just a guess since I am not really a string expert but Julia natively supports unicode so maybe the additional overhead causes the difference between Rust’s `split` and Julia’s `split`, although Rust also support UTF-8 in `str`.  
Btw. a couple of years ago we had `ASCIIString` in Julia which was deprecated somewhere before v1. I am wondering if that would make any difference 😉

…but again, that’s just a guess.

If you compare this (`data` is the content of the provided gist file above):

```julia
function solve(data)
    maximum(eachsplit(data, "\n\n")) do block
        sum(parse(Int64, num) for num in eachsplit(block, "\n"))
    end
end

```

I get

```julia
  491.542 μs (3757 allocations: 234.77 KiB)

```

and this one

```julia
@btime collect(eachsplit($data, "\n\n"))

```

already yields

```julia
167.167 μs (2264 allocations: 140.75 KiB)

```

which is 5 times slower than Rust’s total run time. Then we even have another nested `split` inside `solve` which adds another `150us`-ish to the overall time in case of the Julia implementation.

I am not familiar with the Rust implementation but to me it looks like `split` and probably also `parse` are doing a better job in string processing, **but 10x faster is really suspicious**.

I did a quick check with a crude bytes-parsing implementation:

```julia
function splitbytes(bytes, seq)
    out = Vector{Vector{UInt8}}()
    seq_n = length(seq)
    seq_idx = 1
    chunk = Vector{UInt8}()
    for byte ∈ bytes
        # sequence found, pushing everything before it to `out`
        if byte == seq[seq_idx] && seq_n == seq_idx
            push!(out, chunk[1:end-(seq_n-1)])
            seq_idx = 1
            chunk = Vector{UInt8}()
            continue
        end
        # reset sequence matcher if consecutive byte is not matching
        if byte != seq[seq_idx] && seq_n > 1
            seq_idx = 1
        end
        # increase sequence index to match next time
        if byte == seq[seq_idx]
            seq_idx += 1
        end
        push!(chunk, byte)
    end
    push!(out, chunk)
    out
end

```

and then running that over the very same data and I only get a tiny improvement (20% or so) in runtime and the `Int64` interpretation and `maximum` determination are missing:

```julia
function solvebytes(bytes)
    splitbytes.(splitbytes(bytes, [0xA, 0xA]), [0xA])
end

```

```julia
@btime solvebytes($bytes)
  399.708 μs (7504 allocations: 460.39 KiB)

```

Anyways, are you sure that Rust is not somehow doing some caching behind the scenes, during compilation? To me it’s the only plausible explanation since I don’t know how to do this iteration including comparisons almost 10x faster…

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [December 27, 2022, 3:30pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/8 "2022-12-27T15:30:59Z")

</div>

> [@giordano](#):
>
> after I compile this program, whenever I run it I get always the same result, even if I edit the file input file, adding some garbage to make parsing fail or some very large numbers to change the result. Or it’s only `include_str!` that is inlined at compile-time and the rest runs at runtime?

Ha, you are right 😉 You can even remove the `input.txt` and it will still run and give the same result. It seems it reads in the file at compile time and treats it as a constant or so (or at least bakes it into the binary, since it still takes 40us to run, so something happens).

---

<div class="post-metadata">

**Author:** ![jlapeyre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlapeyre/32/4514_2.png) [@jlapeyre](https://discourse.julialang.org/u/jlapeyre)\
**Post date:** [December 27, 2022, 3:44pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/9 "2022-12-27T15:44:04Z")

</div>

> [@giordano](#):
>
> only `include_str!` that is inlined at compile-time

[include\_str!](https://doc.rust-lang.org/std/macro.include_str.html) is a macro invocation.

> The file is located relative to the current file (similarly to how modules are found). The provided path is interpreted in a platform-specific way at compile time. So, for instance, an invocation with a Windows path containing backslashes \ would not compile correctly on Unix.
> 
> This macro will yield an expression of type `&'static str` which is the contents of the file.

It would be informative to use [load\_str!](https://docs.rs/load_file/latest/load_file/macro.load_str.html) instead:

> This macro can often be a drop-in replacement for include\_str!, switching it to be a run-time rather than compile-time operation.

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [December 27, 2022, 5:05pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/10 "2022-12-27T17:05:23Z")

</div>

The Rust macro inlines the string at compile time. For a fair comparison, I manually inline the same content as a Julia string so it does not involve any I/O.

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [December 27, 2022, 5:08pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/11 "2022-12-27T17:08:08Z")

</div>

> [@giordano](#):
>
> If I’m reading the output of [`@profview`](https://github.com/timholy/ProfileView.jl) correctly, all time in your first `solve` function is spent in type inference.

Type inference should happen only once though? I want to benchmark the runtime not compile time…

---

<div class="post-metadata">

**Author:** ![jlapeyre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlapeyre/32/4514_2.png) [@jlapeyre](https://discourse.julialang.org/u/jlapeyre)\
**Post date:** [December 27, 2022, 6:32pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/12 "2022-12-27T18:32:21Z")

</div>

This is faster. (But not very generic)

```julia
julia> @btime max_num($s)
  24.435 μs (0 allocations: 0 bytes)
75501

```

I’ve found that iterating over codeunits if you can is faster. Also `Base.parse` could be faster. It’s not hard to make it faster for parsing binary strings either. [Here is an example](https://github.com/jlapeyre/BitsX.jl/blob/main/src/parse.jl), although you can get much of the performance gain with much less complexity.

The code is here:

```julia
to_digit(char_code::Unsigned) = Int(char_code - UInt('0'))
to_digit(char::Char) = to_digit(UInt(char))

_isdigit(char_code::Unsigned) = char_code <= UInt('9') && char_code >= UInt('0')

function _parse_int(v, istart, istop)
    n = 0
    t = 1
    @inbounds for i in istop:-1:istart
        n += to_digit(v[i]) * t
        t *= 10
    end
    return n
end

function max_num(_codeunits)
    istart = 0
    istop = 0
    inword = false
    max_num = 0
    block_num = 0
    @inbounds for i in 1:length(_codeunits)
        if _isdigit(_codeunits[i])
            istop = i
            if !inword
                istart = i
                inword = true
            end
        else
            if inword
                block_num += _parse_int(_codeunits, istart, istop)
            else
                if block_num > max_num
                    max_num = block_num
                end
                block_num = 0
            end
            inword = false
        end
    end
    if block_num > max_num
        max_num = block_num
    end
    return max_num
 end

max_num(str::AbstractString) = max_num(codeunits(str))

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [December 27, 2022, 6:33pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/13 "2022-12-27T18:33:51Z")

</div>

Give atry to this

```julia
function solvelox(data)
    v = transcode(UInt8,data)
    largest = 0
    total = 0
    totpar=0
    pre=true
    for e in v
        if (e == 0x0a) && pre
            largest = max(largest, total)
            total = 0
            totpar=0
        elseif (e == 0x0a) && !pre
            total=total+totpar
            pre=true
            totpar=0
        else
            totpar = totpar*10+e-0x30
            pre=false
        end
        
    end
    return max(largest, total+totpar)
end

```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [December 27, 2022, 6:39pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/14 "2022-12-27T18:39:11Z")

</div>

> [@jlapeyre](#):
>
> Also `Base.parse` could be faster.

Use [`Parsers.parse`](https://github.com/JuliaData/Parsers.jl)

---

<div class="post-metadata">

**Author:** ![jlapeyre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlapeyre/32/4514_2.png) [@jlapeyre](https://discourse.julialang.org/u/jlapeyre)\
**Post date:** [December 27, 2022, 6:45pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/15 "2022-12-27T18:45:40Z")

</div>

`Parsers.parse` is much better than the last time I tried it.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [December 27, 2022, 7:01pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/16 "2022-12-27T19:01:59Z")

</div>

```julia
julia> data = "11\n21\n\n3\n34\n5\n\n16\n17\n10"
"11\n21\n\n3\n34\n5\n\n16\n17\n10"

julia> bigdata=(data*"\n\n")^255
"11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n" ⋯ 5877 bytes ⋯ "1\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n11\n21\n\n3\n34\n5\n\n16\n17\n10\n\n"
julia> @btime solvelox(bigdata)
  6.960 μs (0 allocations: 0 bytes)
43

julia> @btime max_num(bigdata)
  17.300 μs (0 allocations: 0 bytes)
43

```

---

<div class="post-metadata">

**Author:** ![jlapeyre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlapeyre/32/4514_2.png) [@jlapeyre](https://discourse.julialang.org/u/jlapeyre)\
**Post date:** [December 27, 2022, 7:42pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/17 "2022-12-27T19:42:36Z")

</div>

This is almost twice as fast as the solution I posted. The code I posted can be made faster by annotating functions with `@inline`, but not twice as fast.

One possible difference is that in my code, code units are read from the array twice. (Still, they are never copied so there are no allocations.)

More interesting would be to find a way to match the performance of rust with higher level tools. Or to create such tools.

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [December 27, 2022, 7:54pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/18 "2022-12-27T19:54:10Z")

</div>

> [@jlapeyre](#):
>
> More interesting would be to find a way to match the performance of rust with higher level tools

Exactly. If Rust can do it efficiently with high level iterators and functional programming in just 5 lines of code, it’s hard to believe that Julia cannot match that.

---

<div class="post-metadata">

**Author:** ![tk3369](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tk3369/32/2824_2.png) [@tk3369](https://discourse.julialang.org/u/tk3369)\
**Post date:** [December 27, 2022, 8:22pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/19 "2022-12-27T20:22:44Z")

</div>

> [@rocco\_sprmnt21](#):
>
> Give atry to this

Nice algorithm.

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [December 27, 2022, 8:54pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/20 "2022-12-27T20:54:00Z")

</div>

Maybe this feels a bit more “high-level”:

```julia
hilox(data) = 
  foldl((((largest, total, running), primed), c)->
    ifelse(c == UInt8('\n'),
      ifelse(primed,
        ((max(largest, total), 0, 0), false),
        ((largest, total + running, 0), true)),
      ((largest, total, 10*running + (c-UInt8('0'))), false)),
    Iterators.flatten((transcode(UInt8, data), UInt8('\n'), UInt8('\n'))); 
    init = ((0, 0, 0), false)) |> first |> first

```

This benchmarks as the fast loopy algorithm, and is actually a one-liner. Eat dust Rust! (just kidding, Rust is also good)

---

<div class="post-metadata">

**Author:** ![tamasgal](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamasgal/32/27946_2.png) [@tamasgal](https://discourse.julialang.org/u/tamasgal)\
**Post date:** [December 27, 2022, 9:31pm UTC](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161/21 "2022-12-27T21:31:26Z")

</div>

Haha, nice one @Dan, a little bit of Lisp-feeling along the lines 😉

Given all the nice specialised implementations above which outperform Rust are evidence that the compiler is pretty solid and it’s clear that it does some quite nice foldings. I agree with @tk3369 that Julia should be able to do this high-level crunching either, at least, to be honest I would be less surprised if Rust didn’t and Julia did 😆 that’s why I was sticking to the straight-forward implementation in my example above because I thought Rust would do something similar.

Anyways, Rust surprises me more often than thought, I should spent more time on that I guess.

[Next page](https://discourse.julialang.org/t/performance-of-splitting-string-and-parsing-numbers/92161.md?page=2)
