2

I understand what .blank? , .nil? and .empty? does now.

I'm thinking why should I not replace all the .nil? and .empty? to .blank? for less risks of making mistakes. For example, if current_user.blank?

Is there a performance issue for .blank? method? Is it slower or consuming more memory?

If it is, how bad is it? As bad as string vs symbol?

Thanks in advance.

Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69
Ivan Wang
  • 8,306
  • 14
  • 44
  • 56

2 Answers2

6

Theory

All three methods have various realizations for various classes:

  1. :nil?:

    # NilClass
    def nil?
      true
    end
    
    # Object
    def nil?
      false
    end
    
  2. :empty?:

    # String, Array
    def empty?
      if self.size == 0
        true
      else
        false
      end
    end
    
  3. :blank?:

    # FalseClass, NilClass
    def blank?
      true
    end
    
    # Object
    def blank?
      respond_to?(:empty?) ? empty? : !self
    end
    
    # TrueClass
    def blank?
      false
    end
    
    # String
    def blank?
      self !~ /[^[:space:]]/
    end
    

As you may see the various classes implement various methods style. In case of String class it takes time of a single Regexp, in case of Object, including Hash, and Array it takes time of call to :respond and return a value nil or not Object. The seconds are just operations that takes time similar to :nil?. :respond? method checks the presense of the :empty? method that takes theoretically slight more times than two times to :empty?.

Investigations

I wrote simple script that simulates the behaviour of those methods, and calculates execution time of them:

#! /usr/bin/env ruby

require 'benchmark'

obj = Object.new
array = []
empty_string = ''
non_empty_string = '   '

funcs = 
[ proc { empty_string.empty? }, 
  proc { non_empty_string.empty? },
  proc { obj.nil? },
  proc { nil.nil? },
  proc { true },
  proc { respond_to?(:empty?) ? empty? : !self },
  proc { array.respond_to?(:empty?) ? array.empty? : !array },
  proc { non_empty_string !~ /[^[:space:]]/ } ]

def ctime func
   time = 0
   1000.times { time += Benchmark.measure { 1000.times { func.call } }.to_a[5].to_f }
   rtime = time /= 1000000
end

funcs.each {| func | p ctime( func ) }

And results:

# empty String :empty?
# 4.604020118713379e-07
# non_empty String :empty?
# 4.5903921127319333e-07
# Object :nil?
# 5.041143894195557e-07
# NilClass :nil?
# 4.7951340675354e-07
# FalseClass, NilClass, TrueClass :blank?
# 4.09862756729126e-07
# main :blank? ( respond_to returns false )
# 6.444177627563477e-07
# Array :blank? ( respond_to returns true )
# 6.491720676422119e-07
# String :blank?
# 1.4315705299377442e-06

As you may see, obvious champion from the end of table in case of speed is method :blank? of String class. It has execution time descreased in 3-4 times against an simple empty? method of a class. Object's :blank? method has only 1.5 times execution time degradation. Note, that :respond_to method in it has just a few time to execute, becase as far as I see the ruby interpreter caches the result of its execution. So, conclusion. Try to avoid using String's .blank? method.

Additional, if you need to know how to use the methods see here.

Community
  • 1
  • 1
Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69
  • This is a great full detail report of everything! Thank you so much. Learnt much more than I have expected. Right, `.blank?` is doing too perfect on checking white space string. But not so perfect on checking array. Really interesting. – Ivan Wang Dec 29 '13 at 18:08
  • @IvanWang Vise versa, `:blank?` on `String` takes more time then the same on `Array` or `Hash`. so on `String` it becomes worse more than twice. – Малъ Скрылевъ Dec 29 '13 at 18:13
  • I have confused you again. I meant `.blank?` is doing literally perfect on string, because it checks every white space in string. You are right, doing too perfect surely will make it slow. However, `.blank?` decided to not do white space checking on array. That makes it seems "half-way-done". (I think that is for a good reason. If it does white space check on every instant inside an array, it will take ages if the array is full of white space instances.) – Ivan Wang Dec 29 '13 at 18:28
  • Yes, functionally `:blank?` for `String` is better, then `Array`'s one. May be it because of that `Array` `[nil]`, or `Hash` `{ nil => nil }` aren't frequently occurred in Rails =) – Малъ Скрылевъ Dec 29 '13 at 18:32
3

check the source here blank?

def blank?
  respond_to?(:empty?) ? empty? : !self
end

Technically its a 2x of empty?

is there a performance issue for .blank? ? Is it slower or consuming more memory?

Well, that depends on lot of factors

  • amount of data your app handles
  • server hardware
  • no of incoming requests

But frankly this is not gonna bother you unless you run twitter

Siva
  • 7,780
  • 6
  • 47
  • 54
  • Nice. The source codes help me understand much better. 2X .empty? sounds reasonable. Now I can just feel free to use more .blank on small scale projects. – Ivan Wang Dec 28 '13 at 12:31