Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add strutils in-place #13610

Closed
wants to merge 14 commits into from
Closed

Add strutils in-place #13610

wants to merge 14 commits into from

Conversation

juancarlospaco
Copy link
Collaborator

@juancarlospaco juancarlospaco commented Mar 9, 2020

  • Add strutils in-place functions.
  • Documentation with links and code-block, runnableExamples, since, changelog.
  • Speed performance improves, worse case at least ~2x.
$ nim c -r -d:release -d:danger example.nim
strutils.toLowerAscii
30 microseconds and 592 nanoseconds     4.005999999999975e-05
toLowerAscii2
9 microseconds and 729 nanoseconds     1.216100000000012e-05

$ nim c -r example.nim
strutils.toLowerAscii
70 microseconds and 935 nanoseconds     9.858399999999878e-05
toLowerAscii2
25 microseconds and 743 nanoseconds     4.490400000000012e-05
Bench

Try yourself if you want:

import times
from strutils import toLowerAscii

template toLowerAscii2*(c: var char, linearScanEnd: static[char] = ' ') =
  c = case c
    of 'A':
      when linearScanEnd == 'a': {.linearScanEnd.}
      'a'
    of 'B':
      when linearScanEnd == 'b': {.linearScanEnd.}
      'b'
    of 'C':
      when linearScanEnd == 'c': {.linearScanEnd.}
      'c'
    of 'D':
      when linearScanEnd == 'd': {.linearScanEnd.}
      'd'
    of 'E':
      when linearScanEnd == 'e': {.linearScanEnd.}
      'e'
    of 'F':
      when linearScanEnd == 'f': {.linearScanEnd.}
      'f'
    of 'G':
      when linearScanEnd == 'g': {.linearScanEnd.}
      'g'
    of 'H':
      when linearScanEnd == 'h': {.linearScanEnd.}
      'h'
    of 'I':
      when linearScanEnd == 'i': {.linearScanEnd.}
      'i'
    of 'J':
      when linearScanEnd == 'j': {.linearScanEnd.}
      'j'
    of 'K':
      when linearScanEnd == 'k': {.linearScanEnd.}
      'k'
    of 'L':
      when linearScanEnd == 'l': {.linearScanEnd.}
      'l'
    of 'M':
      when linearScanEnd == 'm': {.linearScanEnd.}
      'm'
    of 'N':
      when linearScanEnd == 'n': {.linearScanEnd.}
      'n'
    of 'O':
      when linearScanEnd == 'o': {.linearScanEnd.}
      'o'
    of 'P':
      when linearScanEnd == 'p': {.linearScanEnd.}
      'p'
    of 'Q':
      when linearScanEnd == 'q': {.linearScanEnd.}
      'q'
    of 'R':
      when linearScanEnd == 'r': {.linearScanEnd.}
      'r'
    of 'S':
      when linearScanEnd == 's': {.linearScanEnd.}
      's'
    of 'T':
      when linearScanEnd == 't': {.linearScanEnd.}
      't'
    of 'U':
      when linearScanEnd == 'u': {.linearScanEnd.}
      'u'
    of 'V':
      when linearScanEnd == 'v': {.linearScanEnd.}
      'v'
    of 'W':
      when linearScanEnd == 'w': {.linearScanEnd.}
      'w'
    of 'X':
      when linearScanEnd == 'x': {.linearScanEnd.}
      'x'
    of 'Y':
      when linearScanEnd == 'y': {.linearScanEnd.}
      'y'
    of 'Z':
      when linearScanEnd == 'z': {.linearScanEnd.}
      'z'
    else: c

echo "strutils.toLowerAscii"
var char1: char
let t0 = now()
let t0a = cpuTime()
for _ in 0..99:
  char1 = 'A'
  discard strutils.toLowerAscii(char1)
echo now() - t0, '\t', cpuTime() - t0a

echo "toLowerAscii2"
var char0: char
let t1 = now()
let t1a = cpuTime()
for _ in 0..99:
  char0 = 'A'
  toLowerAscii2(char0, linearScanEnd = 'b')
echo now() - t1, '\t', cpuTime() - t1a
import times
from strutils import toLowerAscii

template toLowerAscii2*(c: var char, linearScanEnd: static[char] = ' ') =
  c = case c
    of 'A':
      when linearScanEnd == 'a': {.linearScanEnd.}
      'a'
    of 'B':
      when linearScanEnd == 'b': {.linearScanEnd.}
      'b'
    of 'C':
      when linearScanEnd == 'c': {.linearScanEnd.}
      'c'
    of 'D':
      when linearScanEnd == 'd': {.linearScanEnd.}
      'd'
    of 'E':
      when linearScanEnd == 'e': {.linearScanEnd.}
      'e'
    of 'F':
      when linearScanEnd == 'f': {.linearScanEnd.}
      'f'
    of 'G':
      when linearScanEnd == 'g': {.linearScanEnd.}
      'g'
    of 'H':
      when linearScanEnd == 'h': {.linearScanEnd.}
      'h'
    of 'I':
      when linearScanEnd == 'i': {.linearScanEnd.}
      'i'
    of 'J':
      when linearScanEnd == 'j': {.linearScanEnd.}
      'j'
    of 'K':
      when linearScanEnd == 'k': {.linearScanEnd.}
      'k'
    of 'L':
      when linearScanEnd == 'l': {.linearScanEnd.}
      'l'
    of 'M':
      when linearScanEnd == 'm': {.linearScanEnd.}
      'm'
    of 'N':
      when linearScanEnd == 'n': {.linearScanEnd.}
      'n'
    of 'O':
      when linearScanEnd == 'o': {.linearScanEnd.}
      'o'
    of 'P':
      when linearScanEnd == 'p': {.linearScanEnd.}
      'p'
    of 'Q':
      when linearScanEnd == 'q': {.linearScanEnd.}
      'q'
    of 'R':
      when linearScanEnd == 'r': {.linearScanEnd.}
      'r'
    of 'S':
      when linearScanEnd == 's': {.linearScanEnd.}
      's'
    of 'T':
      when linearScanEnd == 't': {.linearScanEnd.}
      't'
    of 'U':
      when linearScanEnd == 'u': {.linearScanEnd.}
      'u'
    of 'V':
      when linearScanEnd == 'v': {.linearScanEnd.}
      'v'
    of 'W':
      when linearScanEnd == 'w': {.linearScanEnd.}
      'w'
    of 'X':
      when linearScanEnd == 'x': {.linearScanEnd.}
      'x'
    of 'Y':
      when linearScanEnd == 'y': {.linearScanEnd.}
      'y'
    of 'Z':
      when linearScanEnd == 'z': {.linearScanEnd.}
      'z'
    else: c

func toLowerAscii2*(s: var string, linearScanEnd: static[char] = ' ') {.inline.} =
  var i = 0
  for c in mitems(s):
    toLowerAscii2(c, linearScanEnd)
    s[i] = c
    inc i

echo "strutils.toLowerAscii"
var str0: string
let t0 = now()
let t0a = cpuTime()
for _ in 0..99:
  str0 = "ABCDEF"
  discard strutils.toLowerAscii(str0)
echo now() - t0, '\t', cpuTime() - t0a

echo "toLowerAscii2"
var str1: string
let t1 = now()
let t1a = cpuTime()
for _ in 0..99:
  str1 = "ABCDEF"
  toLowerAscii2(str1, linearScanEnd = 'f')
echo now() - t1, '\t', cpuTime() - t1a

@Araq
Copy link
Member

Araq commented Mar 9, 2020

The code itself is ok (I only skimmed it though) but the ideas behind it need to be polished:

Now that we have dup, do we want a new strutils that is more efficient? Why does toUpperAscii have to be so efficient, better optimize cmpIgnoreCase instead. Why do I need to have the control over linearScanEnd? Most Nim programmers never heard of this feature, too specialized for Nim's stdlib, make it a Nimble package?

@Varriount
Copy link
Contributor

A better question might be, does linearScanEnd provide any performance benefit in this case?

@juancarlospaco
Copy link
Collaborator Author

@Varriount

{.linearScanEnd.} does improve a lot (in the order of microseconds), in a loop it accumulates,
is not my opinion, is just what the numbers say,
I can make it have a default argument, but is all done compile-time anyways,
but we need to rename the proc because signature are equal to the old one.

I can remove the linearScanEnd if you want no problem,
you loose performance you win nothing, but if you insist 🤷‍♀️

@juancarlospaco
Copy link
Collaborator Author

@Araq

cmpIgnoreCase and cmpIgnoreStyle literally uses toLowerAscii a lot internally. 🙂

linearScanEnd improve speed performance, maybe not Seconds, but Microseconds.

linearScanEnd is documented and usable, what is the blocker on using it?.

I can remove the linearScanEnd if you want no problem,
you loose performance you win nothing, but if you insist 🤷‍♀️

About dup, from changelog:

sugar.dup for turning in-place to work on copy of the data.

  • sugar.dup: In-Place --> Copy.
  • This strutils In-Place: Copy --> In-Place.

Literally the opposite.

Again is not my opinion, is just what the numbers say,
feel free to post here a benchmark where sugar.dup outperforms my code
by greater difference than my code Vs vanilla strutils.

I just want std lib to be faster, other parts of std lib will benefit from this eventually,
anything that uses string may benefit from an in-place strutils.
Code is simple, just a case.

You can read the code of strutils, sometimes to change 1 char copies a huge string in loop.

People confirmed that Nim is slower than Interpreted languages about this.

Confirmed root cause may be Nim strutils.

People want to use Nim string find is slower than Interpreted Python.

For a Compiled language to be faster than an Interpreted language, is a good Marketing.

@Araq
Copy link
Member

Araq commented Mar 9, 2020

I don't mind "linearScanEnd", I invented it. But I assume the documentation is only understandable by me. cmpIgnoreCase does not use toUpperAscii for strings, only for single chars. Microbenchmarks are always flawed, if people want a faster find, well, you didn't write a faster find, did you? ;-)

@bluenote10
Copy link
Contributor

Is linearScanEnd even faster here? The benchmark only uses the char A and sets the linear scan end to B, which doesn't look like a general test (if I understand linearScanEnd correctly, which I probably don't ;)).

Trying the naive in-place version

proc toLowerAscii3*(c: var char) =
  if c in {'A'..'Z'}:
    c = chr(ord(c) + (ord('a') - ord('A')))

and using the full linear scan range required gives me:

strutils.toLowerAscii
10 microseconds and 251 nanoseconds	9.879000000000018e-06
toLowerAscii2
2 microseconds and 433 nanoseconds	2.941999999999919e-06
toLowerAscii3
1 microsecond and 956 nanoseconds	2.517000000000057e-06

@juancarlospaco
Copy link
Collaborator Author

@Araq

This has toLowerAscii and toUpperAscii for char and string

I have seen the comment about hot spot in the compiler etc,
but if you dont improve toLowerAscii and toUpperAscii cant improve cmpIgnoreCase.

Documentation of linearScanEnd is understandable.

If a Developer cant understand a case switch,
linearScanEnd or not, is not going to help them. 🤷‍♀️

@alaviss
Copy link
Collaborator

alaviss commented Mar 9, 2020

I have seen the comment about hot spot in the compiler etc,
but if you dont improve toLowerAscii and toUpperAscii cant improve cmpIgnoreCase.

That's a bold claim.

Comment on lines 2865 to 2903
template toLowerAsciiInPlace*(c: var char) =
## Returns the lower case version of character ``c``.
##
## This works only for the letters ``A-Z``. See `unicode.toLower
## <unicode.html#toLower,Rune>`_ for a version that works for any Unicode
## character.
##
## See also:
## * `isLowerAscii proc<#isLowerAscii,char>`_
## * `toLowerAscii proc<#toLowerAscii,string>`_ for converting a string
runnableExamples:
var character = 'F'
toLowerAsciiInPlace(character)
doAssert character == 'f'
var chara = 'A'
toLowerAsciiInPlace(chara)
doAssert chara == 'a'
if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A')))


template toUpperAsciiInPlace*(c: var char) =
## Converts character `c` into upper case.
##
## This works only for the letters ``A-Z``. See `unicode.toUpper
## <unicode.html#toUpper,Rune>`_ for a version that works for any Unicode
## character.
##
## See also:
## * `isLowerAscii proc<#isLowerAscii,char>`_
## * `toUpperAscii proc<#toUpperAscii,string>`_ for converting a string
## * `capitalizeAscii proc<#capitalizeAscii,string>`_
runnableExamples:
var character = 'f'
toUpperAsciiInPlace(character)
doAssert character == 'F'
var chara = 'z'
toUpperAsciiInPlace(chara)
doAssert chara == 'Z'
if c in {'a'..'z'}: c = chr(ord(c) - (ord('a') - ord('A')))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But... why? Is it that slow to return a char?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this makes no sense.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try for yourself:

import times
from strutils import toLowerAscii

template toLowerAscii2(c: var char) =
  if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A')))

echo "strutils.toLowerAscii"
var char1: char
let t0 = now()
let t0a = cpuTime()
for _ in 0..99:
  char1 = 'A'
  discard strutils.toLowerAscii(char1)
echo now() - t0, '\t', cpuTime() - t0a

echo "toLowerAscii2"
var char0: char
let t1 = now()
let t1a = cpuTime()
for _ in 0..99:
  char0 = 'A'
  toLowerAscii2(char0)
echo now() - t1, '\t', cpuTime() - t1a

$ nim c -r example2.nim
strutils.toLowerAscii
55 microseconds and 883 nanoseconds     4.047999999999899e-05
toLowerAscii2
9 microseconds and 76 nanoseconds      1.158199999999883e-05

$ nim c -r -d:release -d:danger example2.nim
strutils.toLowerAscii
32 microseconds and 9 nanoseconds       4.101599999999962e-05
toLowerAscii2
8 microseconds and 804 nanoseconds     1.222200000000023e-05

🤷‍♂️

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I tweaked the test a bit:

import std/monotimes, times, strutils
proc toLowerAscii1(c: char): char {.inline.} =
  # same as stdlib but with {.inline.}
  if c in {'A'..'Z'}:
    result = chr(ord(c) + (ord('a') - ord('A')))
  else:
    result = c
proc toLowerAscii2(c: var char) {.inline.} =
  if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A')))

template bench(body: untyped): untyped =
  block:
    let start = getMonoTime()
    var ch {.inject.}: char
    for _ in 0..99:
      ch = 'A'
      body
    echo getMonoTime() - start

echo "strutils.toLowerAscii"
bench: ch = toLowerAscii(ch)

echo "toLowerAscii1"
bench: ch = toLowerAscii1(ch)

echo "toLowerAscii2"
bench: toLowerAscii2(ch)

And the results:

$ nim c -r test.nim
strutils.toLowerAscii
2 microseconds and 170 nanoseconds
toLowerAscii1
1 microsecond and 800 nanoseconds
toLowerAscii2
1 microsecond and 731 nanoseconds

$ nim c -d:danger -r test.nim
strutils.toLowerAscii
330 nanoseconds
toLowerAscii1
20 nanoseconds
toLowerAscii2
20 nanoseconds

It seems to me that the only overhead here is the function call overhead, you can easily fix this by adding {.inline.} into the stdlib version.

Copy link
Collaborator Author

@juancarlospaco juancarlospaco Mar 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats 330 Vs 20, that confirms is faster. 🤷‍♂️

I think that the point is that difference accumulates in the loop,
I dont care too much about the char functions,
but the string function the difference is bigger.

I got more difference (Microseconds Vs Nanoseconds, but I believe you).

What do we do?, add {.inline.} to all the strutils ones?, close this PR?. 🤔

Copy link
Collaborator

@alaviss alaviss Mar 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats 330 Vs 20, that confirms is faster.

Well of course there will be a difference when you {.inline.} vs calling functions. The point here is var char doesn't really improve anything, so we don't need to add new APIs for that, just adding {.inline.} strategically is enough.

I dont care too much about the char functions

You may not care about it, but it does matter when a new API is added to the stdlib. We should be conservative about adding new APIs, since once added we will be stuck with it for the rest of the 1.x series.

With that said, I don't have any objections on adding the in-place versions for the string variant, as there are actual speed up there.

add {.inline.} to all the strutils ones?

We should only target simple procs (ie. <= 3 lines). Better would be to make the compiler able to perform this optimization on it's own, though that's a lot harder.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
Only string proc is public.

Comment on lines 2924 to 2925
toLowerAsciiInPlace(c)
s[i] = c
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in-place already, so why assigning it again?

@juancarlospaco
Copy link
Collaborator Author

Interestingly C tolower() is slower than Nim. If someone want to try:

import std/monotimes, times

proc toLowerAsciiC(c: char): char {.importc: "tolower", header: "ctype.h".}

block:
  echo "toLowerAsciiC"
  var ch: char
  let start = getMonoTime()
  for _ in 0..99:
    ch = 'A'
    ch = toLowerAsciiC(ch)
  echo getMonoTime() - start

Comment on lines 2865 to 2869
template toLowerAsciiInPlace(c: var char) =
if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A')))

template toUpperAsciiInPlace(c: var char) =
if c in {'a'..'z'}: c = chr(ord(c) - (ord('a') - ord('A')))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add {.inline.} to the current toLowerAscii and toUpperAscii instead of duplicating the code here. It's demonstrated that the performance gains of this duplication is negligible compared to when you add {.inline.} to the current versions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 2890 to 2892
toLowerAsciiInPlace(c)
s[i] = c
inc i
Copy link
Collaborator

@alaviss alaviss Mar 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
toLowerAsciiInPlace(c)
s[i] = c
inc i
c = toLowerAscii(c)

You're using mitems, which allows you to directly modify the elements of the passed string.

Comment on lines 2913 to 2915
toUpperAsciiInPlace(c)
s[i] = c
inc i
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
toUpperAsciiInPlace(c)
s[i] = c
inc i
c = toUpperAscii(c)

var stringx = "-bar"
capitalizeAsciiInPlace(stringx)
doAssert stringx == "-bar"
toUpperAsciiInPlace(s[0])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
toUpperAsciiInPlace(s[0])
s[0] = toUpperAscii(s[0])

toLowerAsciiInPlace(strng)
doAssert strng == "nim"
for c in mitems(s):
if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A')))
Copy link
Collaborator

@alaviss alaviss Mar 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add {.inline.} to to{Lower,Upper}Ascii(char) then use them directly instead? Please don't reply "Done" to my suggestions then do it your way.

Suggested change
if c in {'A'..'Z'}: c = chr(ord(c) + (ord('a') - ord('A')))
c = toLowerAscii(c)

If you disagree with my suggestions, please voice up instead of keep doing it your way.

@alaviss
Copy link
Collaborator

alaviss commented Mar 10, 2020

@Araq

Now that we have dup, do we want a new strutils that is more efficient?

I assume what you're referring to is an new strutils that's completely in-place, then users may use dup whenever they want a duplicate?

@Araq
Copy link
Member

Araq commented Mar 10, 2020

I assume what you're referring to is an new strutils that's completely in-place, then users may use dup whenever they want a duplicate?

Yes.

@timotheecour
Copy link
Member

timotheecour commented Mar 10, 2020

@Araq
Copy link
Member

Araq commented Mar 10, 2020

should toUpperAsciiInPlace + friends operate on openArray instead of string ? or should that be reserved for the new improved strutils designed from ground up (eg using all inplace algos and all openArray where it makes sense) ?

I only use strutils because I have nothing better. I thought about introducing strbasics that has the stuff from strutils that I actually use, strutils could import strbasics and can be kept backwards compatible. strbasics should indeed offer optimized solutions. If that means var string and openArray[char] so be it.

@juancarlospaco
Copy link
Collaborator Author

juancarlospaco commented Mar 10, 2020

@timotheecour If you want to PR do it, I close this no problem,
we are all for the benefit of the community.
🙂

@Araq I think of using a separate file and doing since (1, 1): include strutils_inplace,
you prefer a new module ?.

@timotheecour
Copy link
Member

@timotheecour If you want to PR do it, I close this no problem,

I have a lot of PR's open already, and you're almost done (after addressing review comments) so just keep working on your PR instead

@Araq I think of using a separate file and doing since (1, 1): include strutils_inplace,

include should be almost always be avoided (and always avoided when import works); IMO that new module should be done in a separate PR

var x = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ*+,-./:;<=>?@[]_{|}~"
toLowerAsciiInPlace(x)
doAssert x == "abcdefghijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrstuvwxyz*+,-./:;<=>?@[]_{|}~"
const t = "abcdefghijklmnopqrstuvwxyz".indent(65).cstring
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silly, pointless lookup table. Instead use c = chr(ord(c) - ord('A') + ord('a')). Basic math, it's a thing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is slower on my bench, but if you say so 🤷‍♂️

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Microbenchmarks are misleading, in reality you can also have I-cache problems and then you appreciate not everything was "optimized" by 8 times loop unrolling with SSE instructions and 1KB lookup tables.

var z = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ*+,-./:;<=>?@[]_{|}~"
toUpperAsciiInPlace(z)
doAssert z == "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ*+,-./:;<=>?@[]_{|}~"
const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".indent(97).cstring
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

@Araq
Copy link
Member

Araq commented Mar 11, 2020

Sorry, I have to reject this before even more time and effort is spent on it.

  1. Not covered by an RFC.
  2. People want a faster strutils, but not a faster toLower as cmpIgnoreCase exists. They ask for find, replace and faster IO operations.
  3. More bloat to strutils, if I need an inplace toLower it's one for loop plus the char version of toLowerAscii. (Which should have been declared inline, but wasn't, but this PR doesn't touch it either.)

@Araq Araq closed this Mar 11, 2020
@juancarlospaco juancarlospaco deleted the strutils-inplace branch March 11, 2020 17:53
@juancarlospaco
Copy link
Collaborator Author

I pressed the Close button too, and it failed, now I know why :P
Was literally the same code anyways.

@Araq
I wonder if it is possible to create a Macro that forces {.inline.} on whatever proc you feed it:

import forceInline
forceInline( someProc )  # Adds {.inline.} to someProc

🙂

@Araq
Copy link
Member

Araq commented Mar 12, 2020

No, that's not possible. We would need to delay code generation until the full program was processed, that means it's anti-modular.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants